Hi!
I have been developing a GATTClient and GATTServer on two separate nrf52840dk boards and have been doing so for a long time.
This Friday I got everything to work when it comes to notifications and write without response. Offline I tried adding functionality for extending the data length and setting the PHY to 2 on the server. Once I tried to build this code and flash it to my Server the whole functionality just crashed.
It went from looking like this (GATTClient --> GATTServer) on the left and GATTServer output on the right:
To now the dev-kits are connecting but the Client does not discover the service I have built on the Server side which it has done flawlessly so far.
I THINK I have narrowed it down to some sort of Cmake issue on the Client side for some reason, and I get the following error messages in my output console of the NRF Connect for VSCode:
I have tried:
- Restarting VSCode and my computer several times.
- Updating every extension I have and also VSCode
- Re-installed the nRF Connect extension pack for VSCode
- Re-installed the SDK Toolchain to get a newer version, from 2.7.0 to 2.9.1
And still this issue is there, please help me how to proceed?
Regards,
Björn
PS: Just for reference, I attach the Client code below:
/* * 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> /* --------------------- START OF FROG UUID:s ---------------- */ #define FROG_SERVICE BT_UUID_128_ENCODE(0xd099cd20, 0xb2ea, 0x11e5, 0xbf01, 0x0002a5d5c51b) // Notify characteristics #define FROG_TXDATA_UUID BT_UUID_128_ENCODE(0x388706a0, 0xb2eb, 0x11e5, 0xa790, 0x0002a5d5c51b) #define FROG_RXCREDIT BT_UUID_128_ENCODE(0x43802b40, 0xb2eb, 0x11e5, 0xb6c5, 0x0002a5d5c51b) // Write without response characteristics #define FROG_TXCREDIT_UUID BT_UUID_128_ENCODE(0x2c1b1820, 0xb2eb, 0x11e5, 0x8f66, 0x0002a5d5c51b) #define FROG_RXDATA BT_UUID_128_ENCODE(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 0x0002a5d5c51b) // Macros for service and characteristics #define BT_UUID_FROG BT_UUID_DECLARE_128(FROG_SERVICE) #define BT_UUID_TXDATA BT_UUID_DECLARE_128(FROG_TXDATA_UUID) #define BT_UUID_RXCREDIT BT_UUID_DECLARE_128(FROG_RXCREDIT) #define BT_UUID_TXCREDIT BT_UUID_DECLARE_128(FROG_TXCREDIT_UUID) #define BT_UUID_RXDATA BT_UUID_DECLARE_128(FROG_RXDATA) /* ----------------------------- END ------------------------- */ #define STACKSIZE 1024 #define PRIORITY 7 static uint16_t txcredit_wor_data = 2U; static int scan_start(void); static struct bt_conn *default_conn; /* Variable for write without response*/ static uint16_t char_handle = 0; static struct bt_uuid_16 frog_ccc_discover_uuid = BT_UUID_INIT_16(0); static struct bt_uuid_128 frog_discover_uuid = BT_UUID_INIT_128(0); //static const struct bt_uuid *ccc_uuid = BT_UUID_GATT_CCC; //static const struct bt_uuid *ccc_uuid2 = BT_UUID_GATT_CCC; //static const struct bt_uuid *ccc_uuid2; static struct bt_gatt_discover_params discover_params; static struct bt_gatt_subscribe_params subscribe_params_txdata; static struct bt_gatt_subscribe_params subscribe_params_rxcredit; 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; } printf("[NOTIFICATION]: Data from server %p.\n", data); uint8_t* uint8data = (uint8_t *)data; printf("[NOTIFICATION]: Data from server"); int i; for (i = 0; i < length; ++i) { printf(" 0x%02x", uint8data[i]); } printf("\n"); 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) { 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_FROG)) { printk("Discovering TxData\n"); //What attribute in the table are we looking for //Copy that UUID information into the &frog_discover_uuid address //Put the information into the discover parameters and call the bt_gatt_discover to find that characteristic //BUT search in the next start_handle, otherwise we are looking in the same place. memcpy(&frog_discover_uuid, BT_UUID_TXDATA, sizeof(frog_discover_uuid)); discover_params.uuid = &frog_discover_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_TXDATA)) { printk("Discovering CCCD TxData\n"); memcpy(&frog_ccc_discover_uuid, BT_UUID_GATT_CCC, sizeof(frog_ccc_discover_uuid)); discover_params.uuid = &frog_ccc_discover_uuid.uuid; discover_params.start_handle = bt_gatt_attr_value_handle(attr);//attr->handle + 1; discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; subscribe_params_txdata.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_RXCREDIT)) { printk("[CHARACTERISTIC] RxCredit handle %u\n", attr->handle); printk("Discovering CCC RxCredit\n"); //memcpy(&frog_discover_uuid, BT_UUID_GATT_CCC, sizeof(frog_discover_uuid)); memcpy(&frog_ccc_discover_uuid, BT_UUID_GATT_CCC, sizeof(frog_ccc_discover_uuid)); discover_params.uuid = &frog_ccc_discover_uuid.uuid; discover_params.start_handle = bt_gatt_attr_value_handle(attr); discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; printk("Value handle RxCredit: %d \n", bt_gatt_attr_value_handle(attr)); subscribe_params_rxcredit.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_GATT_CCC)){ //printk("Discover params Start handle:%d\n", discover_params.start_handle); //printk("Discover params Value handle: TxData %d\n", subscribe_params_txdata.value_handle); printk("Discovered CCCD \n"); if (attr->handle-1 == subscribe_params_txdata.value_handle) { printk("[SUBSCRIBE PARAMS] TxData\n"); subscribe_params_txdata.notify = notify_func; subscribe_params_txdata.value = BT_GATT_CCC_NOTIFY; subscribe_params_txdata.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &subscribe_params_txdata); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("[SUBSCRIBED] TxData \n"); } printk("Discovering RxCredit\n"); //memcpy(&frog_discover_uuid, BT_UUID_GATT_CCC, sizeof(frog_discover_uuid)); memcpy(&frog_discover_uuid, BT_UUID_RXCREDIT, sizeof(frog_discover_uuid)); discover_params.uuid = &frog_discover_uuid.uuid; discover_params.start_handle = attr->handle + 1; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; } else if (attr->handle-1 == subscribe_params_rxcredit.value_handle) { printk("[SUBSCRIBE PARAMS] RxCredit\n"); subscribe_params_rxcredit.notify = notify_func; subscribe_params_rxcredit.value = BT_GATT_CCC_NOTIFY; subscribe_params_rxcredit.ccc_handle = attr->handle; err = bt_gatt_subscribe(conn, &subscribe_params_rxcredit); if (err && err != -EALREADY) { printk("Subscribe failed (err %d)\n", err); } else { printk("[SUBSCRIBED] RxCredit\n"); } printk("Discovering TxCredit\n"); memcpy(&frog_discover_uuid, BT_UUID_TXCREDIT, sizeof(frog_discover_uuid)); discover_params.uuid = &frog_discover_uuid.uuid; discover_params.start_handle = attr->handle + 1; discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; } //subscribe_params_rxcredit.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_TXCREDIT)) { // Saving TxCredit handle in char_handle for write without response char_handle = bt_gatt_attr_value_handle(attr); printk("Discovering RxData \n"); memcpy(&frog_discover_uuid, BT_UUID_RXDATA, sizeof(frog_discover_uuid)); discover_params.uuid = &frog_discover_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); } return BT_GATT_ITER_STOP; } return BT_GATT_ITER_STOP; } 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(&frog_discover_uuid, BT_UUID_FROG, sizeof(frog_discover_uuid)); discover_params.uuid = &frog_discover_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 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_UUID128_SOME: case BT_DATA_UUID128_ALL: if (data->data_len % BT_UUID_SIZE_128 != 0U) { printk("AD malformed\n"); return true; } printk("INSIDE SWITCH\n"); for (i = 0; i < data->data_len; i += BT_UUID_SIZE_128) { struct bt_uuid *uuid128; uint8_t btadvData[16]; memcpy(btadvData, data->data, 16); uuid128 = (struct bt_uuid *)btadvData; int err; printk("INSIDE FOR LOOP\n"); if (bt_uuid_cmp(uuid128, BT_UUID_TXDATA)) { //printk("COMPARISON OK!\n"); printk("uuid128 == TXDATA\n"); err = bt_le_scan_stop(); if(err) { printk("Stop LE scan failed (err %d)\n", err); } printk("make connection\n"); err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn); printk("err: %d\n", err); if (err) { printk("Create connection failed (err %d)\n", err); } //continue; return false; } if (bt_uuid_cmp(uuid128, BT_UUID_RXCREDIT)) { //printk("COMPARISON OK!\n"); printk("uuid128 == RXCREDIT\n"); err = bt_le_scan_stop(); if(err) { printk("Stop LE scan failed (err %d)\n", err); } printk("make connection"); err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn); printk("err: %d\n", err); if (err) { printk("Create connection failed (err %d)\n", err); } //continue; 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 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); /* We're only interested in connectable events */ if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) { 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 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 frog_txcredit_wor(uint16_t txcredit_val) { return bt_gatt_write_without_response(default_conn, char_handle, &txcredit_val, sizeof(txcredit_val), false); } static void simulate_data(void) { txcredit_wor_data++; if (txcredit_wor_data == 200) { txcredit_wor_data = 2; } } /* Function to send data using Write Without Response */ void write_without_resp_data_thread(void) { //uint16_t *data, uint16_t len printk("[CHAR] WWOR Handle: %u\n", char_handle); printk("DO WE EVEN GET HERE?!?!\n"); while (1) { if (!default_conn || !char_handle) { printk("No connection or characteristic handle found!\n"); //return; } printk("[CHAR] WWOR Handle: %u\n", char_handle); simulate_data(); int err = frog_txcredit_wor(txcredit_wor_data); if (err) { printk("Write Without Response failed (err %d)\n", err); } else { printk("Write Without Response sent: %u\n", txcredit_wor_data); //len, } k_sleep(K_MSEC(500)); } } 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"); /* Wait for connection */ while (!default_conn) { k_sleep(K_MSEC(100)); } return 0; } K_THREAD_DEFINE(write_without_resp_thread, STACKSIZE, write_without_resp_data_thread, NULL, NULL, NULL, PRIORITY, 0, 0);