ESP32 nimBLE: How to send notifications

nimBLE Notification

If you are using ESP-IDF, there are two Bluetooth options: nimBLE and Bluedroid. The lightweight option is nimBLE, and it supports only BLE, not Bluetooth Classic. In this article, we will see how to send notifications over a characteristic with nimBLE. Notifications are an important aspect of BLE, and allow you to communicate events to the client even when the client is not actively reading the characteristic.


It is assumed that you are familiar with nimBLE on ESP-IDF and are aware of the relevant BLE terms: services, characteristics, GATT, GAP, etc. This article will focus solely on notifications.

It is also assumed that you are familiar with the setup of nimBLE in your project and the various initialization functions. If not, check out the example nimBLE projects here.

Notifications on BLE

Step 1: Define a notification handle

You need a handle to be passed to the notification function every time you want to notify.

uint16_t control_notif_handle;

Step 2: Add the NOTIFY flag in the characteristic definition

You need to add BLE_GATT_CHR_F_NOTIFY flag to the characteristic on which you wish to enable notifications. Also, you need to assign the handle defined above to this characteristic.

For example, consider a service having 2 characteristics: config and control. You may want to enable notifications on control characteristic, and read and write flags on both the characteristics. Your service definition will look like:

static const ble_uuid128_t gatt_svr_svc_control_config_uuid =
    BLE_UUID128_INIT(0xcd, 0x26, ..., 0x56, 0x12);

static const ble_uuid128_t gatt_svr_chr_control_uuid =
    BLE_UUID128_INIT(0x09, 0x01, ..., 0x65, 0x45);

static const ble_uuid128_t gatt_svr_chr_config_uuid =
    BLE_UUID128_INIT(0xbb, 0xbe, ..., 0xad, 0x25);

static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
        /*** Service: Control and config */
        .type = BLE_GATT_SVC_TYPE_PRIMARY,
        .uuid = &gatt_svr_svc_control_config_uuid.u,
        .characteristics = (struct ble_gatt_chr_def[])
        { {
                /*** Characteristic: Control */
                .uuid = &gatt_svr_chr_control_uuid.u,
                .access_cb = gatt_svr_chr_access_config_control,
                .val_handle = &control_notif_handle,
            }, {
                /*** Characteristic: Config */
                .uuid = &gatt_svr_chr_config_uuid.u,
                .access_cb = gatt_svr_chr_access_config_control,
                .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE
            }, {
                0, /* No more characteristics in this service. */ 

        0, /* No more services. */

The above struct is used within the gatt_svr_init() function.

int gatt_svr_init(void)
    int rc;


    rc = ble_gatts_count_cfg(gatt_svr_svcs);
    if (rc != 0) {
        return rc;

    return 0;

And the gatt_svr_init() function, in turn, is called in the function that starts the BLE process.

Step 3: Create a notification function

Create a function to be called whenever you wish to send a notification. This function will have the connection handle and the uint32_t notification data as the arguments.

void notify_over_control_chr(int16_t conn_handle, uint32_t data){
    struct os_mbuf *om;
    if(conn_handle > -1){
        om = ble_hs_mbuf_from_flat(&data, sizeof(&data));
        ESP_LOGI(TAG, "Notifying conn=%d", conn_handle);
        int rc = ble_gattc_notify_custom((uint16_t)conn_handle, control_notif_handle, om);
        if (rc != 0) {
            ESP_LOGE(TAG, "error notifying; rc=%d", rc);

That’s it. Now you simply need to call this function whenever you need to notify a value over a characteristic.

For more tutorials on ESP32, check out Also, you may find this course on ESP32 on Udemy to be quite helpful. Do check it out

Leave a comment

Your email address will not be published. Required fields are marked *