How to add BLE BT_GATT_CCC_NOTIFY notify function ?

Hi All,

Hardware : nrf52840 early DK (PCA10056)

I modified Zephyr code from "central_multilink" sample to connect to 15 HR devices. The connection to all 15 devices works. Can someone please suggest how to integrate the notify function to receive the data from HR devices? I tried using the  code from central_hr sample but ran into issues adding/merging following functions from central_hr code.

notify_func
eir_found
discover_func
device_found

The issues I encountered were segmentation fault and the notify_func never got called. Appreciate any help. Following is modified central_multilink.c file

Thanks,

Pranav

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

/*
* Copyright (c) 2021 Nordic Semiconductor ASA
* 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/kernel.h>
#include <zephyr/sys/printk.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 <zephyr/sys/byteorder.h>

#define SCAN_INTERVAL 0x0640 /* 1000 ms */
#define SCAN_WINDOW 0x0030 /* 30 ms */
#define INIT_INTERVAL 0x0010 /* 10 ms */
#define INIT_WINDOW 0x0010 /* 10 ms */
#define CONN_INTERVAL 0x0320 /* 1000 ms */
#define CONN_LATENCY 0
#define CONN_TIMEOUT MIN(MAX((CONN_INTERVAL * 125 * \
MAX(CONFIG_BT_MAX_CONN, 6) / 1000), 10), 3200)

static void start_scan(void);

static struct bt_conn *conn_connecting;
static uint8_t volatile conn_count;
static bool volatile is_disconnecting;


#define nDONGLES 15
// A list of allowed HR devices
const char hrd_mac_id[nDONGLES][BT_ADDR_LE_STR_LEN] = {

"37:39", "CF:32", "93:DF", "5D:E4", "4C:8C", "C4:07", "AC:9E", "3B:4F", "03:F2", "71:D2", "F9:86", "02:F1", "EA:AA", "BB:3D", "EF:E9"

};

// Check if the MAC ID is in the list of allowed HR devices
static bool device_allowed(char *addr_str)
{
bool found_device = false;
// Check if the device found is one of the HR Devices from our list
int i = 0;
char *found_HRD;
// Check if we found an allowed HR Device
for (i = 0 ; i < nDONGLES ; i++) {
found_HRD = strstr(addr_str, hrd_mac_id[i]);
//printk("addr_str = %s white list = %s\n", addr_str, hrd_mac_id[i]);
if(found_HRD != NULL) {
//printk("Found an HR Device : %s\n", hrd_mac_id[i]);
found_device = true;
i = nDONGLES; //break; // If we find an allowed HRD, come out of for loop to connect to the device
}
}
return found_device;
}


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;

bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
// Check the allowed list of devices
if(!device_allowed(addr_str)) {
return;
}
//printk("...[HR Device found]: %s (RSSI %d)\n", addr_str, rssi);


struct bt_conn_le_create_param create_param = {
.options = BT_CONN_LE_OPT_NONE,
.interval = INIT_INTERVAL,
.window = INIT_WINDOW,
.interval_coded = 0,
.window_coded = 0,
.timeout = 0,
};
struct bt_le_conn_param conn_param = {
.interval_min = CONN_INTERVAL,
.interval_max = CONN_INTERVAL,
.latency = CONN_LATENCY,
.timeout = CONN_TIMEOUT,
};

if (conn_connecting) {
return;
}

/* We're only interested in connectable events */
if (type != BT_GAP_ADV_TYPE_ADV_IND &&
type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND &&
type != BT_GAP_ADV_TYPE_EXT_ADV) {
return;
}



// connect only to devices in close proximity
/*
if (rssi < -35) {
return;
}*/


if (bt_le_scan_stop()) {
printk("Scanning successfully stopped\n");
return;
}

err = bt_conn_le_create(addr, &create_param, &conn_param,
&conn_connecting);
if (err) {
printk("Create conn to %s failed (%d)\n", addr_str, err);
start_scan();
}
}

static void start_scan(void)
{
struct bt_le_scan_param scan_param = {
.type = BT_HCI_LE_SCAN_PASSIVE,
.options = BT_LE_SCAN_OPT_NONE,
.interval = SCAN_INTERVAL,
.window = SCAN_WINDOW,
};
int err;

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");
}

#if defined(CONFIG_BT_GATT_CLIENT)
static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_exchange_params *params)
{
printk("MTU exchange %u %s (%u)\n", bt_conn_index(conn),
err == 0U ? "successful" : "failed", bt_gatt_get_mtu(conn));
}

static struct bt_gatt_exchange_params mtu_exchange_params[CONFIG_BT_MAX_CONN];

static int mtu_exchange(struct bt_conn *conn)
{
uint8_t conn_index;
int err;

conn_index = bt_conn_index(conn);

printk("MTU (%u): %u\n", conn_index, bt_gatt_get_mtu(conn));

mtu_exchange_params[conn_index].func = mtu_exchange_cb;

err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params[conn_index]);
if (err) {
printk("MTU exchange failed (err %d)", err);
} else {
printk("Exchange pending...");
}

return err;
}
#endif /* CONFIG_BT_GATT_CLIENT */


static char *strremove(char *str, const char *sub) {
char *p, *q, *r;
if (*sub && (q = r = strstr(str, sub)) != NULL) {
size_t len = strlen(sub);
while ((r = strstr(p = r + len, sub)) != NULL) {
while (p < r)
*q++ = *p++;
}
while ((*q++ = *p++) != '\0')
continue;
}
return str;
}

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

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

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

