The basic relationships between the various GATT primitives look like this:
In order to have the SoftDevice work properly, each service and descriptor will have to be added in the order it appears in the database. For the first simple example we are going to build the DeviceInformation service specified by Bluetooth (documentation can be found here). All characteristics are optional so we are going to implement four characteristics: The Manufacturer name string, the model number string and the Firmware/Software revision strings. We do this because iOS expect these to be defined if DeviceInfo service is supported
By looking at the 16-bit UUIDs document (found here) we can find the following UUIDs:
- Device Information Service has the uuid 0x180A,
- Manufacturer name has the uuid 0x2A29
- Model number has the uuid 0x2A24
- Firmware revision has the uuid 0x2A26
- Software revision has the uuid 0x2A28
To define a new service we only need to define the UUID for the service
Each characteristic needs two set of metadata to be defined, one for the characteristic itself, and one for configuring the attribute value. Since all the characteristics here are of the same type (public read-only with fixed length) we only need to make two sets of metadata.
The one for the characteristic looks like this
Here we only set the read property to 1 as all other properties need to be 0 (which is default for partial initialization). This will make the characteristic read-only. The other properties are commented out but can be used as template for future configurations. It should be noted that the soft device groups a number of Characteristic concepts in the definition of this metadata. They map in the following way to the bluetooth specification volume 3 part G:
- The char_props (Characteristic properties) are documented in section 3.3.1.1
- The char_ext_props (Extended Characteristic properties are documented in section 3.3.3.1
- The p_char_user_desc can point to a string containing a human readable description of the characteristic. If not set to NULL, char_user_desc_max_size and char_user_desc_size must be set as well. This is documented in 3.3.3.2
- p_char_pf can be set to point to a format specifier as described in 3.3.3.5. If set, the format specifier has of be of the type described here
- The *_md fields are used if metadata for user description, CCCD (section 3.3.3.3) or SCCD (section 3.3.3.4) should be different than the metadata for the attribute value. This would usually not be the case
Then we have the attribute value metadata:
We set the read permission to security mode 1 level 1, which means public readable without encryption. The security levels are explained here: Security Levels
You can set separate permissions for read and write, however in most cases it makes most sense to set them to the same. Since the characteristics are read-only we only set access for the read part.
We store the value in user space as indicated by setting .vloc to BLE_GATTS_VLOC_USER. This especially makes sense for read-only values as it allows us to store the value directly in flash memory.
If the value had variable length, we would set .vlen to 1. If we would have to get permission from the application to read or write we would set .rd_auth or .wr_auth to 1 respectively.
Now the actual attribute can be set up like this (manufacturer name as example):
We declare the UUID as being a standard Bluetooth UUID, and then we set up the attribute with its UUID, attribute metadata, the initial length, max length and offset of the value pointed to by p_value.
If this had been a variable length attribute, these two could have been different and the length could have been stored in e.g. the first byte in the value so that the offset had been 1 instead.
Once we have declared the data for all the characteristics, we register them with the SoftDevice like this - first installing the service, and then associate the characteristics with the service via its handle:
In this case we are going to throw the handles for the characteristics away as we don't need to deal further with them after they have been created. This will change in the next installment where we will look at dynamic characteristics that interact with the physical world.
For now the last thing we need to do is to install the service. We do that in ble_start() right before we start advertising:
