BLE Hands on Working with Characteristics - joe-possum/IoT-Developer-Boot-Camp GitHub Wiki
In this worksheet we will introduce the Bluetooth GATT Configurator and implement a Health Thermometer Service and Characteristics starting from Bluetooth - SoC Empty.
Create a new Bluetooth- SoC Empty project, but at the Project Configuration dialog, change the name of the project to "secure_thermometer" (We will implement the secure feature in the next worksheet).
In the new project, locate the file config/btconf/gatt-configuration.btconf
. Double-click on the file. This will open the Bluetooth GATT Configurator.
Currently the project contains a very basic collection of Services and associated characteristics. Click on the "Device Name" characteristic in the "Generic Access" service (1). The ID field (2) provides a #define
to access this characteristic by its handle.
The USER HEX UTF-8 selector (3) will modify how the stack will process read and write access requests from clients. If a characteristic is HEX or UTF-8, the stack will automatically handle requests, it will respond to reads with the current value in the characteristic with no events being generated. When a write occurs, the stack will replace the current value with the requested value and the application will receive an event indicating the value written. If a characteristic is USER, then the stack passes these requests to the application which must process them.
Change the value of the Device Name characteristic and adjust the length to match. We are going to add the Health Thermometer Service to the project. The GATT Configurator contains a comprehensive set of Bluetooth SIG defined profiles, services and characteristics which can easily be added to a project. Click on the Add icon (4) this changes the view in the right-hand pane. Expand the Health Thermometer profile. It requires two services, Health Thermometer and Device Information which is already present. Notice that when the mouse hovers over a profile of service, a plus icon appears to the right. Click on the plus icon to the right of the Health Thermometer service (5).
Switching back to edit mode, expand the newly added Health Thermometer service. There are a number of characteristics and descriptors which indicate they require configuration. For simplicity we only need include the Temperature Measurement and Temperature Type characteristics. We can delete Intermediate Temperature and Measurement Interval using the Delete icon. Since we are not configuring the Temperature Measurement's Client Characteristic Configuration descriptor, we can also delete it also.
Selecting the Temperature Measurement characteristic, enable the indicate property and set the length to 5. There are a number of optional fields we will implement the simplest.
Selecting the Temperature Type characteristic, enable the read property and set the initial value to 02.
Save the GATT Configuration with [Ctrl]-[S]. It will be automatically saved during the build process, but may not complete before the old values are used.
Since the Temperature Measurement characteristic has the indicate property, a client will request indications. When this happens, the application will receive a gatt-server-characteristic-status event. If the client is requesting a change in indication status, the status_flags
field will have the value 1 (sl_bt_gatt_server_client_config
) and the client_config_flags
field will contain the requested status (0: no indication requested or 2: indication requested.) This can be coded as where temperature_measurement_config
is a global variable storing the current status.
case sl_bt_evt_gatt_server_characteristic_status_id:
if(gattdb_temperature_measurement == evt->data.evt_gatt_server_characteristic_status.characteristic) {
if(1 == evt->data.evt_gatt_server_characteristic_status.status_flags) {
temperature_measurement_config = evt->data.evt_gatt_server_characteristic_status.client_config_flags;
}
}
break;
The data used in the Temperature Measurement characteristic can be represented by the following structure
struct __attribute__((packed)) {
uint8_t flags;
uint32_t encodedValue;
} measurementData;
The following function will encode a floating point temperature in Celcius in the correct format.
uint32_t encodeValue(float value) {
uint8_t exponent = -3;
int32_t number = 1000*value;
return (0x00ffffff & number) | (exponent << 24);
}
We can measure the temperature using the Application Sensor Relative Humidity sensor. On some radio boards only a mock version will be available.
This should be initialized inapp_init
void app_init(void)
{
sl_sensor_rht_init();
}
The component provides the temperature in milli-Celcius. The following code reads the temperature from the sensor, converts it to the Temperature Measurement format and sends an indication using sl_bt_gatt_server_send_indication
sl_status_t sc;
int32_t temperature = 0;
uint32_t humidity = 0;
// Measure temperature; units are % and milli-Celsius.
sc = sl_sensor_rht_get(&humidity, &temperature);
if(SL_STATUS_OK == sc) {
measurementData.flags = 0;
measurementData.encodedValue = encodeValue(1e-3 * temperature);
sl_bt_gatt_server_send_indication(
connection,
gattdb_temperature_measurement,
5, (uint8_t*)&measurementData);
}
Implementing a working Health Thermometer server is left as an exercise. The following hints may help:
- The event structure for the connection-opened event includes the connection.
- A simple method to send indications at reasonable intervals would be to send an indication every n-th time
app_process_action
is called.
My solution