Can't add more than 6 GATT characteristics

I am using nRF Connect Toolchain v 1.9.1 on a nrf52832 processor and I am not able to access any of the GATT characteristics beyond the 6th characteristic that I define.  The other characteristics are visible but when I try to read from them the system resets.  I have seen other posts that reference setting BLE_GATT_DB_MAX_CHARS but I don't see that defined anywhere and I don't see the ble_gatt_db.h file that they reference.  I also can't find anything in the Kconfig build switches that looks relevant.  How do I increase the maximum number of characteristics allowed?

-Camren

Parents
  • Hi Camren, 

    I tried to modify the peripheral_ht sample to add more characteristics and I didn't have any issue adding 9 characteristic with CCCD and can discover them on the phone and can do a read with no problem. 

    Attached is the file. 

    Could you check if you can find any log in the app when it crashed  ?

    Note that BLE_GATT_DB_MAX_CHARS and ble_gatt_db.h  are belong to the legacy nRF5 SDK, not nRF Connect. So it's not related. 

Reply
  • Hi Camren, 

    I tried to modify the peripheral_ht sample to add more characteristics and I didn't have any issue adding 9 characteristic with CCCD and can discover them on the phone and can do a read with no problem. 

    Attached is the file. 

    Could you check if you can find any log in the app when it crashed  ?

    Note that BLE_GATT_DB_MAX_CHARS and ble_gatt_db.h  are belong to the legacy nRF5 SDK, not nRF Connect. So it's not related. 

