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