BT_ATT_ERR_INVALID_ATTRIBUTE_LEN when writing to a BLE characteristic

Hello,

I'm attempting to write to a ble characteristic but when i call bt_gatt_write, I get BT_ATT_ERR_INVALID_ATTRIBUTE_LEN, I have tried to send the same command using nRF Connect application with ByteArray format and it works.

Here's my source code :

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/sys/printk.h>

static struct bt_conn *default_conn;
static  bt_addr_le_t target_addr = {
	.type = BT_ADDR_LE_PUBLIC,
	.a = {
        .val = {0x0e, 0xa0, 0x80, 0x7d, 0xcf, 0x85}
	}
};
static struct bt_gatt_read_params read_params;
static struct bt_gatt_subscribe_params subscribe_params;

// K_THREAD_DEFINE(ble_thread, 1024, read_characteristic, NULL, NULL, NULL, 5, 0, 0);
K_SEM_DEFINE(ble_sem, 0, 1);

static struct bt_uuid_128 uuid = BT_UUID_INIT_128(
    0xE6, 0xA9, 0xF4, 0xB4, 0x3E, 0x00, 0x0F, 0x9A, 
    0xDC, 0x4E, 0x42, 0x24, 0xD4, 0x74, 0x3D, 0x36
);

// static struct bt_uuid_128 uuid = BT_UUID_INIT_128(
//     0x36, 0x3D, 0x74, 0xD0, 0x24, 0x42, 0x4E, 0xDC, 
//     0x9A, 0x0F, 0x00, 0x3E, 0xB4, 0xF4, 0xA9, 0xE6
// );
static void read_func(struct bt_conn *conn,
                      struct bt_gatt_subscribe_params *params,
                      const void *data, uint16_t length)
{

    if (data) {
        printk("Characteristic value: ");
        for (int i = 0; i < length; i++) {
            printk("%02x ", ((uint8_t *)data)[i]);
        }
        printk("\n");
    } else {
        printk("Read complete\n");
    }
}

static void ccc_write_cb(struct bt_conn *conn, uint8_t err,
    struct bt_gatt_write_params *params)
{
if (err) {
printk("CCCD write failed (err %u)\n", err);
} else {
printk("CCCD write successful\n");
}
}

static struct bt_gatt_write_params write_params;
static uint8_t ccc_value[] = {0x04, 0x62, 0x15, 0x00, 0x00} ; /* Write cmd to send current value*/
// static uint64_t ccc_value = 0x04062150000; /* Write cmd to send current value*/
// static uint8_t ccc_value[] = {'04', '62', '15', '00', '00'} ;
// static uint8_t ccc_value[] = {'0x04', '0x62', '0x15', '0x00', '0x00'} ;
// static uint8_t ccc_value[] = "0x04062150000" ;
// Write cmd to send current value in byte array 
// 0x04, 0x62, 0x15, 0x00, 0x00


static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                             struct bt_gatt_discover_params *params)
{
    if (!attr) {
        printk("Discover complete\n");
        memset(params, 0, sizeof(*params));
        return BT_GATT_ITER_STOP;
    }

    struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;

    if (!bt_uuid_cmp(chrc->uuid, &uuid.uuid)) {
        printk("Found characteristic\n");
        printk("Value handle: %u\n", chrc->value_handle);
        // // Read the characteristic value
        // read_params.func = read_func;
        // read_params.handle_count = 1;
        // read_params.single.handle = chrc->value_handle + 1;
        // read_params.single.offset = 0;
        // Subscribe to notifications
        subscribe_params.value = BT_GATT_CCC_NOTIFY;
        subscribe_params.notify = read_func;
        subscribe_params.ccc_handle = chrc->value_handle; // CCCD handle is usually characteristic handle + 1
        int err = bt_gatt_subscribe(conn, &subscribe_params);
        if (err) {
            printk("Subscribe failed (err %d)\n", err);
        }

        printk("Subscribed\n");
    

        // Write to CCCD to send value of current 
        write_params.data = ccc_value;
        write_params.length = sizeof(ccc_value);
        write_params.handle = chrc->value_handle + 1;// CCCD handle is usually characteristic handle + 
        write_params.func = ccc_write_cb;


        err = bt_gatt_write(conn, &write_params);

        printk("Writing CCCD\n");

        if (err) {
            printk("Failed to write CCCD (err %d)\n", err);
        }

        printk("CCCD write started\n");

        k_sem_give(&ble_sem);
        return BT_GATT_ITER_STOP;
    }else{
        // PRINT UUID
        char uuid_str[37];
        bt_uuid_to_str(chrc->uuid, uuid_str, sizeof(uuid_str));
        printk("UUID Found: %s\n", uuid_str);
    }

    return BT_GATT_ITER_CONTINUE;
}

static void connected(struct bt_conn *conn, uint8_t err)
{
    if (err) {
        printk("Connection failed (err %u)\n", err);
        return;
    }

    default_conn = bt_conn_ref(conn);
    printk("Connected\n");

    // Discover services and characteristics
    static struct bt_gatt_discover_params discover_params;
    memset(&discover_params, 0, sizeof(discover_params));
    discover_params.func = discover_func;
    discover_params.start_handle = 0x0001;
    discover_params.end_handle = 0xffff;
    discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
    printk("Discovering primary services\n");
    int ret = bt_gatt_discover(default_conn, &discover_params);
    if (ret) {
        printk("Discover failed (err %d)\n", ret);
    } else {
        printk("Discover started\n");
    }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    printk("Disconnected (reason %u)\n", reason);

    if (default_conn) {
        bt_conn_unref(default_conn);
        default_conn = NULL;
    }
}

