Advertising data via BLE GATT SERVICE with bl_notify_gatt() function

Hi! I am working on an application using the XIAO BLE SENSE soc equipped with the nRF52840 chip. The application should be capable of advertising two streams of data via a BLE GATT SERVICE connection by sending notifications to the client whenever a value is available. The GATT service is constructed of a service and two characteristics, for the moment the value of both characteristics works with the function bt_gatt_attr_read() function and the client can read the values. The problem i am facing is adding the option of letting the client be able to subscribe to the characteristics of my service and get notified whenever a new value has been attained from the GATT service. With the code I have written for the moment, I can connect to the GATT service from the client and enable the subscription, but no values are sent. I get the error code "-22" when i call the bt_gatt_notify, which indicates on invalid argument to the function but i cant seem to figure out what I am doing wrong.

So the problem I'm having is implementing the notify function correctly. The following code is the ble_functions.c file, but i have a corresponding header.h file and a main.c file which uses the "update_advertising_data(picoFarad, batV)" function, updating the values of the two variables float picoFarad_32 and float batV_32.

#include <stdio.h>
#include <math.h>

#include <nrfx_timer.h>
#include <nrfx_gpiote.h>
#include "nrf.h"

#include <zephyr/types.h>
#include <stddef.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/util.h>

#include <hal/nrf_gpio.h>
#include <zephyr/device.h>
#include <zephyr/dt-bindings/adc/adc.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/devicetree.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>
#include "ble_functions.h"

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

// UUID of the Custom Service
#define BT_UUID_MY_CUSTOM_SERV_VAL BT_UUID_128_ENCODE(0x445817d2,0x9e86,4216,8054,0x703dc002ef41)
// 41,ef,02,c0,3d,70,76,1f,78,10,83,9e,d2,17,58,44
#define BT_UUID_MY_CUSTOM_SERVICE BT_UUID_DECLARE_128(BT_UUID_MY_CUSTOM_SERV_VAL)

// UUID of the own Capacitance Characteristics
#define BT_UUID_CAPACITANCE_CHRC BT_UUID_128_ENCODE(0x445817d2,0x9e86,4216,8054,0x703dc002ef42)
// 41,ef,02,c0,3d,70,76,1f,78,10,83,9e,d2,17,58,44
#define BT_UUID_MY_CAPACITANCE_CHRC BT_UUID_DECLARE_128(BT_UUID_CAPACITANCE_CHRC)

// UUID of the own battery state Characteristics
#define BT_UUID_BATV_CHRC BT_UUID_128_ENCODE(0x445817d2,0x9e86,4216,8054,0x703dc002ef43)
// 41,ef,02,c0,3d,70,76,1f,78,10,83,9e,d2,17,58,44
#define BT_UUID_MY_BATV_CHRC BT_UUID_DECLARE_128(BT_UUID_BATV_CHRC)

// BLUETOOTH PARAMS
struct bt_le_adv_param adv_params;
volatile bool ble_ready = false;
float picoFarads_32;
float batV_32;
bool current_conn_state = false;
uint8_t temp = 8;

static const struct bt_data bl_data[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN)
};

/* BLE connection */
struct bt_conn *conn;

// ------------------ Gatt service without notification:
// ssize_t my_read_capacitance_function(struct bt_conn *conn,
// 					const struct bt_gatt_attr *attr, void *buf,
// 					uint16_t len, uint32_t offset);

// ssize_t my_read_battery_status_function(struct bt_conn *conn,
// 					const struct bt_gatt_attr *attr, void *buf,
// 					uint16_t len, uint32_t offset);

// BT_GATT_SERVICE_DEFINE(custom_srv,
//         BT_GATT_PRIMARY_SERVICE(BT_UUID_MY_CUSTOM_SERVICE),
//         BT_GATT_CHARACTERISTIC(BT_UUID_MY_CAPACITANCE_CHRC, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, my_read_capacitance_function, NULL, NULL),
//         BT_GATT_CHARACTERISTIC(BT_UUID_MY_BATV_CHRC, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, my_read_battery_status_function, NULL, NULL)
// );


// ------------------ Gatt service with notification:
ssize_t my_read_capacitance_function(struct bt_conn *conn,
					const struct bt_gatt_attr *attr, void *buf,
					uint16_t len, uint32_t offset);

ssize_t my_read_battery_status_function(struct bt_conn *conn,
					const struct bt_gatt_attr *attr, void *buf,
					uint16_t len, uint32_t offset);

BT_GATT_SERVICE_DEFINE(custom_srv,
        BT_GATT_PRIMARY_SERVICE(BT_UUID_MY_CUSTOM_SERVICE),
        BT_GATT_CHARACTERISTIC(BT_UUID_MY_CAPACITANCE_CHRC, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, my_read_capacitance_function, NULL, NULL),
        BT_GATT_CHARACTERISTIC(BT_UUID_MY_BATV_CHRC, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, my_read_battery_status_function, NULL, NULL)
);

// Notify function for Capactiance read characteristic
ssize_t my_notify_capacitance_function(void *buf, uint16_t len) {
  if (!conn || !current_conn_state) {
    return BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED); // No notification if not connected
  }
  
  // Attempt to notify using bt_gatt_notify
  int ret = bt_gatt_notify(conn, &custom_srv.attrs[1],  &picoFarads_32, sizeof(picoFarads_32));
  if (ret < 0) {
	printk("%d\n",ret);
    // Handle potential errors from bt_gatt_notify
    return BT_GATT_ERR(ret); // Pass through error code from bt_gatt_notify
  }
  // Notification successful (assuming ret >= 0)
  return 0; // Indicate success

}

