Hello!
I've been trying to do the same project for about a month now - just a simple keyboard adapter that takes the input from a keyboard and translates it into HID signals to my laptop.
Right now, I'm trying to read the GATT characteristics of the keyboard (I know they're correct and standard since the NRF connect app identifies them immediately) and the bt_gatt_discover() function simply refuses to work! I've attached the file below but there's really nothing special about it - the connection get made successfully, I start scanning and... attr is immediately empty.
So far I've tried setting security, relaxing the filtering in the discover params to a minimum and checking everything possible in the cb function but I can't find a solution. Any help would be welcome!
#include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/logging/log.h> #include <zephyr/sys/byteorder.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/conn.h> #include <zephyr/bluetooth/uuid.h> #include <zephyr/bluetooth/gatt.h> #include <bluetooth/gatt_dm.h> LOG_MODULE_REGISTER(ska_logging); struct bt_conn *target_conn; static void bt_ready(int err); static void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf); static bool scan_parse(struct bt_data *data, void *user_data); static void connected(struct bt_conn *conn, uint8_t err); static void disconnected(struct bt_conn *conn, uint8_t reason); static uint8_t primary_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params); static struct bt_gatt_discover_params primary_discover_all_params = { .type = BT_GATT_DISCOVER_PRIMARY, .uuid = NULL, .func = primary_discover_func, }; // I've tried ordering the structs in their logical order static struct bt_le_scan_param scan_params = { .type = BT_LE_SCAN_TYPE_ACTIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = BT_GAP_SCAN_FAST_INTERVAL, .window = BT_GAP_SCAN_FAST_WINDOW, }; static struct bt_conn_cb conn_callbacks = { .connected = connected, .disconnected = disconnected, // not needed for now .recycled = NULL, }; int main(void) { LOG_INF("Program starting."); int err; // error checking in the callback bt_enable(bt_ready); err = bt_conn_cb_register(&conn_callbacks); if (err) { LOG_ERR("Error registering connection callbacks: %d", err); } err = bt_le_scan_start(&scan_params, scan_cb); if(err) { LOG_ERR("Error starting BLE scan: %d", err); } LOG_INF("Scanning started."); return 0; } static void bt_ready(int err) { if(err) { LOG_ERR("Error initializing BLE stack: %d", err); return; } LOG_INF("BLE stack initialized."); } 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[BT_ADDR_LE_STR_LEN]; bool hid_found = false; bt_data_parse(buf, scan_parse, &hid_found); if(hid_found) { bt_le_scan_stop(); bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); LOG_INF("HID Service found at address %s, connecting...", addr_str); int err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &target_conn); if (err) { LOG_ERR("Connection failed (err %d)", err); } } } static bool scan_parse(struct bt_data *data, void *user_data) { bool *hid_found = (bool *)user_data; switch(data->type) { // Function is just used to set the hid_found flag for now // case BT_DATA_NAME_COMPLETE: // case BT_DATA_NAME_SHORTENED: case BT_DATA_UUID16_SOME: case BT_DATA_UUID16_ALL: for(int i = 0; i < data->data_len; i += 2) { uint16_t service = sys_get_le16(&data->data[i]); switch(service) { case BT_UUID_HIDS_VAL: *hid_found = true; return BT_GATT_ITER_STOP; break; default: break; // Left for further service discovery } } } return BT_GATT_ITER_CONTINUE; } static void connected(struct bt_conn *conn, uint8_t err) { if(err) { LOG_ERR("Error connecting: %d", err); bt_le_scan_start(&scan_params, scan_cb); return; } LOG_INF("Connected! Attempting to set security..."); // This is not necessary, I've included it in case some keyboard wants to set security err = bt_conn_set_security(conn, BT_SECURITY_L1); if(err) { LOG_INF("Error setting security: %d", err); } else { LOG_INF("Secuirty set(theoretically)! Beginning HID service discovery..."); } target_conn = conn; err = bt_gatt_discover(target_conn, &primary_discover_all_params); if (err) { LOG_INF("Error discovering primary GATT services: %d", err); } } static void disconnected(struct bt_conn *conn, uint8_t reason) { LOG_INF("Disconnected, reason: %d", reason); target_conn = NULL; int err = bt_le_scan_start(&scan_params, scan_cb); if (err) { LOG_ERR("Error restarting scan after disconnect: %d", err); return; } LOG_INF("Scanning restarted."); } static uint8_t primary_discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr, struct bt_gatt_discover_params *params) { if(!attr) { LOG_INF("attr is empty, discovery finished."); return BT_GATT_ITER_STOP; } struct bt_gatt_service_val *service_val = (struct bt_gatt_service_val *)attr->user_data; char service_uuid_str[BT_UUID_STR_LEN]; uint16_t service_end_handle; bt_uuid_to_str(service_val->uuid, service_uuid_str, BT_UUID_STR_LEN); service_end_handle = service_val->end_handle; printk("Service with uuid %s found, end handle is 0x%04x", service_uuid_str, service_end_handle); return BT_GATT_ITER_CONTINUE; }