Memory error when calling bt_gatt_dm_start

Hello!

I'm trying to make what initially seemed like a straightfoward program - a bluetooth-to-hid adapter.
Since Nordic's sample (BLE Central HIDS) uses the hogp and bt_scan libs, I decided to try using them as well.

However, I seem to be stuck at getting the services from the bt_conn once I've established it.
The program finds the device, and then errors out with the following message:
[00:01:08.417,694] <err> bt_gatt_dm: Discover failed, error: -128.

As far as I can tell, this is a lack of memory error code, so I tried adding memory both to the heap, as well
as touching the CONFIG_BT_ATT_TX_COUNT config.

I've attached both the prj.conf and the main.c below.
(please have in mind I'm new and this is my first serious attempt at embedded programming, so there may be other mistakes along the way)

/* --- Header --- */

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

// The main lib itself includes
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>

#include <bluetooth/scan.h>
#include <bluetooth/gatt_dm.h>
#include <bluetooth/services/hogp.h>

#define TARGET_DEVICE_NAME "Flow84@Lofree"

struct bt_conn *target_conn;
struct bt_hogp target_hogp;

LOG_MODULE_REGISTER(ska_logging, LOG_LEVEL_INF);

/* --- End Header --- */

/* --- BLE Connection Handling --- */

static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable);
static void scan_filter_no_match(struct bt_scan_device_info *device_info, bool connectable);
static void connecting_error(struct bt_scan_device_info *device_info);
static void connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn);

BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, connecting_error, connecting);

static const struct bt_le_scan_param scan_params =
{
    .type     = BT_LE_SCAN_TYPE_ACTIVE,
    .options  = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
    .interval = BT_GAP_SCAN_FAST_INTERVAL,
    .window   = BT_GAP_SCAN_FAST_WINDOW,
};

static const struct bt_scan_init_param init_params = 
{
    .scan_param      = &scan_params,
    .connect_if_match = true,
    .conn_param      = NULL,
};

/* --- End BLE Connection Handling --- */

/* --- GATT Discovery Functions --- */

static void dm_completed(struct bt_gatt_dm *dm, void *context);
static void dm_error(struct bt_conn *conn, int err, void *context);
static void dm_service_not_found(struct bt_conn *conn, void *context);

static struct bt_gatt_dm_cb dm_cbs =
{
    .completed = dm_completed,
    .error_found = dm_error,
    .service_not_found = dm_service_not_found,
};

/* --- End GATT Discovery Functions */

/* --- HOGP Handling --- */

static void hogp_ready(struct bt_hogp *hogp);
static void hogp_init_error(struct bt_hogp *hogp, int error);
static void hogp_pm_update(struct bt_hogp *hogp);

static struct bt_hogp_init_params hogp_params =
{
    .ready_cb = hogp_ready,
    .prep_error_cb = hogp_init_error,
    .pm_update_cb = hogp_pm_update,
};

/* --- End HOGP Handling --- */

int main(void)
{
    int err;

    err = bt_enable(NULL);
    if(err) { LOG_INF("Error initializing Bluetooth stack."); return 1; }

    bt_hogp_init(&target_hogp, &hogp_params);

    // Note: The filtering needs to be defined after the scan object has been initialized
    bt_scan_init(&init_params);
    bt_scan_cb_register(&scan_cb);
    bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_HIDS);
    bt_scan_filter_enable(BT_SCAN_UUID_FILTER, true);

    bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);

    return 0;
}

static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match, bool connectable)
{
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    if(filter_match->uuid.match) { LOG_INF("HID Device found at addr %s with RSSI of %d db. The device is %s", addr_str, device_info->recv_info->rssi, connectable ? "connectable." : "not connectable"); }
}

static void scan_filter_no_match(struct bt_scan_device_info *device_info, bool connectable)
{
    // char addr_str[BT_ADDR_LE_STR_LEN];
    // bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    // LOG_INF("Non-HID device found at address %s with RSSI of %d db", addr_str, device_info->recv_info->rssi);
}

static void connecting_error(struct bt_scan_device_info *device_info) 
{ 
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    LOG_ERR("Error connecting to target device at address %s.", addr_str); 
    return;
}

// I'm doing quite a bit in the connecting function, but it's the only logical point to start most of the opetations
static void connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
    int err;

    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr_str, sizeof(addr_str));

    LOG_INF("Target device found at address %s, connecting...", addr_str);

    if(!conn) { LOG_ERR("Received NULL pointer for connection object, aborting."); return; }

    target_conn = bt_conn_ref(conn);
    
    LOG_INF("Connected! Connection information saved in the global variable. Now starting GATT discovery");

    err = bt_gatt_dm_start(conn, BT_UUID_HIDS, &dm_cbs, NULL);
}

