
Overview
Low Energy Bluetooth (BLE) is one of the most popular communication protocols for IoT devices. While it shares a name with traditional Bluetooth technology, Bluetooth Classic, BLE is considered a completely different technology, designed for different types of devices. The main difference between Bluetooth Classic and BLE lies in the duty cycle.
BLE achieves its “low energy” characteristic by attempting to keep the radio module powered off as much as possible. The radio module is usually the component that consumes the most power on IoT devices, so keeping it off as much as possible can significantly reduce overall power consumption. In contrast, Bluetooth Classic is designed to keep the radio on as much as possible to minimize latency or maximize bandwidth. Although the details of BLE are beyond the scope of this article, the NovelBits website (https://novelbits.io/bluetooth-low-energy-ble-complete-guide/) provides a comprehensive introduction.The Zephyr Project RTOS (https://zephyrproject.org/) has a mature BLE subsystem that supports popular devices. In this blog post, I will demonstrate how to run our application on the Nordic nRF52840 development kit (https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk) to support BLE.Kconfig
Like most Zephyr-based applications, we need to use Kconfig to enable the necessary features. We need to set the following Kconfig options in the prj.conf file:
- CONFIG_BT: Enable the BLE subsystem.
- CONFIG_BT_PERIPHERAL: Configure the device as a BLE peripheral.
- CONFIG_BT_DEVICE_NAME: The string to be advertised as the device name.
- CONFIG_BT_HCI: Enable the Host Controller Interface subsystem.
Zephyr also includes configuration options to enable common BLE services. For example, the Device Information Service (DIS) provides information about the device’s manufacturer. We can enable DIS in our application by enabling the following configuration options:
-
CONFIG_BT_DIS: Enable DIS.
- CONFIG_BT_DIS_MANUF: Set the name of the device manufacturer, which is advertised as part of DIS.
- CONFIG_BT_DIS_MODEL: Set the model of the device.
- CONFIG_BT_DIS_SERIAL_NUMBER: Set the serial number of the device.
Source Code
Zephyr provides some simple macros and functions that allow us to customize BLE products in our application. Before reviewing the source code, we need to go over some BLE terminology:
-
GATT: Stands for Generic Attribute Profile, which specifies the details of how devices send information via BLE.
-
Service Service: A collection of information transmitted via BLE, such as sensor data.
-
Characteristic Characteristic: The location and manner of presenting actual information.
-
UUID: Abbreviation for Universally Unique ID, a number used to identify services and characteristics.
-
Read Permission Read Permission: An attribute associated with information that can be queried asynchronously by a remote device via BLE.
-
Notify Permission Notify Permission: An attribute associated with information that can notify a remote device immediately when changes occur via BLE.
We can use several macros to implement custom services and characteristics. First, we need to define some base UUIDs that can serve as the basis for the UUIDs used by our services and characteristics.
We can define base UUIDs using the following macros:
#define CUSTOM_BASE_UUID_w32 0x12345678#define CUSTOM_BASE_UUID_w1 0x90AB#define CUSTOM_BASE_UUID_w2 0xCDEF#define CUSTOM_BASE_UUID_w3 0x0123#define CUSTOM_BASE_UUID_w48 0x456789ABCDEF
Then, we can use the macros available in Zephyr to define UUIDs corresponding to our services and characteristics, as shown below:
#define CUSTOM_SERVICE_UUID 0x1#define CUSTOM_CHARACTERISTIC_UUID 0x2#define BT_UUID_CUSTOM_SERVICE BT_UUID_128_ENCODE(CUSTOM_BASE_UUID_w32, CUSTOM_BASE_UUID_w1, CUSTOM_BASE_UUID_w2, CUSTOM_BASE_UUID_w3, CUSTOM_BASE_UUID_w48 + CUSTOM_SERVICE_UUID)#define BT_UUID_CUSTOM_CHARACTERISTIC BT_UUID_128_ENCODE(CUSTOM_BASE_UUID_w32, CUSTOM_BASE_UUID_w1, CUSTOM_BASE_UUID_w2, CUSTOM_BASE_UUID_w3, CUSTOM_BASE_UUID_w48 + CUSTOM_CHARACTERISTIC)
Finally, we can use the macros defined above to initialize the data structures for the UUIDs that will ultimately be used to instantiate BLE services and characteristics.
static struct bt_uuid_128 custom_service_uuid = BT_UUID_INIT_128(BT_UUID_CUSTOM_SERVICE);static struct bt_uuid_128 custom_characteristic_uuid = BT_UUID_INIT_128(BT_UUID_CUSTOM_CHARACTERISTIC)
Then, we can use the macros provided by Zephyr to create a custom service with custom characteristics.
BT_GATT_SERVICE_DEFINE(custom_service, BT_GATT_PRIMARY_SERVICE(&custom_uuid), BT_GATT_CHARACTERISTIC(&custom_characteristic_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, custom_data_callback, NULL, custom_data), BT_GATT_CCC(nextiles_motion_notify_cb, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
The following list describes the permission macros and data types defined above:
-
BT_GATT_CHRC_READ: Allows a remote device to read a specific characteristic value.
-
BT_GATT_CHRC_NOTIFY: Allows notifying a remote device of corresponding data.
-
BT_GATT_PERM_READ: Sets the read permission attributes for the characteristic.
-
custom_data_callback: The function called when a remote device attempts to read the characteristic: A byte array used to send notifications to the remote device.
The response to characteristic reads and notifications is as follows:
static uint8_t custom_data[DATA_LEN];static ssize_t custom_data_callback(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset){ return bt_gatt_attr_read(conn, attr, buf, len, offset, custom_data, DATA_LEN);}void some_thread(void *p1, void *p2, void *p3){ while (1) { struct bt_gatt_attr *custom_char = bt_gatt_find_by_uuid(attr_custom_bt_service, 0 &custom_characteristic_uuid); bt_gatt_notify(NULL, custom_char, custom_data, sizeof(motion_data)); k_msleep(MSEC(1000)); }}
In this article, I introduced how to set up a Zephyr application that supports BLE. I demonstrated the configuration options that need to be enabled and configured and showed how to use the macros and functions provided by Zephyr to configure custom BLE services and characteristics.