Hello!
This is the third time I'm asking something about the same project, but I'm running into problems one after the other.
The current problem is this - the hogp object I have saved of a BLE keyboard I'm trying to read the input of has 6 HID handles.
I'm not sure which handle to subscribe to or if even bt_hogp_rep_subscribe() is the correct funciton to call.
I've included the whole program and my prj.conf befow, and also shared the relevant functions as code snippets.
/* --- 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; struct bt_hogp_rep_info *target_rep = NULL; 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); static void exch_mtu_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params); static void conn_connected(struct bt_conn *conn, uint8_t err); static void disconnected_cb(struct bt_conn *conn, uint8_t reason) { LOG_INF("Disconnected, reason: %d", reason); } BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match, connecting_error, connecting); static struct bt_conn_cb conn_callbacks = { .connected = conn_connected, .disconnected = disconnected_cb, }; 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 uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data); 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); bt_conn_cb_register(&conn_callbacks); // 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 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 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 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. Now attempting to subscribe..."); struct bt_hogp_rep_info *rep = bt_hogp_rep_find(hogp, BT_HIDS_REPORT_TYPE_INPUT, 1); size_t hid = bt_hogp_rep_count(hogp); LOG_INF("There are %zu HID reports in the HOGP object.", hid); int err = bt_hogp_rep_subscribe(hogp, rep, hogp_read_cb); if (err) { LOG_INF("There was an error subscribing to the hogp read report: %d", err); } } 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) { } static uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data) { if (err) { LOG_ERR("Error in report notification: %d", err); return 0; } size_t size = bt_hogp_rep_size(rep); LOG_HEXDUMP_INF(data, size, "Received input report (keycodes):"); return 0; }
The hogp ready callback that is subscribing to the handle and invoking the read callback:
static void hogp_ready(struct bt_hogp *hogp) { LOG_INF("The HOG profile has been created successfully. Now attempting to subscribe..."); struct bt_hogp_rep_info *rep = bt_hogp_rep_find(hogp, BT_HIDS_REPORT_TYPE_INPUT, 1); size_t hid = bt_hogp_rep_count(hogp); LOG_INF("There are %zu HID reports in the HOGP object.", hid); int err = bt_hogp_rep_subscribe(hogp, rep, hogp_read_cb); if (err) { LOG_INF("There was an error subscribing to the hogp read report: %d", err); } }
the read callback itself:
static uint8_t hogp_read_cb(struct bt_hogp *hogp, struct bt_hogp_rep_info *rep, uint8_t err, const uint8_t *data) { if (err) { LOG_ERR("Error in report notification: %d", err); return 0; } size_t size = bt_hogp_rep_size(rep); LOG_HEXDUMP_INF(data, size, "Received input report (keycodes):"); return 0; }
Currently, the callback does get called, meaning the subscribe function does succeed, but it gets called once and that's it.
As far as I understand it, this callback should be called every time the keyboard sends out a report on that handle.
Any help with identifying the correct handle to subscribe to and what the correct flow for subscribing is would be greatly appreciated!