“NF Interface” uses GATT profile from Bluetooth Low Energy (BLE) for communication with the device and to construct an interface. It’s designed to read GATT services along with their characteristics, types, and corresponding values.
BLE specification defines set of standard characteristics. If a device already exposes them, then no further configuration is necessary.
Service | ||
Name | Device information | |
UUID | 0x180A | |
Charasteristic | ||
Name | Model Number String | |
UUID | 0x2A24 | |
Charasteristic | ||
Name | Manufacturer Name String | |
UUID | 0x2A29 |
In the example above, the device exposes Device information service using the standard UUID 0x180A. This service includes two standard characteristics: Model Number String and Manufacturer Name String. These characteristics are documented in the official Bluetooth specifications and can be interpreted by the “NF Interface.”
It is also possible to expose custom services and characteristics, both of which can be supported by the “NF Interface”. However, additional configuration is required. Please consider the following example:
Service | |||
UUID | 43033f70-aab1-4159-bad0-07dff2602374 | ||
Charasteristic | |||
UUID | a6172cec-dce5-4d51-8e3e-fbd61fd25ddc | ||
Descriptor | |||
Name | Characteristic User Description | ||
UUID | 0x2901 | ||
Value | Custom parameter | ||
Descriptor | |||
Name | Characteristic Presentation Format | ||
UUID | 0x2904 | ||
Value | 0x19002700000000 |
Any custom characteristic must have 2 standard descriptors:
The value of Characteristic Presentation Format descriptor should follow Bluetooth Specification 4.0 (Vol. 3), Part G, Section 3.3.3.5.
typedef struct {
uint8_t gatt_format;
int8_t exponent;
uint16_t gatt_unit;
uint8_t gatt_namespace;
uint16_t gatt_nsdesc;
} presentation_format_t;
In the example above format defined as unitless (0x2700) UTF-8 string (0x19).
Sometimes device doesn’t have bluetooth, but have something to expose. Or several devices connected together and only one has bluetooth chip. In that case QR codes can be generated for individual service. Where every device should provide all characteristics under its own service UUID.
Service | ||
UUID | 810915bd-7a4a-467e-ae9b-fbad996604da | |
Charasteristic | ||
Name | Model Number String | |
UUID | 0x2A24 | |
Charasteristic | ||
Name | Manufacturer Name String | |
UUID | 0x2A29 | |
Service | ||
UUID | fa7ed53b-3f3f-46a1-aaa3-9ee64af081a0 | |
Charasteristic | ||
Name | Model Number String | |
UUID | 0x2A24 | |
Charasteristic | ||
Name | Manufacturer Name String | |
UUID | 0x2A29 |
According to BLE specification, a device has the capability to “push” updates to a characteristic. “NF Interface” checks if characteristic has Client Characteristic Configuration descriptor (UUID 0x2902) and if it exists, then it will set the first bit to enable the reception of notifications.
Service | |||
Name | Device information | ||
UUID | 0x180A | ||
Charasteristic | |||
UUID | ef150ac3-8944-46c8-b26f-90d28a66e653 | ||
Descriptor | |||
Name | Characteristic User Description | ||
UUID | 0x2901 | ||
Value | Load average | ||
Descriptor | |||
Name | Characteristic Presentation Format | ||
UUID | 0x2904 | ||
Value | 0x14002700000000 | ||
Descriptor | |||
Name | Client Characteristic Configuration | ||
UUID | 0x2902 | ||
Value | 0x1 | ||
Charasteristic | |||
Name | Model Number String | ||
UUID | 0x2A24 |
In the example above, device has two characteristics: a standard one, Model Number String, and a custom one named Load Average. Since the custom characteristic includes a Client Characteristic Configuration descriptor “NF Interface” will write value 0x1 to it and wait for any updates.
BLE specification allows showing only a single last value for characteristic. However in some certain cases it is useful to show historical data. For example, device can share current temperature and update it every minute. “NF Interface” can read temperature readings for the last day and show them as a graph. This protocol is custom and must be supported by device.
BLE server which is implemented on the device should add the following custom descriptor to the characteristic to indicate historical support:
Descriptor | |||
Name | Historical support | ||
UUID | 65407396-50e4-7fee-6540-739650e47fee | ||
Value | 0x0000 |
If characteristic has the descriptor above, then “NF Interface” will show it’s current value, but also mark it as “historical”. If user tap on it, then “NF Interface” will attempt to write a value for this descriptor and expect to receive several notifications with historical data.
“NF Interface” will write request in the following format:
typedef struct {
uint8_t format;
} message_t;
Where format
can be one of:
Notification from the device must be in the following format:
typedef struct {
uint64_t timestamp;
float value;
} data_point_t;
typedef struct {
uint8_t protocol_version;
uint8_t type;
uint8_t points_length;
data_point_t *points;
} historical_message_t;
Where type
can be one of:
typedef enum {
NOTIFICATION_REALTIME = 0x00,
NOTIFICATION_GRAPH = 0x01,
NOTIFICATION_END_OF_GRAPH = 0x02
} message_type_t;
Once all messages sent, device must send the final message with NOTIFICATION_END_OF_GRAPH
type.
If characteristic supports historical data AND normal notifications, then notifications will be in the following format:
typedef struct {
uint8_t protocol_version;
uint8_t type;
void *value;
} notification_message_t;
Where type=NOTIFICATION_REALTIME
and value determined by Characteristic Presentation Format.