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
  • Hi,

    Good that you attached the source code!

    That said, on line 87 the comment says `CCCD handle is usually characteristic handle + 1` but the handle that is subscribed to is:

    `chrc->value_handle`. 

    On line 99 the comment says `// CCCD handle is usually characteristic handle +` but the code writes to:

    `chrc->value_handle + 1`.

    Comments that do not align with what the code do are a bit confusing when reviewing. Also, are you sure that the assumptions hinted by the comments are correct? Perhaps it is more safe to find both characteristics by using their UUIDs?

    Perhaps include also the log output in your reply!

  • Hello,

    Yes, I have tried both with and without adding +1 to the handle value. However, when I write without adding +1, I receive the error BT_ATT_ERR_WRITE_NOT_PERMITTED. This approach—adding 1 to the handle when writing—was suggested by one of Nordic’s developers on DevZone.

    Regarding the logs:

    *** Booting nRF Connect SDK v2.7.0-5cb85570ca43 ***
    *** Using Zephyr OS v3.6.99-100befc70c74 ***
    Bluetooth initialized
    Scanning started
    Scanned C3:00:00:34:C3:6A || target : 85:CF:7D:80:A0:0E 
    Scanned 1F:4D:30:A3:1F:DC || target : 85:CF:7D:80:A0:0E 
    Scanned 31:2D:2F:AD:C4:34 || target : 85:CF:7D:80:A0:0E 
    Scanned 1D:2E:07:F6:C4:50 || target : 85:CF:7D:80:A0:0E 
    Scanned 05:65:62:36:6B:DD || target : 85:CF:7D:80:A0:0E 
    Scanned 33:EA:BF:9B:61:73 || target : 85:CF:7D:80:A0:0E 
    Scanned 1D:2E:07:F6:C4:50 || target : 85:CF:7D:80:A0:0E 
    Scanned 05:65:62:36:6B:DD || target : 85:CF:7D:80:A0:0E 
    Scanned F1:1C:C6:BF:1D:BD || target : 85:CF:7D:80:A0:0E 
    Scanned 33:EA:BF:9B:61:73 || target : 85:CF:7D:80:A0:0E 
    Scanned 31:2D:2F:AD:C4:34 || target : 85:CF:7D:80:A0:0E 
    Scanned 85:CF:7D:80:A0:0E || target : 85:CF:7D:80:A0:0E 
    Found device: 85:CF:7D:80:A0:0E (RSSI -70)
    Connecting to 85:CF:7D:80:A0:0E
    Connected
    Discovering primary services
    Discover started
    UUID Found: 2a05
    UUID Found: 2a00
    UUID Found: 2a01
    UUID Found: 2a04
    Found characteristic
    Value handle: 14
    Subscribed
    Writing CCCD
    CCCD write started
    characteristic read thread
    Read complete
    CCCD write failed (err 13)

    Let me know if you need any further clarification!

  • OK. Thanks for the log. Is this a known service you are trying to use? Do you have the source code also for the peripheral? I still think it is better to discover the characteristic instead of assuming which handle it has. Especially since it seems to not be working.

    That said. The function `read_func()` should return an `uint8_t` and not a void. In your case you might want to return `BT_GATT_ITER_CONTINUE`.

    In the function `scan_cb()` the arrays used to store the Bluetooth addresses as strings are too small so you are overwriting some random memory locations when you call the `bt_addr_le_to_str()`. Use the length `BT_ADDR_LE_STR_LEN` as suggested in the documentation for the function instead.

    Next time you submit source code, please clean it up first to avoid confusion and consider to look at the warnings generated by the compiler before raising a ticket.

    By looking at these things I hope you can make progress with your project!

Reply
  • OK. Thanks for the log. Is this a known service you are trying to use? Do you have the source code also for the peripheral? I still think it is better to discover the characteristic instead of assuming which handle it has. Especially since it seems to not be working.

    That said. The function `read_func()` should return an `uint8_t` and not a void. In your case you might want to return `BT_GATT_ITER_CONTINUE`.

    In the function `scan_cb()` the arrays used to store the Bluetooth addresses as strings are too small so you are overwriting some random memory locations when you call the `bt_addr_le_to_str()`. Use the length `BT_ADDR_LE_STR_LEN` as suggested in the documentation for the function instead.

    Next time you submit source code, please clean it up first to avoid confusion and consider to look at the warnings generated by the compiler before raising a ticket.

    By looking at these things I hope you can make progress with your project!

Children
No Data
Related