Skip to content

Commit 1e8b3c2

Browse files
authored
Enables the setting of the Generic Access characteristics (#68)
1 parent b1c6436 commit 1e8b3c2

File tree

6 files changed

+144
-39
lines changed

6 files changed

+144
-39
lines changed

README.md

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ This implementation supports a cut down version of the Gatt Server and Gatt Clie
5656

5757
The device can either run as a Server or Client, but not at the same time.
5858
For example if you start a Watcher to look for advertisements from Server devices you will not
59-
be able to connect to those devices until the Watcher has been stopped. But you can recieve data from connected
59+
be able to connect to those devices until the Watcher has been stopped. But you can receive data from connected
6060
devices while Watcher is scanning.
6161

6262
For more information see relevant sections: -
@@ -86,7 +86,7 @@ Guid uuid1 = Utility.CreateUuidFromShortCode(0x2A19);
8686

8787
### Security and Pairing
8888

89-
The assembly supports pairing with encryption and authenication.
89+
The assembly supports pairing with encryption and authentication.
9090

9191
If you don't do anything in your code it will use the **Just works** method of pairing which will enable encryption on the connection.
9292

@@ -102,6 +102,20 @@ Calling dispose will remove the object from memory.
102102

103103
This object is new and doesn't exist in the normal Windows implementation and was added to allow better handling of pairing and connections from the managed code.
104104

105+
### Setting the Device Name and it Appearance
106+
107+
The device name and appearance is part of the Generic Access service which is automatically
108+
included in each Gatt Server definition. The name should be the name of current device and the appearance which
109+
is optional is a 16 bit code that represents the use of the device. Defaults to 0 which is "Unknown Device".
110+
For details on codes see the Bluetooth Sig Assigned numbers document; section **2.6.3 Appearance Sub­category values**. Use the values column for the code.
111+
For example code 0x0481 is for a cycling computer.
112+
113+
```csharp
114+
BluetoothLEServer server = BluetoothLEServer.Instance;
115+
server.DeviceName = "Esp32_01";
116+
server.Appearance = 0x0481;
117+
```
118+
105119
### Defining the service and associated Characteristics
106120

107121
The GattServiceProvider is used to create and advertise the primary service definitions. An extra device information service will be automatically created when first service is created.
@@ -116,7 +130,7 @@ if (result.Error != BluetoothError.Success)
116130
serviceProvider = result.ServiceProvider;
117131
```
118132

119-
To create further services for the Gatt server call **GattServiceProvider.Create(uuid)** for each new service.
133+
To create further services for the Gatt server call **GattServiceProvider.Create(UUID)** for each new service.
120134

121135
Access all created services via the BluetoothLEServer instance.
122136

@@ -173,7 +187,7 @@ GattLocalCharacteristicParameters ReadParameters = new GattLocalCharacteristicPa
173187
};
174188

175189
```
176-
If the **StaticValue** is set the the read event will not be called and doesn't need to be defined.
190+
If the **StaticValue** is set the read event will not be called and doesn't need to be defined.
177191

178192
### Adding a Write or WriteWithoutResponse Characteristic
179193

@@ -240,7 +254,7 @@ private static void UpdateNotifyValue(double newValue)
240254
### Read requested event
241255

242256
When a client requests to read a characteristic, the managed event will be called assuming a static value hasn't been set.
243-
If no event handler is set or you don't respond in a timely manner an Unlikely bluetooth error will be returned to client.
257+
If no event handler is set or you don't respond in a timely manner an Unlikely Bluetooth error will be returned to client.
244258
If reading the value from a peripheral device takes time then best to put this outside the event handler.
245259

246260
This show the returning of 2 values to client request.
@@ -265,7 +279,7 @@ private static void _readCharacteristic_ReadRequested(GattLocalCharacteristic se
265279
## Write requested event
266280

267281
When data is sent to a write characteristic the managed event is called. If no event handler is
268-
set or you don't respond in a timely manner an Unlikely bluetooth error will be returned to client.
282+
set or you don't respond in a timely manner an Unlikely Bluetooth error will be returned to client.
269283

270284
The data received is a array of bytes and this is formatted as required by characteristic. This could be a single
271285
value of Int16, Int32, string etc. or it could be a number of different values.
@@ -339,7 +353,7 @@ We have 2 samples available:
339353
- Central2 - This more of a full on sample and is an example on how to collect values in this case temperatures
340354
from a bunch of devices with the Environmental Sensor service. The devices are an updated version of the Sample device example.
341355

342-
## Watching for Advertisments
356+
## Watching for Advertisements
343357

344358
To watch for advertisements you use the BluetoothLEAdvertisementWatcher class.
345359

@@ -366,25 +380,29 @@ RSSI filter
366380

367381
## Creating a device and connecting to device.
368382

369-
To communicate with a device a BluetoothLEDevice class needs to be created using the devices bluetooth address and type.
370-
This can be the bluetooth address from the BluetoothLEAdvertisementWatcher event or using a hard coded address.
383+
To communicate with a device a BluetoothLEDevice class needs to be created using the devices Bluetooth address and type.
384+
This can be the Bluetooth address from the BluetoothLEAdvertisementWatcher event or using a hard coded address.
371385

372386
In this case from the Watcher advertisement received event BluetoothAddress argument.
373387
```csharp
374388
BluetoothLEDevice device = BluetoothLEDevice.FromBluetoothAddress(args.BluetoothAddress)
375389
```
376-
There are no specific connection methods but a connection will be made automatically when the device is queried for services or a Pairing operation is started.
390+
There are no specific connection methods, a connection will be made automatically when the device is queried for services or a Pairing operation is started.
377391
The ConnectionStatusChanged event can be used to detect a change in connection status and an attempt to
378392
reconnect can be done by a query to the devices services again. Avoid doing this in the event, as it can block other
379393
events being fired during the connection.
380394

381-
After connecting to teh device you can go back to Watching for advertisements with the restriction that you can't connect to newly found devcies until the Watching is stopped.
395+
After connecting to the device go back to Watching for advertisements with the restriction that you can't connect to newly found devices until the Watching is stopped.
382396
You can still communication with connected devices while the Watcher is running. Best way is to collect all found devices in a table until Watcher is
383397
stopped then connect to all found devices. See [Central 2 sample](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central2)
384398
385399
The Close() method is not exposed in the desktop version but it has been implemented in this version
386400
to give better control over the connection.
387401

402+
## Getting information about the connected device
403+
404+
The Generic Access device name and appearance code are available as properties of the BluetoothLEDevice object after connecting to the device.
405+
388406
## Querying device services
389407

390408
Querying for all services provided by device. If the GattDeviceServicesResult status is a GattCommunicationStatus.Success then an
@@ -412,7 +430,7 @@ The services are cached so the first time they are queried it will retrieve them
412430
Further calls for services will return the cached results. To clear the cache the device must be disposed.
413431

414432

415-
## Querying service characterisics
433+
## Querying service characteristics
416434

417435
With the GattDeviceService object a query can be made for the required Characteristics.
418436
In the same way as the service there is a method to query for a all Characteristics or just specific Characteristics.
@@ -438,7 +456,7 @@ Note:
438456
The Characteristics are cached so the first time requested it will retrieve them from the device.
439457
Further calls to same service will return the cached results.
440458

441-
## Querying characterisics descriptors
459+
## Querying characteristics descriptors
442460

443461
Descriptors can be retrieved in the same way as Services and Characteristics using the methods
444462
GetDescriptors and GetDescriptorsForUuid.
@@ -497,7 +515,7 @@ This can be used to Write with or without a response.
497515
}
498516
```
499517
500-
## Enabling the value changed notifcations
518+
## Enabling the value changed notifications
501519

502520
This enables the receiving of events when a Characteristic value changes on the server. The notifications are enabled
503521
by setting up an event then setting the value of the CCCD descriptor as below.
@@ -546,7 +564,7 @@ For good information on Bluetooth Pairing and how the IO Capabilities effect the
546564

547565
By default the *ProtectionLevel* is **None** and the *IOCapabilities* is **NoInputNoOutput** so the pairing will use **Just Works** method if both ends are nanoFramework.
548566

549-
The ProtectionLevel will be automaticlly updated depending on the requirements of any added Characteristics.
567+
The ProtectionLevel will be automatically updated depending on the requirements of any added Characteristics.
550568
This can be manually set to force a different level of protection.
551569

552570
| *ProtectionLevel* | |
@@ -557,8 +575,8 @@ This can be manually set to force a different level of protection.
557575

558576
## IOCapabilities
559577

560-
The IO Capabilities are input and output peripherals availble on the device for pairing.
561-
Depending on what IOCapabilities each device has will covern how the pairing will be done.
578+
The IO Capabilities are input and output peripherals available on the device for pairing.
579+
Depending on what IOCapabilities each device has will govern how the pairing will be done.
562580

563581
| *IOCapabilities* | |
564582
| ------- | ----- |
@@ -569,11 +587,11 @@ Depending on what IOCapabilities each device has will covern how the pairing wil
569587

570588
## Pairing Matrix
571589

572-
The Initiator is the devcie thats starts the pairing operation. This would normally be the Client.
573-
The Responder is the devcie responding to the pairing request.
590+
The Initiator is the device thats starts the pairing operation. This would normally be the Client.
591+
The Responder is the device responding to the pairing request.
574592

575-
*Just Works* : Means there is no inpt required and connection is just set up. If any Characteristics
576-
have a protection level of Authentiction then a access error will be given.
593+
*Just Works* : Means there is no input required and connection is just set up. If any Characteristics
594+
have a protection level of Authentication then a access error will be given.
577595

578596
| **Responder IOCapabilities** || <- | <- | *Initiator* *IOCapabilities* | -> | -> |
579597
| --------- | ----------- | ------------ | ------------ | --------------- | --------------- |
@@ -632,7 +650,7 @@ The client has to provide the correct passKey.
632650
The PairingKind indicates what the application needs to do.
633651
In this case its DevicePairingKinds.DisplayPin which just needs the passKey to compare with client.
634652

635-
For a general use device with a display the passkey should be displayd so client knows what to input.
653+
For a general use device with a display the passkey should be displayed so client knows what to input.
636654

637655
```csharp
638656
private static void Pairing_PairingRequested(object sender, DevicePairingRequestedEventArgs args)
@@ -665,7 +683,7 @@ private static void Pairing_PairingComplete(object sender, DevicePairingEventArg
665683

666684
#### Client
667685

668-
##### Setup of BluetoothLEDevice for pairing with Authenication.
686+
##### Setup of BluetoothLEDevice for pairing with Authentication.
669687

670688
```csharp
671689
static void SetupDevice(BluetoothLEDevice device)
@@ -801,7 +819,7 @@ The **nanoFramework** Class Libraries are licensed under the [MIT license](LICEN
801819

802820
## Code of Conduct
803821

804-
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behaviour in our community.
822+
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
805823
For more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
806824
807825
## .NET Foundation

nanoFramework.Device.Bluetooth/BluetoothLEDevice.cs

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ public class BluetoothLEDevice : IDisposable
2020
private BluetoothConnectionStatus _connectionStatus;
2121
private readonly ulong _bluetoothAddress;
2222
private readonly BluetoothAddressType _addressType;
23-
24-
private readonly string _name;
23+
private string _name;
24+
private ushort _appearance;
2525
private ushort _connectionHandle;
26+
private bool _genericAccessRead;
2627

2728
private ArrayList _services;
2829
private Hashtable _attributes;
@@ -49,6 +50,9 @@ internal BluetoothLEDevice(ulong bluetoothAddress, BluetoothAddressType addressT
4950
_addressType = addressType;
5051

5152
_name = "";
53+
_appearance = 0; // Unknown
54+
_genericAccessRead = false;
55+
5256
_connectionStatus = BluetoothConnectionStatus.Disconnected;
5357

5458
_services = new();
@@ -146,7 +150,7 @@ public void Close()
146150
public GattDeviceServicesResult GetGattServices()
147151
{
148152
GattCommunicationStatus status = ConnectDeviceIfNotConnected();
149-
153+
150154
// we already have services then we must be trying to re-connect
151155
// Just return current services
152156
if (status == GattCommunicationStatus.Success &&
@@ -286,19 +290,80 @@ internal set
286290
/// <summary>
287291
/// Gets the name of the Bluetooth LE device.
288292
/// </summary>
289-
public string Name { get => _name; }
293+
public string Name
294+
{
295+
get
296+
{
297+
LoadGenericAccessValues();
298+
return _name;
299+
}
300+
}
301+
302+
/// <summary>
303+
/// Gets the appearance of the Bluetooth LE device.
304+
/// </summary>
305+
public ushort Appearance
306+
{
307+
get
308+
{
309+
LoadGenericAccessValues();
310+
return _appearance;
311+
}
312+
}
313+
314+
/// <summary>
315+
/// Access the Generic Access service and retrieve Device name and Appearance to set properties.
316+
/// </summary>
317+
private void LoadGenericAccessValues()
318+
{
319+
if (!_genericAccessRead && ConnectDeviceIfNotConnected() == GattCommunicationStatus.Success)
320+
{
321+
// Get Generic Access Service
322+
GattDeviceServicesResult sr = GetGattServicesForUuid(GattServiceUuids.GenericAccess);
323+
if (sr.Status == GattCommunicationStatus.Success)
324+
{
325+
GattDeviceService srv = sr.Services[0];
326+
GattCharacteristicsResult cr = srv.GetCharacteristicsForUuid(GattCharacteristicUuids.GapDeviceName);
327+
if (cr.Status == GattCommunicationStatus.Success)
328+
{
329+
// Get device name value
330+
GattReadResult grn = ReadAttributeValue(cr.Characteristics[0].AttributeHandle);
331+
if (grn.Status == 0)
332+
{
333+
DataReader rdr = DataReader.FromBuffer(grn.Value);
334+
_name = rdr.ReadString(rdr.UnconsumedBufferLength);
335+
}
336+
}
337+
338+
cr = srv.GetCharacteristicsForUuid(GattCharacteristicUuids.GapAppearance);
339+
if (cr.Status == GattCommunicationStatus.Success)
340+
{
341+
// Get device appearance value
342+
GattReadResult gra = ReadAttributeValue(cr.Characteristics[0].AttributeHandle);
343+
if (gra.Status == 0)
344+
{
345+
DataReader rdr = DataReader.FromBuffer(gra.Value);
346+
_appearance = rdr.ReadUInt16();
347+
}
348+
}
349+
}
350+
351+
// Set flag so we don't try to re-read generic access characteristics.
352+
// even if we failed this time. The service or characteristics may be missing from device.
353+
_genericAccessRead = true;
354+
}
355+
}
290356

291357
/// <summary>
292358
/// Gets the address type for the Bluetooth LE device.
293359
/// </summary>
294360
public BluetoothAddressType BluetoothAddressType { get => _addressType; }
295361

296-
// TODO pairing
297-
///// <summary>
298-
///// Gets a boolean indicating whether the BluetoothLEDevice was paired using a Secure
299-
///// Connection.
300-
///// </summary>
301-
////public bool WasSecureConnectionUsedForPairing { get; }
362+
/// <summary>
363+
/// Gets a boolean indicating whether the BluetoothLEDevice was paired using a Secure
364+
/// Connection.
365+
/// </summary>
366+
public bool WasSecureConnectionUsedForPairing { get => _pairing.WasSecureConnectionUsedForPairing; }
302367

303368
/// <summary>
304369
/// Occurs when the connection status for the device has changed.
@@ -425,7 +490,7 @@ internal GattWriteResult WriteAttributeValueWithResult(ushort attributeHandle, B
425490
result = (readWriteValueResult)_eventStatus;
426491
switch (result)
427492
{
428-
case readWriteValueResult.success:
493+
case readWriteValueResult.acessDenied:
429494
if (!securityStarted)
430495
{
431496
securityStarted = true;

nanoFramework.Device.Bluetooth/BluetoothLEServer.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,19 @@ private void Init()
7373

7474
/// <summary>
7575
/// Server device name, defaults to 'nanoFramework'.
76+
/// Set in the Generic Access service.
7677
/// </summary>
7778
public String DeviceName { get => BluetoothNanoDevice.DeviceName; set => BluetoothNanoDevice.DeviceName = value; }
7879

80+
/// <summary>
81+
/// Appearance value of the device set in the Generic Access service and also the advertisment if enabled.
82+
/// The appearance is a 16 bit value comprising of bits 6 to 15 the device category and bits 0 to 5 the sub-categoty.
83+
/// See Bluetooth assigned numbers document section "Appearance Sub­category values" for values.
84+
/// For example a "IOT Gateway" has a value of 0x008D.
85+
/// The Appearance value defaults to 0, a Generic Unknown device.
86+
/// </summary>
87+
public ushort Appearance { get => BluetoothNanoDevice.Appearance; set => BluetoothNanoDevice.Appearance = value; }
88+
7989
/// <summary>
8090
/// Get GattSession aasoicated with this server.
8191
/// </summary>

nanoFramework.Device.Bluetooth/BluetoothNanoDevice.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ internal enum Mode { NotRunning, Server, Client };
2121
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
2222
private static string _deviceName = "nanoFramework";
2323

24+
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
25+
private static ushort _appearance = 0;
26+
2427
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
2528
private static Mode _mode = Mode.NotRunning;
2629

@@ -31,6 +34,8 @@ static BluetoothNanoDevice()
3134

3235
internal static string DeviceName { get => _deviceName; set => _deviceName = value; }
3336

37+
internal static ushort Appearance { get => _appearance; set => _appearance = value; }
38+
3439
/// <summary>
3540
/// Checks if current mode enabled otherwise switch to mode.
3641
/// </summary>
@@ -55,7 +60,7 @@ internal static Mode RunMode
5560
set
5661
{
5762
_mode = value;
58-
NativeSetOperationMode(_mode, DeviceName);
63+
NativeSetOperationMode(_mode, DeviceName, Appearance);
5964
}
6065
}
6166

@@ -64,7 +69,7 @@ internal static Mode RunMode
6469
private static extern void NativeInitilise();
6570

6671
[MethodImpl(MethodImplOptions.InternalCall)]
67-
private static extern void NativeSetOperationMode(Mode mode, string deviceName);
72+
private static extern void NativeSetOperationMode(Mode mode, string deviceName, ushort appearance);
6873
#endregion
6974
}
7075
}

0 commit comments

Comments
 (0)