ble notify always returns null

Trying to connect to a ble device, once I connect and discover the notification characteristic I activate it, but in the callback function it always returns null, I don't understand where I'm going wrong, the notification should send me different signals all the time.
here is my code ,
thanks for the help

/* main.c - Application main entry point */

/*
 * Copyright (c) 2020 SixOctets Systems
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <stddef.h>
#include <errno.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#define NAME_LEN 100

static int scan_start(void);

static struct bt_conn *default_conn;
static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
static struct bt_gatt_discover_params discover_params;
static struct bt_gatt_subscribe_params subscribe_params;




static uint8_t notify_func(struct bt_conn *conn,
			   struct bt_gatt_subscribe_params *params,
			   const void *data, uint16_t length)
{
	// if (!data) {
	// 	printk("[UNSUBSCRIBED]\n");
	// 	params->value_handle = 0U;
	// 	return BT_GATT_ITER_STOP;
	// }

	printk("[NOTIFICATION] data %p length %u\n", data, length);

	return BT_GATT_ITER_CONTINUE;
}


static uint8_t discover_func(struct bt_conn *conn,
			     const struct bt_gatt_attr *attr,
			     struct bt_gatt_discover_params *params)
{
	struct bt_gatt_service_val *gatt_service;
	struct bt_gatt_chrc *gatt_chrc;
	struct bt_gatt_include *gatt_include;
	char str[37];

	
	if (!attr) {
			printk("Discover complete\n");
			memset(params, 0, sizeof(*params));
			return BT_GATT_ITER_STOP;
	}
	switch (params->type) {
	case BT_GATT_DISCOVER_SECONDARY:
	case BT_GATT_DISCOVER_PRIMARY:
		gatt_service = attr->user_data;
		bt_uuid_to_str(gatt_service->uuid, str, sizeof(str));
		printk("Service %s found: start handle %x, end_handle %x\n",
		       str, attr->handle, gatt_service->end_handle);
		break;

	case BT_GATT_DISCOVER_CHARACTERISTIC:
		gatt_chrc = attr->user_data;
		bt_uuid_to_str(gatt_chrc->uuid, str, sizeof(str));

		

		if (strcmp(str, "6e400002-b5a3-f393-e0a9-e50e24dcca9e")==0)
		{
			printk("Characteristic %s found: handle %x\n", str,
		       attr->handle);

			printk("Found characteristic to write\n");
			// Valore da scrivere
			uint8_t value[] = {0x01, 0x00};

			// Scrittura senza risposta
			int err = bt_gatt_write_without_response(conn, attr->handle, &value, sizeof(value), false);
			if (err) {
				printk("Failed to write value, err: %d\n", err);
			} else {
				printk("Value written successfully\n");
			}
		}else if (strcmp(str, "6e400003-b5a3-f393-e0a9-e50e24dcca9e")==0)
		{
			printk("Characteristic %s found: handle %x\n", str,
		       attr->handle);

			subscribe_params.notify = notify_func;
			subscribe_params.value = BT_GATT_CHRC_NOTIFY;
			subscribe_params.ccc_handle = attr->handle;

			int err = bt_gatt_subscribe(conn, &subscribe_params);
			if (err && err != -EALREADY) {
				printk("Subscribe failed (err %d)\n", err);
			} else {
				printk("[SUBSCRIBED]\n");
			}

			return BT_GATT_ITER_STOP;
		}
		
		
		break;
	
	case BT_GATT_DISCOVER_INCLUDE:
		gatt_include = attr->user_data;
		bt_uuid_to_str(gatt_include->uuid, str, sizeof(str));
		printk("Include %s found: handle %x, start %x, end %x\n",
		       str, attr->handle, gatt_include->start_handle,
		       gatt_include->end_handle);
		break;
	default:
		bt_uuid_to_str(attr->uuid, str, sizeof(str));
		printk("Descriptor %s found: handle %x\n", str, attr->handle);
		break;
	}
	

	return BT_GATT_ITER_CONTINUE;
}




static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;
	uint8_t len;

	switch (data->type)
	{
	case BT_DATA_NAME_SHORTENED:

	case BT_DATA_NAME_COMPLETE:
		len = MIN(data->data_len, NAME_LEN - 1);
		(void)memcpy(name, data->data, len);
		name[len] = '\0';
		return false;
	default:
		return true;
	}
}

static bool eir_found(struct bt_data *data, void *user_data)
{
	bt_addr_le_t *addr = user_data;
	int i;

	printk("[AD]: %u data_len %u\n", data->type, data->data_len);

	switch (data->type)
	{
	case BT_DATA_UUID16_SOME:
	case BT_DATA_UUID16_ALL:
		if (data->data_len % sizeof(uint16_t) != 0U)
		{
			printk("AD malformed\n");
			return true;
		}

		for (i = 0; i < data->data_len; i += sizeof(uint16_t))
		{
			struct bt_uuid *uuid;
			uint16_t u16;
			int err;

			memcpy(&u16, &data->data[i], sizeof(u16));
			uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16));
			if (bt_uuid_cmp(uuid, BT_UUID_HTS))
			{
				continue;
			}

			err = bt_le_scan_stop();
			if (err)
			{
				printk("Stop LE scan failed (err %d)\n", err);
				continue;
			}

			err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
									BT_LE_CONN_PARAM_DEFAULT,
									&default_conn);
			if (err)
			{
				printk("Create connection failed (err %d)\n",
					   err);
				scan_start();
			}

			return false;
		}
	}

	return true;
}

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
						 struct net_buf_simple *ad)
{
	char name[NAME_LEN];
	uint16_t data_len;

	(void)memset(name, 0, sizeof(name));
	data_len = ad->len;

	bt_data_parse(ad, data_cb, name);

	if (strcmp(name, "") != 0)
	{
		if (strstr(name, "OxySmart") != NULL){
			printk("[nome]: %s \n", name);
			char dev[BT_ADDR_LE_STR_LEN];

			bt_addr_le_to_str(addr, dev, sizeof(dev));

			printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
				   dev, type, ad->len, rssi);

			int err = bt_le_scan_stop();
			if (err)
			{
				printk("Stop LE scan failed (err %d)\n", err);
			}

			err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
									BT_LE_CONN_PARAM_DEFAULT,
									&default_conn);
			if (err)
			{
				printk("Create connection failed (err %d)\n",
					   err);
				scan_start();
			}
			/* We're only interested in connectable events */
			// bt_data_parse(ad, eir_found, (void *)addr);
		}
	}
}