static void dm_completed(struct bt_gatt_dm *dm, void *context) 
{ 
    LOG_INF("HID Service discovery complete!");

    int err = bt_hogp_handles_assign(dm, &target_hogp);
    if (err) {
        LOG_ERR("Failed to assign HOGP handles: %d", err);
        bt_gatt_dm_data_release(dm);
        return;
    }

    bt_gatt_dm_data_print(dm);
    // const struct bt_hogp_rep_info *rep = NULL;

    bt_gatt_dm_data_release(dm);
}

static void dm_error(struct bt_conn *conn, int err, void *context) { LOG_INF("Error when discovering HID service: %d", err); }

static void dm_service_not_found(struct bt_conn *conn, void *context) { LOG_INF("HID service not found."); }

static void hogp_ready(struct bt_hogp *hogp) { LOG_INF("The HOG profile has been created successfully."); }

static void hogp_init_error(struct bt_hogp *hogp, int error) { LOG_INF("Error initializing the HOGP struct: %d", error); } 

static void hogp_pm_update(struct bt_hogp *hogp) {  }

Parents
  • Interesting, it seems like the discovery handling is done wrongly, maybe it helps if you move the discovery call into the connected event callback (or after we exchange MTU), ensuring the low-level link is up before probing for services.

    Try something like this

    static void conn_connected(struct bt_conn *conn, uint8_t err)
    {
        if (err) {
            LOG_ERR("Connection failed (%u)", err);
            return;
        }
        LOG_INF("Connection ready, exchanging MTU");
        static struct bt_gatt_exchange_params exch = { .func = exch_mtu_cb };
        bt_gatt_exchange_mtu(conn, &exch);
    }
    
    static void exch_mtu_cb(struct bt_conn *conn, uint8_t err,
                            struct bt_gatt_exchange_params *params)
    {
        if (err) {
            LOG_ERR("MTU exchange failed (%u)", err);
            return;
        }
        LOG_INF("MTU exchanged, starting GATT discovery");
        bt_gatt_dm_start(conn, BT_UUID_HIDS, &dm_cbs, NULL);
    }
    
    static struct bt_conn_cb conn_callbacks = {
        .connected    = conn_connected,
        .disconnected = disconnected_cb,
    };
    
    bt_conn_cb_register(&conn_callbacks);
    

  • Since I realize I haven't included the prj.conf in the original post, here it is:

    CONFIG_LOG=y
    CONFIG_LOG_DEFAULT_LEVEL=3
    
    # You need to determine the controller's role before enabling the scan lib
    CONFIG_BT=y
    CONFIG_BT_CENTRAL=y
    CONFIG_BT_SCAN=y
    CONFIG_BT_SCAN_FILTER_ENABLE=y
    CONFIG_BT_SCAN_UUID_CNT=1
    CONFIG_BT_SMP=y
    
    CONFIG_BT_GATT_CLIENT=y
    CONFIG_BT_GATT_DM=y
    CONFIG_BT_HOGP=y
    
    CONFIG_HEAP_MEM_POOL_SIZE=2048
    CONFIG_BT_ATT_TX_COUNT=5

  • Thanks for that info Orlin, 

    [00:00:10.093,750] <err> os: >>> ZEPHYR FATAL ERROR 19: Unknown error on CPU 0
    [00:00:10.093,780] <err> os: Current thread: 0x200023a0 (unknown)
    [00:00:10.163,574] <err> os: Halting system

    The thread name is shown as unknown here and we need to know that name to know the context of this assert.

    Can you please enable the below in your prj.conf

    CONFIG_THREAD_ANALYZER=y
    CONFIG_THREAD_NAME=y
    CONFIG_THREAD_MONITOR=y
    CONFIG_DEBUG_OPTIMIZATIONS=y
    

    And do a pristine build so that we can see the thread name causing this assert. It will also be nice to enable the thread monitor to keep an eye on the stack and cpu usage of different threads in your system. 

    When you have done that, please repost the logs for the assert you see and we can then proceed to attempt to diagnose the issue based on that info.

  • Hello again, 

    I literally just managed to solve the problem - I've been looking for the correct config file all morning and finally found it in this post: Problems with the "bt_gatt_dm_start" function
    (I've looked at it before but for some reason didn't register that it was my solution)

    the problem is the CONFIG_BT_GATT_DM_MAX_ATTRS value. despite it being set in the default kconfig file, it needs to be explicitly set once more in prj.conf. Setting it to 50 solved the problem for me though in my case I need a whole lot less. Once I added the value to the conf file the program started working as intended.

    This probably needs to be marked as an issue haha

  • On that note, your code was very helpful in identifying another mistake I'd made - calling the dm start function in the connecting callback results in an error because (I'm presuming) the connection hasn't been established yet. the dm start function in the standard zephyr connected cb however works exactly as expected. 

Reply Children
No Data
Related