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!