ssize_t my_read_capacitance_function(struct bt_conn *conn,
					const struct bt_gatt_attr *attr, void *buf,
					uint16_t len, uint32_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &picoFarads_32, sizeof(picoFarads_32));
}

ssize_t my_read_battery_status_function(struct bt_conn *conn,
					const struct bt_gatt_attr *attr, void *buf,
					uint16_t len, uint32_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &batV_32, sizeof(batV_32));
}

static void connected(struct bt_conn *connected, uint8_t err)
{
	if (err) {
		printk("Connection failed (err %u)", err);
	} else {
		printk("Connected");
		if (!conn) {
			conn = bt_conn_ref(connected);
		}
	}
}

static void disconnected(struct bt_conn *disconn, uint8_t reason)
{
	if (conn) {
		bt_conn_unref(conn);
		conn = NULL;
	}

	printk("Disconnected (reason %u)", reason);
}

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

void bt_ready(int err){
	if(err){
		printk("Bluetooth init failed (err %d)", err);
		return;
	}
	ble_ready = true;
	printk("Bluetooth initialized");
	/* Start advertising */
	adv_params = *(BT_LE_ADV_CONN);
	adv_params.interval_min = BT_GAP_ADV_FAST_INT_MIN_1;
	adv_params.interval_max = BT_GAP_ADV_FAST_INT_MAX_1;
	err = bt_le_adv_start(&adv_params, bl_data, ARRAY_SIZE(bl_data), NULL, 0);
	if (err) {
		printk("Advertising failed to start (err %d)", err);
		return;
	}
	printk("Configuration mode: waiting connections...");
}

int init_ble(void){
	int err_bluetooth;
	err_bluetooth = bt_enable(bt_ready);
	if (err_bluetooth)
    {
        printk("Bluetooth initialization failed (err_bluetooth %d)\n", err_bluetooth);
        return err_bluetooth;
    }
	return 0;
}

void update_advertising_data(float picoFarads, float batV)
{
	picoFarads_32 = picoFarads;
	batV_32 = batV;

	// Check for existing connection using the conn flag
	if (conn) { 
		// printk("Active connection: Updating data\n");
		current_conn_state = true;
		// Notify server about capacitance change (assuming notification function exists)
		int err = my_notify_capacitance_function(&picoFarads_32, sizeof(picoFarads_32));
		if (err < 0) {
		printk("Error sending notification (err %d)\n", err);
		}
	} else {
		printk("No active connection: Data not sent\n");
		current_conn_state = false;
	}
    
}

void ble_start_advertising()
{
    bt_le_adv_start(&adv_params, bl_data, ARRAY_SIZE(bl_data), NULL, 0);
}

void ble_stop_advertising()
{
    bt_le_adv_stop();
	bt_disable();
}

The configuration I'm using is:

- The nRF connect SDK (ncs) version used for this project is "v.2.2.0"

- xiao ble nrf52840

Parents
  • Hello,

    Please try to change the '&custom_srv.attrs[1]' parameter to '&custom_srv.attrs[2]' so it points to your characteristic in the service structure and not the service itself. 

    Best regards,

    Vidar

  • Hi Vidar,

    I tried to swap to '&custom_srv.attrs[2]', but i still get the error code '-22' after calling 'int ret = bt_gatt_notify(conn, &custom_srv.attrs[2],  &picoFarads_32, sizeof(picoFarads_32));'. Could it be that i have missed something when defining the notify functionality?

    Best regards,
    Ossian

  • Hi Ossian,

    I see now that you are also missing the CCCD descriptor needed by the client to enable notifications. Please try changing your service declaration like this:

    static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr,
    				  uint16_t value)
    {
    	if (value == BT_GATT_CCC_NOTIFY) {
    		printk("Notifications enabled\n");
    	}
    	else {
    		printk("Notifications disabled\n");
    	} 
    }
    
    BT_GATT_SERVICE_DEFINE(custom_srv,
            BT_GATT_PRIMARY_SERVICE(BT_UUID_MY_CUSTOM_SERVICE),
            BT_GATT_CHARACTERISTIC(BT_UUID_MY_CAPACITANCE_CHRC, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, my_read_capacitance_function, NULL, NULL),
    	BT_GATT_CCC(tx_ccc_cfg_changed,
    			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
            BT_GATT_CHARACTERISTIC(BT_UUID_MY_BATV_CHRC, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, my_read_battery_status_function, NULL, NULL)
    );

    Best regards,

    Vidar

Reply
  • Hi Ossian,

    I see now that you are also missing the CCCD descriptor needed by the client to enable notifications. Please try changing your service declaration like this:

    static void tx_ccc_cfg_changed(const struct bt_gatt_attr *attr,
    				  uint16_t value)
    {
    	if (value == BT_GATT_CCC_NOTIFY) {
    		printk("Notifications enabled\n");
    	}
    	else {
    		printk("Notifications disabled\n");
    	} 
    }
    
    BT_GATT_SERVICE_DEFINE(custom_srv,
            BT_GATT_PRIMARY_SERVICE(BT_UUID_MY_CUSTOM_SERVICE),
            BT_GATT_CHARACTERISTIC(BT_UUID_MY_CAPACITANCE_CHRC, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, my_read_capacitance_function, NULL, NULL),
    	BT_GATT_CCC(tx_ccc_cfg_changed,
    			       BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
            BT_GATT_CHARACTERISTIC(BT_UUID_MY_BATV_CHRC, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, my_read_battery_status_function, NULL, NULL)
    );

    Best regards,

    Vidar

Children
No Data
Related