Children
  • I was able to reproduce your results.  I then modified the design to remove the ht service and add my own.  With this new service I was able to start adding characteristics and was also able to add more than six.  However, when I added a 14th characteristic, then characteristics 7-13 stopped being able to be read.  There is no error thrown (see attached log).  The read just never occurs.  I verified with a breakpoint that the callback for the read never occurs on those characteristics but it does for the others.  I have attached the modified design here.

  • Here is the log: 

    *** Booting Zephyr OS build v2.7.99-ncs1-1 ***
    [0Bluetooth initialized
    temp device is 0x28244, name is TEMP_0
    Advertising successfully started
    0:00:02.342,376] <inf> sdc_hci_driver: SoftDevice Controller build revision:
    0e e7 c5 66 67 18 3c ac b3 d2 cc 81 a3 dc f1 c0 |...fg.<. ........
    c0 36 02 22 |.6."
    [00:00:02.345,977] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
    [00:00:02.346,008] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
    [00:00:02.346,038] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 14.50663 Build 1008232294
    [00:00:02.347,259] <inf> bt_hci_core: Identity: F2:D2:F6:03:F1:D2 (random)
    [00:00:02.347,259] <inf> bt_hci_core: HCI: version 5.2 (0x0b) revision 0x12fe, manufacturer 0x0059
    [00:00:02.347,290] <inf> bt_hci_core: LMP: version 5.2 (0x0b) subver 0x12fe
    Connected

  • Here is the modified source for hts.c

    /** @file
     *  @brief HTS Service sample
     */
    
    /*
     * Copyright (c) 2020 SixOctets Systems
     * Copyright (c) 2019 Aaron Tsui <[email protected]>
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <stddef.h>
    #include <string.h>
    #include <errno.h>
    
    #include <zephyr.h>
    #include <drivers/sensor.h>
    #include <sys/printk.h>
    #include <sys/byteorder.h>
    
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/hci.h>
    #include <bluetooth/conn.h>
    #include <bluetooth/uuid.h>
    #include <bluetooth/gatt.h>
    
    uint32_t            param1_val;
    uint32_t            param2_val;
    uint8_t             param3_val[6];
    uint32_t            param4_val;
    uint8_t             param5_val;
    uint64_t            param6_val;
    uint32_t            param7_val;
    uint8_t             param8_val;
    uint8_t             param9_val;
    uint8_t             param10_val;
    uint8_t             param11_val;
    uint8_t             param12_val;
    uint8_t             param13_val;
    uint8_t             param14_val;
    /*uint32_t            param15_val;
    uint32_t            param16_val;
    uint8_t             param17_val;*/
    static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value);
    static ssize_t ble_on_read_8(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset);
    static ssize_t ble_on_read_32(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset);
    static ssize_t ble_on_read_48(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset);
    static ssize_t ble_on_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
    
    static struct bt_uuid_128 my_servic_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb0, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param1_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb1, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param2_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb2, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param3_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb3, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param4_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb4, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param5_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb5, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param6_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb6, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param7_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb7, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param8_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb8, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param9_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebb9, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param10_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebba, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param11_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebbb, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param12_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebbc, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param13_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebbd, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param14_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebbe, 0x4453, 0xaf0d, 0x957fa5138174));
    /*static struct bt_uuid_128 param15_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebbf, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param16_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebc0, 0x4453, 0xaf0d, 0x957fa5138174));
    static struct bt_uuid_128 param17_uuid =
        BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x66bc7c41, 0xebc1, 0x4453, 0xaf0d, 0x957fa5138174));
    */
    //My Service Declaration
    static BT_GATT_SERVICE_DEFINE(app_service, BT_GATT_PRIMARY_SERVICE(&my_servic_uuid),
                                  BT_GATT_CHARACTERISTIC(&param1_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, ble_on_read_32, 0, &param1_val),
                                  BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
                                  BT_GATT_CHARACTERISTIC(&param2_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_32, ble_on_write, &param2_val),
                                  BT_GATT_CHARACTERISTIC(&param3_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_48, ble_on_write, &param3_val),
                                  BT_GATT_CHARACTERISTIC(&param4_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_32, 0, &param4_val),
                                  BT_GATT_CHARACTERISTIC(&param5_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_8, ble_on_write, &param5_val),
                                  BT_GATT_CHARACTERISTIC(&param6_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_48, ble_on_write, &param6_val),
                                  BT_GATT_CHARACTERISTIC(&param7_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_32, 0, &param7_val),
                                  BT_GATT_CHARACTERISTIC(&param8_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_8, ble_on_write, &param8_val),
                                  BT_GATT_CHARACTERISTIC(&param9_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param9_val),
                                  BT_GATT_CHARACTERISTIC(&param10_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param10_val),
                                  BT_GATT_CHARACTERISTIC(&param11_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param11_val),
                                  BT_GATT_CHARACTERISTIC(&param12_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param12_val),
                                  BT_GATT_CHARACTERISTIC(&param13_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param13_val),
                                  BT_GATT_CHARACTERISTIC(&param14_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_8, 0, &param14_val),
    /*                              BT_GATT_CHARACTERISTIC(&param15_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_32, ble_on_write, &param15_val),
                                  BT_GATT_CHARACTERISTIC(&param16_uuid.uuid, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, ble_on_read_32, 0, &param16_val),
                                  BT_GATT_CHARACTERISTIC(&param17_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, ble_on_read_8, ble_on_write, &param17_val),
    */
                                  );
    static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
        return;
    }
    static ssize_t ble_on_read_8(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) {
        const uint8_t *value = attr->user_data;
        return bt_gatt_attr_read(c, attr, buf, len, offset, value, 1);
    }
    static ssize_t ble_on_read_32(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) {
        const uint8_t *value = attr->user_data;
        return bt_gatt_attr_read(c, attr, buf, len, offset, value, 4);
    }
    static ssize_t ble_on_read_48(struct bt_conn *c, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) {
        const uint8_t *value = attr->user_data;
        return bt_gatt_attr_read(c, attr, buf, len, offset, value, 6);
    }
    static ssize_t ble_on_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) {
        return len;
    }
    
    #ifdef CONFIG_TEMP_NRF5
    static const struct device *temp_dev = DEVICE_DT_GET_ANY(nordic_nrf_temp);
    #else
    static const struct device *temp_dev;
    #endif
    
    static uint8_t simulate_htm;
    static uint8_t indicating;
    static struct bt_gatt_indicate_params ind_params;
    
    static void htmc_ccc_cfg_changed(const struct bt_gatt_attr *attr,
                     uint16_t value)
    {
        simulate_htm = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
    }
    
    static void indicate_cb(struct bt_conn *conn,
                struct bt_gatt_indicate_params *params, uint8_t err)
    {
        printk("Indication %s\n", err != 0U ? "fail" : "success");
    }
    
    static void indicate_destroy(struct bt_gatt_indicate_params *params)
    {
        printk("Indication complete\n");
        indicating = 0U;
    }
    static bool                   button_state;
    static ssize_t read_button(struct bt_conn *conn,
                  const struct bt_gatt_attr *attr,
                  void *buf,
                  uint16_t len,
                  uint16_t offset)
    {
        const char *value = attr->user_data;
    
        button_state=1;
            return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
                         sizeof(*value));
       
    
    
    }
    /* Health Thermometer Service Declaration */
    /*BT_GATT_SERVICE_DEFINE(hts_svc,
        BT_GATT_PRIMARY_SERVICE(BT_UUID_HTS),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
        BT_GATT_CHARACTERISTIC(BT_UUID_HTS_MEASUREMENT, BT_GATT_CHRC_INDICATE|BT_GATT_CHRC_READ|BT_GATT_CHRC_WRITE,
                       BT_GATT_PERM_READ|BT_GATT_PERM_WRITE, read_button, NULL, &button_state),
            BT_GATT_CCC(htmc_ccc_cfg_changed,
                BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),                                                                                                                    
        /* more optional Characteristics */
    //);
    
    void hts_init(void)
    {
        if (temp_dev == NULL || !device_is_ready(temp_dev)) {
            printk("no temperature device; using simulated data\n");
            temp_dev = NULL;
        } else {
            printk("temp device is %p, name is %s\n", temp_dev,
                   temp_dev->name);
        }
    }
    
    void hts_indicate(void)
    {
        /* Temperature measurements simulation */
    /*  struct sensor_value temp_value;
    
        if (simulate_htm) {
            static uint8_t htm[5];
            static double temperature = 20U;
            uint32_t mantissa;
            uint8_t exponent;
            int r;
    
            if (indicating) {
                return;
            }
    
            if (!temp_dev) {
                temperature++;
                if (temperature == 30U) {
                    temperature = 20U;
                }
    
                goto gatt_indicate;
            }
    
            r = sensor_sample_fetch(temp_dev);
            if (r) {
                printk("sensor_sample_fetch failed return: %d\n", r);
            }
    
            r = sensor_channel_get(temp_dev, SENSOR_CHAN_DIE_TEMP,
                           &temp_value);
            if (r) {
                printk("sensor_channel_get failed return: %d\n", r);
            }
    
            temperature = sensor_value_to_double(&temp_value);
    
    gatt_indicate:
            printf("temperature is %gC\n", temperature);
    
            mantissa = (uint32_t)(temperature * 100);
            exponent = (uint8_t)-2;
    
            htm[0] = 0; /* temperature in celcius */
    /*      sys_put_le24(mantissa, (uint8_t *)&htm[1]);
            htm[4] = exponent;
    
            ind_params.attr = &hts_svc.attrs[2];
            ind_params.func = indicate_cb;
            ind_params.destroy = indicate_destroy;
            ind_params.data = &htm;
            ind_params.len = sizeof(htm);
    
            if (bt_gatt_indicate(NULL, &ind_params) == 0) {
                indicating = 1U;
            }
        }
    */
        param1_val = 0;
        param2_val = 1;
        param3_val[0] = 2;
        param3_val[1] = 0;
        param3_val[2] = 0;
        param3_val[3] = 0;
        param3_val[4] = 0;
        param3_val[5] = 0;
        param4_val = 3;
        param5_val = 4;
        param6_val = 5;
        param7_val = 6;
        param8_val = 7;
        param9_val = 8;
        param10_val = 9;
        param11_val = 10;
        param12_val = 11;
        param13_val = 12;
        param14_val = 13;
    /*  param15_val = 14;
        param16_val = 15;
        param17_val = 16;*/
    }
  • Hi Camren, 
    I tried your code here and can read all the characteristics. The last one I read, it returns 0x0D. I assume it's as expected. 

    So it could be an issue with the phone that you are testing. Please make sure you clear the cache before you test. What you need to do is to delete bond if any and turn off and on bluetooth. 
    Please try to test using nRF Connect app. 

  • I tried it using the nRF Connect app and had the same behavior.  I guess I will do some additional investigation to see if I can pin point why I am getting differenct behavior.  What hardware are you running the embedded code on?  What device are you running the nRF Connect app on?  Thanks.

Related