bt_conn_unref(conn_connecting);
conn_connecting = NULL;

start_scan();
return;
}

conn_connecting = NULL;

conn_count++;
if (conn_count < CONFIG_BT_MAX_CONN) {
start_scan();
}
char *newAddr = strremove(addr, "(random)");
//printk("...# of Devicecs Connected (%u): Current Connection: %s\n", conn_count, addr);
printk("Connecting to %s device, Total connected devices = %u\n", newAddr, conn_count);

#if defined(CONFIG_BT_SMP)
int err = bt_conn_set_security(conn, BT_SECURITY_L2);

if (err) {
printk("Failed to set security (%d).\n", err);
}
#endif

#if defined(CONFIG_BT_GATT_CLIENT)
mtu_exchange(conn);
#endif
}

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

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(conn);

if ((conn_count == 1U) && is_disconnecting) {
is_disconnecting = false;
start_scan();
}
conn_count--;
}

static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
char addr[BT_ADDR_LE_STR_LEN];

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

printk("LE conn param req: %s int (0x%04x, 0x%04x) lat %d to %d\n",
addr, param->interval_min, param->interval_max, param->latency,
param->timeout);

return true;
}

static void le_param_updated(struct bt_conn *conn, uint16_t interval,
uint16_t latency, uint16_t timeout)
{
char addr[BT_ADDR_LE_STR_LEN];

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

printk("LE conn param updated: %s int 0x%04x lat %d to %d\n",
addr, interval, latency, timeout);
}

#if defined(CONFIG_BT_SMP)
static void security_changed(struct bt_conn *conn, bt_security_t level,
enum bt_security_err err)
{
char addr[BT_ADDR_LE_STR_LEN];

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

if (!err) {
printk("Security changed: %s level %u\n", addr, level);
} else {
printk("Security failed: %s level %u err %d\n", addr, level,
err);
}
}
#endif

#if defined(CONFIG_BT_USER_PHY_UPDATE)
static void le_phy_updated(struct bt_conn *conn,
struct bt_conn_le_phy_info *param)
{
char addr[BT_ADDR_LE_STR_LEN];

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

printk("LE PHY Updated: %s Tx 0x%x, Rx 0x%x\n", addr, param->tx_phy,
param->rx_phy);
}
#endif /* CONFIG_BT_USER_PHY_UPDATE */

#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
static void le_data_len_updated(struct bt_conn *conn,
struct bt_conn_le_data_len_info *info)
{
char addr[BT_ADDR_LE_STR_LEN];

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

printk("Data length updated: %s max tx %u (%u us) max rx %u (%u us)\n",
addr, info->tx_max_len, info->tx_max_time, info->rx_max_len,
info->rx_max_time);
}
#endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */

static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
//.le_param_req = le_param_req,
//.le_param_updated = le_param_updated,

#if defined(CONFIG_BT_SMP)
.security_changed = security_changed,
#endif

#if defined(CONFIG_BT_USER_PHY_UPDATE)
.le_phy_updated = le_phy_updated,
#endif /* CONFIG_BT_USER_PHY_UPDATE */

#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
.le_data_len_updated = le_data_len_updated,
#endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */
};

static void disconnect(struct bt_conn *conn, void *data)
{
char addr[BT_ADDR_LE_STR_LEN];
int err;

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

printk("Disconnecting %s...\n", addr);
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (err) {
printk("Failed disconnection %s.\n", addr);
}
printk("success.\n");
}

int init_central(uint8_t iterations)
{
int err;

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

printk("Bluetooth initialized\n");

bt_conn_cb_register(&conn_callbacks);

start_scan();

while (true) {
while (conn_count < CONFIG_BT_MAX_CONN) {
k_sleep(K_MSEC(10));
}

k_sleep(K_SECONDS(60));

if (!iterations) {
break;
}
iterations--;
printk("Iterations remaining: %u\n", iterations);

printk("Disconnecting all...\n");
is_disconnecting = true;
bt_conn_foreach(BT_CONN_TYPE_LE, disconnect, NULL);

while (is_disconnecting) {
k_sleep(K_MSEC(10));
}
printk("All disconnected.\n");
}

return 0;
}
  • I tried using the  code from central_hr sample but ran into issues adding/merging following functions from central_hr code.

    This looks more like a code review rather than solving a particular technical problem. Can you please be more specific in what issues you are having so that we can try to solve those problems rather than going through your code first?

  • There are few posts similar to my question, but none of them are useful because the answers are short and missing resolution. The reason I decided to post the source code is to help someone trying to figure out the same problem. If I find the solution with or without your help I will post the complete code. Please ignore the code. I think my questions are specific and to the point with link to references. Appreciate if you or someone from Nordic could help. Also if I were you I would not post a sarcastic comment to my customer in public, I don't appreciate your comment, it does not help me or you or the community.

  • Was not trying to be sarcastic, apologies if it seemed so. 

    The issues I encountered were segmentation fault and the notify_func never got called. Appreciate any help. Following is modified central_multilink.c file

    I would try to help you more if you can give more information on the segmentation faults. You need to know the instruction causing segmentation fault to be able to understand the context of this fault. 

Related