Hello!
I am currently developing my own custom service with two nrf 52840 development kits where one acts as the Client and one acts as the Server.
I have managed to set up the service and the service is discoverable and the client can subscribe to notifications from the server.
The next step is to perform a loopback test where I want to take the data sent from the Server ---> Client and send it back with my Write without response attributes.
So my questions are:
1: Is there some sample code on how to write from Client --> Server? What do I need to do on both ends? My Client code is inserted below.
2: Once the writing is set up properly, is it possible to just loop the data back to the Server or is there something I need to think about?
3: When the loopback test is set up, is the nrf sniffer a viable way of testing throughput or are there some pot holes I should be careful about?
#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(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 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, 0xb6e5, 0x0002a5d5c51b) // Write without response 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) /* ----------------------------- END ------------------------- */ static int scan_start(void); static struct bt_conn *default_conn; 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"); 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 CCC 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 1:%d\n", discover_params.start_handle); printk("Discover params 2:%d\n", subscribe_params_txdata.value_handle); printk("Discovered CCCD\n"); if (attr->handle-1 == subscribe_params_txdata.value_handle) { printk("[SUBSCRIBE PARAMS]\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]\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]\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]\n"); } return BT_GATT_ITER_STOP; } //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); } } 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 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"); return 0; }
Regards,
B