Custom Notification Characteristic Not Updating

I'm new to nRF
have done the "nRF Connect SDK Fundamentals", course and have create a custom characteristic to read and write but I cannot get notification to work.

I'm using nRF Connect SDK (vsCode) and nRFConnect to view the BLE output on my iPhone.

I'm based this off zephyr/samples/bluetooth/peripheral and just removed code that I don't need and added a bit. I thought that I was just following the heart rate code with slight changes but I must be missing something. In the nRfConnect app I press the two down arrows and nothing happens to the Value. I'm firing of a notification every second.

If anyone has an example for a custom notification that I can study that would be great, or with the code below can see what I am missing that would be great also.
Also how would I debug this?

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

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/zephyr.h>

#include <zephyr/settings/settings.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>


// NOTIFICATION Service
#define BT_UUID_NOTIFICATION_SERVICE_VAL BT_UUID_128_ENCODE(0x12343333, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
static struct bt_uuid_128 notification_uuid = BT_UUID_INIT_128(BT_UUID_NOTIFICATION_SERVICE_VAL);

// characteristic information
static struct bt_uuid_128 adc_notify_uuid = BT_UUID_INIT_128(
	BT_UUID_128_ENCODE(0x12345611, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));

 #define VND_MAX_LEN 20

// define Notification Service
BT_GATT_SERVICE_DEFINE(adc_svc,
	BT_GATT_PRIMARY_SERVICE(&notification_uuid),
	BT_GATT_CHARACTERISTIC(&adc_notify_uuid.uuid, BT_GATT_CHRC_NOTIFY,
				BT_GATT_PERM_NONE, NULL, NULL, NULL),
);

// load in the advertising services so we can see whats available
static const struct bt_data advertisingData[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NOTIFICATION_SERVICE_VAL),
};

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		printk("Connection failed (err 0x%02x)\n", err);
	} else {
		printk("Connected\n");
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	printk("Disconnected (reason 0x%02x)\n", reason);
}

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

// ble had been enabled and not we start advertising
static void bt_ready(void)
{
	int err;

	printk("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	//start advertising
	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, advertisingData, ARRAY_SIZE(advertisingData), NULL, 0);
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	printk("Advertising successfully started\n");
}

static void notify_adc(void){
	printk("notify adc2 ");
	static uint8_t level[VND_MAX_LEN + 1] = { 'a', 'b', 'a', 'a', '8', 'a', 'a', 'a'};

	int rc;
	rc = bt_gatt_notify(NULL, &adc_svc.attrs[1], &level, sizeof(level));

	return rc == -ENOTCONN ? 0 : rc;
}

void main(void)
{
	printk("testing 20221118");
    int err;

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

	bt_ready();
	while (1) {
		k_sleep(K_SECONDS(1));
		notify_adc();
	}
}

and my prj.conf is

# Increased stack due to settings API usage
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_SMP=y
CONFIG_BT_SIGNING=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DIS=y
CONFIG_BT_ATT_PREPARE_COUNT=5
#CONFIG_BT_BAS=n
#CONFIG_BT_HRS=n
#CONFIG_BT_IAS=n
CONFIG_BT_PRIVACY=y
CONFIG_BT_DEVICE_NAME="Notification A1"
CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_DEVICE_NAME_MAX=65

CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

# setup the debugging
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_UART_CONSOLE=n

Parents
  • Hi Paul, 
    From what I can see in the code is that you have a characteristic with notification permission. But you haven't defined a CCCD characteristic so that the client can enable notification for the characteristic above it. 

    If you have a look at the NUS service (\nrf\subsys\bluetooth\services\nus.c) in peripheral_uart  sample you can find this: 

    Also there were two more issue with notify_adc(), first is that you are sending to attribute handle &adc_svc.attrs[1] which is the declaration of the characteristic, not the value. And secondly, you are trying to send 21 bytes when the maximum is 20 bytes (if you don't use data length extension, max data length is 23 bytes and with 3 bytes overhead you have 20 bytes left )
    This would work: 

    static void notify_adc(void){
    	printk("notify adc2 ");
    	static uint8_t level[20] = { 'a', 'b', 'a', 'a', '8', 'a', 'a', 'a'};
    
    	int rc;
    	rc = bt_gatt_notify(NULL, &adc_svc.attrs[2], &level, sizeof(level));
    
    	return rc == -ENOTCONN ? 0 : rc;
    }

    With this in the declaration: 

    BT_GATT_SERVICE_DEFINE(adc_svc,
    	BT_GATT_PRIMARY_SERVICE(&notification_uuid),
    	BT_GATT_CHARACTERISTIC(&adc_notify_uuid.uuid, BT_GATT_CHRC_NOTIFY,
    				BT_GATT_PERM_NONE, NULL, NULL, NULL),
    	BT_GATT_CCC(NULL,     BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
    
    );


    I would suggest to have a look at my blog here. In the blog I explained why you should use correct attribute handle. 

  • Hi Hung

    Thanks you very much for this information. I was missing a couple of knowledge points and I have studied these, so thanks for this. Much appreciated.

    with the characteristic declaration versus characteristic value

    I tried

    rc = bt_gatt_notify(NULL, &adc_svc.attrs[2], &level, sizeof(level));

    which worked but I also tried

    rc = bt_gatt_notify(NULL, &adc_svc.attrs[1], &level, sizeof(level));

    and this worked also (checked that the firmware was updated by changing the service UUID).

    I assume this is what you are referring to. Or am a I missing something here.

    I also read your blog (thanks for the link), you say "For example in the following service, the characteristic value of BT_UUID_MY_SERVICE_TX and BT_UUID_MY_SERVICE_TEMPERATURE are attrs[3] and attrs[6] respectively."

    and then you say "...in the above example we have the ATT table for the service as follows:"

    with "

    4 TX Char value

    "

    So in my mind it looks like the first sentence should be attrs[4] for service_TX, am I missing something here also?

  • Hi Paul, 


    I think it's my mistake. Either handle, characteristic declare or characteristic value would work. So in your case adc_svc.attrs[1] and adc_svc.attrs[2] would work. 
    I was having the impression that adc_svc.attrs[1] doesn't work but it was because the length was 21 at that point. It was not because of adc_svc.attrs[1]. 

  • thanks Hung
    Much appreciate the feedback and you comment about the characteristic declare and value did highlight some learning that I needed to do, so was well worth hearing. And the other comments and links where very help.

Reply Children
No Data
Related