static int scan_start(void)
{
	/* Use active scanning and disable duplicate filtering to handle any
	 * devices that might update their advertising data at runtime.
	 */
	struct bt_le_scan_param scan_param = {
		.type = BT_LE_SCAN_TYPE_ACTIVE,
		.options = BT_LE_SCAN_OPT_NONE,
		.interval = BT_GAP_SCAN_FAST_INTERVAL,
		.window = BT_GAP_SCAN_FAST_WINDOW,
	};

	return bt_le_scan_start(&scan_param, device_found);
}

static void connected(struct bt_conn *conn, uint8_t conn_err)
{
	char addr[BT_ADDR_LE_STR_LEN];
	int err;

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	if (conn_err)
	{
		printk("Failed to connect to %s (%u)\n", addr, conn_err);

		bt_conn_unref(default_conn);
		default_conn = NULL;

		scan_start();
		return;
	}

	printk("Connected: %s\n", addr);

	if (conn == default_conn)
	{
		memcpy(&uuid, BT_UUID_HTS, sizeof(uuid));
		discover_params.uuid = NULL;
		discover_params.func = discover_func;
		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
		err = bt_gatt_discover(default_conn, &discover_params);
		if (err)
		{
			printk("Discover failed(err %d)\n", err);
			return;
		}
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];
	int err;

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);

	if (default_conn != conn)
	{
		return;
	}

	bt_conn_unref(default_conn);
	default_conn = NULL;

	err = scan_start();
	if (err)
	{
		printk("Scanning failed to start (err %d)\n", err);
	}
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
};