static struct bt_conn_cb conn_callbacks = {
    .connected = connected,
    .disconnected = disconnected,
};

static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type,
                    struct net_buf_simple *buf)
{
    char addr_str[18];
    char add_str_target[18];
    bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
    bt_addr_le_to_str(&target_addr, add_str_target, sizeof(add_str_target));
	printk("Scanned %s || target : %s \n", addr_str, add_str_target);
    if (memcmp(add_str_target, addr_str, 18*sizeof(char)) == 0) {
		printk("Found device: %s (RSSI %d)\n", addr_str, rssi);
        printk("Connecting to %s\n", addr_str);
        bt_le_scan_stop();
        bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &default_conn);
    }
}

void read_characteristic_thread(void)
{
    k_sem_take(&ble_sem, K_FOREVER);
    printk("characteristic read thread\n");
    while (1) {
        // if (default_conn) {
        //     int err = bt_gatt_read(default_conn, &read_params);
        //     if (err) {
        //         printk("Read failed (err %d)\n", err);
        //     }
        // }
        k_sleep(K_SECONDS(1)); // Adjust the sleep duration as needed
    }
}

K_THREAD_DEFINE(read_char_thread, 1024, read_characteristic_thread, NULL, NULL, NULL, 5, 0, 0);

void main(void)
{
    int err;

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

    printk("Bluetooth initialized\n");

    bt_conn_cb_register(&conn_callbacks);

    struct bt_le_scan_param scan_param = {
        .type       = BT_HCI_LE_SCAN_PASSIVE,
        .options    = BT_LE_SCAN_OPT_NONE,
        .interval   = BT_GAP_SCAN_FAST_INTERVAL,
        .window     = BT_GAP_SCAN_FAST_WINDOW,
    };

    err = bt_le_scan_start(&scan_param, scan_cb);
    if (err) {
        printk("Scanning failed to start (err %d)\n", err);
        return;
    }

    printk("Scanning started\n");
}

prj.conf :

CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_HCI=y
CONFIG_BT_CTLR=y
CONFIG_BT_SCAN=y
CONFIG_BT_H4=n
CONFIG_BT_SCAN=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_PRINTK=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_RTT_CONSOLE=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_MODE_IMMEDIATE=n
CONFIG_LOG_MODE_DEFERRED=y

Btw I'm using thingy91/nrf52840

Best regards

Youssef

Parents
  • Hello,

    I think it would be helpful if you were able to show an on-air sniffer log, e.g. using nrf sniffer for BLE when working and failing. If you send two .pcapng files from Wireshark it will be easier to identify the problem in specific. An alternative may be to use the BLE app in nRF Connect for Desktop, then you can plug in an nRF52 DK to act as a central, scan&connect to a specific peripheral, list it's gatt database, and try to read and write arrays to specific handles if you want.

    I do not believe BLE have a built-in feature where a GATT client can directly request a notification from the GATT server. So I assume the bytes you share in ccc_value is some custom implementation, I guess you might try to reverse the order to see if that changes anything. But if that was the problem I would not expect that the return code was BT_ATT_ERR_INVALID_ATTRIBUTE_LEN.

    Kenneth

  • Hello,

    Unfortunately, I am unable to use the nRF Sniffer for BLE because I don’t have any of the supported boards. I do have an nRF9160, but I’m unsure whether it is supported.

    However, I have a screenshot that demonstrates the communication between my nRF Connect application and my device. It shows how the value is sent when I activate notifications and send the command 0462150000 as a ByteArray request.

    Here is the picture illustrating this process:

    Could you please advise me on how to adapt my code accordingly ?

    Best Regards

    Youssef

  • Hi, 

    Youssef Elbattah said:
    Could you please advise me on how to adapt my code accordingly ?

    Sorry no (I don't know what may be the problem), do get yourself an nRF52-DK, nRF52840-DK or nRF52833-DK that you can use either as an nRF Sniffer for BLE or to run the BLE app within nRF Connect for Desktop for comparison. The BLE app provide some better control over the link and may show better what we are missing here (though a sniffer log would even be better). My wild guess would be:

    - try a different handle in case it's not +1.

    - try the opposite order of bytes to see if that helps.

    - experiment with bt_gatt_write() or bt_gatt_write_without_response() to check if there is a difference.

    Kenneth

Reply
  • Hi, 

    Youssef Elbattah said:
    Could you please advise me on how to adapt my code accordingly ?

    Sorry no (I don't know what may be the problem), do get yourself an nRF52-DK, nRF52840-DK or nRF52833-DK that you can use either as an nRF Sniffer for BLE or to run the BLE app within nRF Connect for Desktop for comparison. The BLE app provide some better control over the link and may show better what we are missing here (though a sniffer log would even be better). My wild guess would be:

    - try a different handle in case it's not +1.

    - try the opposite order of bytes to see if that helps.

    - experiment with bt_gatt_write() or bt_gatt_write_without_response() to check if there is a difference.

    Kenneth

Children
No Data
Related