int main(void)
{
	int err;

	err = bt_enable(NULL);
	if (err)
	{
		printk("Bluetooth init failed (err %d)\n", err);
		return 0;
	}

	printk("Bluetooth initialized\n");

	err = scan_start();

	if (err)
	{
		printk("Scanning failed to start (err %d)\n", err);
		return 0;
	}

	printk("Scanning successfully started\n");
	return 0;
}

  • Hi Simone,

    Thank you for contacting DevZone at NordicSemi.

    I have been assigned this case, and I will look into it and will update you accordingly.

    Thanks and regards,

    Naeem

  • I'm not sure why it didn't work before but this is how it works :)

    /*
     * Copyright (c) 2023 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stddef.h>
    #include <errno.h>
    #include <stdint.h>
    #include <string.h>
    
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/byteorder.h>
    
    #include <zephyr/bluetooth/att.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #define NAME_LEN 100
    
    #define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x6e400001, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    #define MTU_TEST_SERVICE_NOTIFY_TYPE                                                               \
    	BT_UUID_128_ENCODE(0x6e400003, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    #define MTU_TEST_SERVICE_WRITE_TYPE                                                               \
    	BT_UUID_128_ENCODE(0x6e400002, 0xb5a3, 0xf393, 0xe0a9, 0xe50e24dcca9e)
    
    #define BT_UUID_MTU_TEST	BT_UUID_DECLARE_128(MTU_TEST_SERVICE_TYPE)
    #define BT_UUID_MTU_TEST_NOTIFY BT_UUID_DECLARE_128(MTU_TEST_SERVICE_NOTIFY_TYPE)
    #define BT_UUID_MTU_TEST_WRITE BT_UUID_DECLARE_128(MTU_TEST_SERVICE_WRITE_TYPE)
    
    
    static void start_scan(void);
    
    static struct bt_conn *default_conn;
    
    static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0);
    static const struct bt_uuid *ccc_uuid = BT_UUID_GATT_CCC;
    
    static struct bt_gatt_discover_params discover_params;
    static struct bt_gatt_subscribe_params subscribe_params;
    
    bt_gatt_notify_func_t notify_cb;
    
    static bool data_cb(struct bt_data *data, void *user_data)
    {
    	char *name = user_data;
    	uint8_t len;
    
    	switch (data->type)
    	{
    	case BT_DATA_NAME_SHORTENED:
    
    	case BT_DATA_NAME_COMPLETE:
    		len = MIN(data->data_len, NAME_LEN - 1);
    		(void)memcpy(name, data->data, len);
    		name[len] = '\0';
    		return false;
    	default:
    		return true;
    	}
    }
    
    static void notify_func(struct bt_conn *conn,
    					  const struct bt_gatt_attr *attr,
    					  const void *buf, uint16_t len,
    					  uint16_t offset, uint8_t flags)
    {
    	// Print the received notification data
    	
        if (((uint8_t *)buf)[4] == 0x01)
        {
            printk("Received notification: ");
            printk("spO2 %d ", ((uint8_t *)buf)[5]);
            printk("HR %d ", ((uint8_t *)buf)[6]);
            printk("\n");
        }
        
       
    	
    }
    
    static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
    			     struct bt_gatt_discover_params *params)
    {
    	int err;
    
    	if (!attr) {
    		printk("Discover complete\n");
    		(void)memset(params, 0, sizeof(*params));
    		return BT_GATT_ITER_STOP;
    	}
    
    	printk("[ATTRIBUTE] handle %u\n", attr->handle);
    
    	if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST)) {
            printk("Discovering MTU_TEST_NOTIFY\n");
    		memcpy(&uuid, BT_UUID_MTU_TEST_NOTIFY, sizeof(uuid));
    		discover_params.uuid = &uuid.uuid;
    		discover_params.start_handle = attr->handle + 1;
    		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    
    		err = bt_gatt_discover(conn, &discover_params);
    		if (err) {
    			printk("Discover failed (err %d)\n", err);
    		}
    	} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST_NOTIFY)) {
            printk("Discovering CCC\n");
    		discover_params.uuid = ccc_uuid;
    		discover_params.start_handle = attr->handle + 2;
    		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
    		subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
    
    		err = bt_gatt_discover(conn, &discover_params);
    		if (err) {
    			printk("Discover failed (err %d)\n", err);
    		}
    	} else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST_NOTIFY)){
    		subscribe_params.notify = notify_func;
    		subscribe_params.value = BT_GATT_CCC_NOTIFY;
    		subscribe_params.ccc_handle = attr->handle;
    		err = bt_gatt_subscribe(conn, &subscribe_params);
    		if (err && err != -EALREADY) {
    			printk("Subscribe failed (err %d)\n", err);
    		} else {
    			printk("[SUBSCRIBED]\n");
    		}
    
    	}else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST_WRITE)){
            printk("Found characteristic to write\n");
            // Valore da scrivere
            uint8_t value[] = {0x01, 0x00};
    
            // Scrittura senza risposta
            int err = bt_gatt_write_without_response(conn, attr->handle, &value, sizeof(value), false);
            if (err) {
                printk("Failed to write value, err: %d\n", err);
            } else {
                printk("Value written successfully\n");
            }
            start_scan();
    	}
    	return BT_GATT_ITER_STOP;
    }
    
    static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
    			 struct net_buf_simple *ad)
    {
    	char addr_str[BT_ADDR_LE_STR_LEN];
    	int err;
    
    	if (default_conn) {
    		return;
    	}
    
    	/* We're only interested in connectable events */
    	if (type != BT_GAP_ADV_TYPE_ADV_IND &&
    	    type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
    		return;
    	}
    
    	// printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
    
        char name[NAME_LEN];
    	uint16_t data_len;
    
    	(void)memset(name, 0, sizeof(name));
    	data_len = ad->len;
    
    	bt_data_parse(ad, data_cb, name);
        
        if (strcmp(name, "") != 0)
    	{
            printk("name: %s\n", name);
            
            if (strstr(name, "OxySmart") != NULL) {
                // printk("name: %s\n", name);
    
                int err = bt_le_scan_stop();
    			if (err)
    			{
    				printk("Stop LE scan failed (err %d)\n", err);
    			}
    
    
                err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
    				BT_LE_CONN_PARAM_DEFAULT, &default_conn);
                
                
                if (err) {
                    printk("Create conn to %s failed (%u)\n", addr_str, err);
                    
                }
    	    }
        }
    	
    }
    
    static void start_scan(void)
    {
    	int err;
        struct bt_le_scan_param scan_param = {
                .type = BT_LE_SCAN_TYPE_ACTIVE,
                .options = BT_LE_SCAN_OPT_NONE,
                .interval = BT_GAP_SCAN_FAST_INTERVAL,
                .window = BT_GAP_SCAN_FAST_WINDOW,
            };
    
    	err = bt_le_scan_start(scan_param, device_found);
    	if (err) {
    		printk("Scanning failed to start (err %d)\n", err);
    		return;
    	}
    
    	printk("Scanning successfully started\n");
    }
    
    static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err,
    			    struct bt_gatt_exchange_params *params)
    {
    	printk("%s: MTU exchange %s (%u)\n", __func__,
    	       err == 0U ? "successful" : "failed",
    	       bt_gatt_get_mtu(conn));
    }
    
    static struct bt_gatt_exchange_params mtu_exchange_params = {
    	.func = mtu_exchange_cb
    };
    
    static int mtu_exchange(struct bt_conn *conn)
    {
    	int err;
    
    	printk("%s: Current MTU = %u\n", __func__, bt_gatt_get_mtu(conn));
    
    	printk("%s: Exchange MTU...\n", __func__);
    	err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params);
    	if (err) {
    		printk("%s: MTU exchange failed (err %d)", __func__, err);
    	}
    
    	return err;
    }
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (err) {
    		printk("Failed to connect to %s (%u)\n", addr, err);
    
    		bt_conn_unref(default_conn);
    		default_conn = NULL;
    
    		start_scan();
    		return;
    	}
    
    	printk("Connected: %s\n", addr);
    
    	(void)mtu_exchange(conn);
    
    	if (conn == default_conn) {
    		memcpy(&uuid, BT_UUID_MTU_TEST, sizeof(uuid));
    		discover_params.uuid = &uuid.uuid;
    		discover_params.func = discover_func;
    		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
    		discover_params.type = BT_GATT_DISCOVER_PRIMARY;
    
    		err = bt_gatt_discover(default_conn, &discover_params);
    		if (err) {
    			printk("Discover failed(err %d)\n", err);
    			return;
    		}
    
    
            
    	}
        
    }
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    
    	if (conn != default_conn) {
    		return;
    	}
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
    
    	bt_conn_unref(default_conn);
    	default_conn = NULL;
    
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    };
    
    void run_central_sample(bt_gatt_notify_func_t cb)
    {
    	int err;
    
    	notify_cb = cb;
    
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	start_scan();
    }
    

  • Hi Simone,

    Can you provide more information, as it is unclear which callback you are talking about, and what are your expectations. What do you see / get? 

    Is your project based on some example project from NCS?

    Typically, it is a good idea to start with example and build upon it.

    Can you test Peripheral Uart for example and connect using nRF connect app on mobile and see its behavior.

    And then if you modify such example, and it is not working or not showing expected output, then we can start debugging into it.

    With regards,

    Naeem

Related