feat(split): central to peripheral communication
This commit is contained in:
parent
6ed14e9634
commit
ff7e0b7e48
6 changed files with 151 additions and 54 deletions
|
@ -3,22 +3,14 @@
|
|||
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#include <zmk/split/bluetooth/service.h>
|
||||
|
||||
int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event, bool state);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators);
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
|
||||
int zmk_split_get_peripheral_battery_level(uint8_t source, uint8_t *level);
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
int zmk_split_central_send_data(enum data_tag, uint8_t data_size, uint8_t *data);
|
||||
|
|
|
@ -10,6 +10,20 @@
|
|||
#include <zmk/sensors.h>
|
||||
|
||||
#define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9
|
||||
#define ZMK_SPLIT_DATA_XFER_MAX_LEN 16
|
||||
|
||||
enum data_tag {
|
||||
// RGB state
|
||||
DATA_TAG_RGB_STATE,
|
||||
// Backlight state
|
||||
DATA_TAG_BACKLIGHT_STATE,
|
||||
// HID indicators state
|
||||
DATA_TAG_HID_INDICATORS_STATE,
|
||||
// Keymap state
|
||||
DATA_TAG_KEYMAP_STATE,
|
||||
// Start of custom tags
|
||||
DATA_TAG_CUSTOM_START,
|
||||
};
|
||||
|
||||
struct sensor_event {
|
||||
uint8_t sensor_index;
|
||||
|
@ -30,6 +44,12 @@ struct zmk_split_run_behavior_payload {
|
|||
char behavior_dev[ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN];
|
||||
} __packed;
|
||||
|
||||
struct zmk_split_data_xfer_data {
|
||||
enum data_tag data_tag;
|
||||
uint8_t data_size;
|
||||
uint8_t data[ZMK_SPLIT_DATA_XFER_MAX_LEN];
|
||||
} __packed;
|
||||
|
||||
int zmk_split_bt_position_pressed(uint8_t position);
|
||||
int zmk_split_bt_position_released(uint8_t position);
|
||||
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001)
|
||||
#define ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002)
|
||||
#define ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000003)
|
||||
#define ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID ZMK_BT_SPLIT_UUID(0x00000004)
|
||||
#define ZMK_SPLIT_BT_CHAR_DATA_XFER_UUID ZMK_BT_SPLIT_UUID(0x00000004)
|
||||
|
|
|
@ -58,6 +58,14 @@ config ZMK_SPLIT_BLE_CENTRAL_SPLIT_RUN_QUEUE_SIZE
|
|||
int "Max number of behavior run events to queue to send to the peripheral(s)"
|
||||
default 5
|
||||
|
||||
config ZMK_SPLIT_BLE_CENTRAL_DATA_XFER_STACK_SIZE
|
||||
int "BLE split central to peripheral thread stack size"
|
||||
default 512
|
||||
|
||||
config ZMK_SPLIT_BLE_CENTRAL_DATA_XFER_QUEUE_SIZE
|
||||
int "Max number of data transfer requests to queue to send to the peripheral(s)"
|
||||
default 5
|
||||
|
||||
config ZMK_SPLIT_BLE_PREF_INT
|
||||
int "Connection interval to use for split central/peripheral connection"
|
||||
default 6
|
||||
|
|
|
@ -28,7 +28,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/events/position_state_changed.h>
|
||||
#include <zmk/events/sensor_event.h>
|
||||
#include <zmk/events/battery_state_changed.h>
|
||||
#include <zmk/hid_indicators_types.h>
|
||||
#include <zmk/events/split_peripheral_status_changed.h>
|
||||
|
||||
static int start_scanning(void);
|
||||
|
@ -53,9 +52,7 @@ struct peripheral_slot {
|
|||
struct bt_gatt_subscribe_params batt_lvl_subscribe_params;
|
||||
struct bt_gatt_read_params batt_lvl_read_params;
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
uint16_t update_hid_indicators;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
uint16_t data_xfer_handle;
|
||||
uint8_t position_state[POSITION_STATE_DATA_LEN];
|
||||
uint8_t changed_positions[POSITION_STATE_DATA_LEN];
|
||||
};
|
||||
|
@ -141,9 +138,7 @@ int release_peripheral_slot(int index) {
|
|||
// Clean up previously discovered handles;
|
||||
slot->subscribe_params.value_handle = 0;
|
||||
slot->run_behavior_handle = 0;
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
slot->update_hid_indicators = 0;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
slot->data_xfer_handle = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -442,12 +437,7 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
|||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID))) {
|
||||
LOG_DBG("Found update HID indicators handle");
|
||||
slot->update_hid_indicators = bt_gatt_attr_value_handle(attr);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
|
@ -469,13 +459,36 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
|
|||
|
||||
bool subscribed = slot->run_behavior_handle && slot->subscribe_params.value_handle;
|
||||
|
||||
} else if (bt_uuid_cmp(chrc_uuid, BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_DATA_XFER_UUID)) == 0) {
|
||||
LOG_DBG("Found data transfer handle");
|
||||
slot->discover_params.uuid = NULL;
|
||||
slot->discover_params.start_handle = attr->handle + 2;
|
||||
slot->data_xfer_handle = bt_gatt_attr_value_handle(attr);
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
} else if (!bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
|
||||
BT_UUID_BAS_BATTERY_LEVEL)) {
|
||||
LOG_DBG("Found battery level characteristics");
|
||||
slot->batt_lvl_subscribe_params.disc_params = &slot->sub_discover_params;
|
||||
slot->batt_lvl_subscribe_params.end_handle = slot->discover_params.end_handle;
|
||||
slot->batt_lvl_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_subscribe_params.notify = split_central_battery_level_notify_func;
|
||||
slot->batt_lvl_subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
split_central_subscribe(conn, &slot->batt_lvl_subscribe_params);
|
||||
|
||||
slot->batt_lvl_read_params.func = split_central_battery_level_read_func;
|
||||
slot->batt_lvl_read_params.handle_count = 1;
|
||||
slot->batt_lvl_read_params.single.handle = bt_gatt_attr_value_handle(attr);
|
||||
slot->batt_lvl_read_params.single.offset = 0;
|
||||
bt_gatt_read(conn, &slot->batt_lvl_read_params);
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
}
|
||||
|
||||
bool subscribed =
|
||||
slot->run_behavior_handle && slot->data_xfer_handle && slot->subscribe_params.value_handle;
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
subscribed = subscribed && slot->sensor_subscribe_params.value_handle;
|
||||
#endif /* ZMK_KEYMAP_HAS_SENSORS */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
subscribed = subscribed && slot->update_hid_indicators;
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING)
|
||||
subscribed = subscribed && slot->batt_lvl_subscribe_params.value_handle;
|
||||
#endif /* IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING) */
|
||||
|
@ -833,47 +846,85 @@ int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *bi
|
|||
return split_bt_invoke_behavior_payload(wrapper);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
K_THREAD_STACK_DEFINE(split_central_data_xfer_q_stack,
|
||||
CONFIG_ZMK_SPLIT_BLE_CENTRAL_DATA_XFER_STACK_SIZE);
|
||||
|
||||
static zmk_hid_indicators_t hid_indicators = 0;
|
||||
struct k_work_q split_central_data_xfer_q;
|
||||
|
||||
static void split_central_update_indicators_callback(struct k_work *work) {
|
||||
zmk_hid_indicators_t indicators = hid_indicators;
|
||||
for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
|
||||
if (peripherals[i].state != PERIPHERAL_SLOT_STATE_CONNECTED) {
|
||||
continue;
|
||||
}
|
||||
K_MSGQ_DEFINE(zmk_split_central_data_xfer_msgq, sizeof(struct zmk_split_data_xfer_data),
|
||||
CONFIG_ZMK_SPLIT_BLE_CENTRAL_DATA_XFER_QUEUE_SIZE, 2);
|
||||
|
||||
if (peripherals[i].update_hid_indicators == 0) {
|
||||
// It appears that sometimes the peripheral is considered connected
|
||||
// before the GATT characteristics have been discovered. If this is
|
||||
// the case, the update_hid_indicators handle will not yet be set.
|
||||
continue;
|
||||
}
|
||||
void split_central_data_xfer_callback(struct k_work *work) {
|
||||
struct zmk_split_data_xfer_data payload;
|
||||
|
||||
int err = bt_gatt_write_without_response(peripherals[i].conn,
|
||||
peripherals[i].update_hid_indicators, &indicators,
|
||||
sizeof(indicators), true);
|
||||
while (k_msgq_get(&zmk_split_central_data_xfer_msgq, &payload, K_NO_WAIT) == 0) {
|
||||
for (uint8_t i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
|
||||
if (peripherals[i].state != PERIPHERAL_SLOT_STATE_CONNECTED) {
|
||||
LOG_ERR("Source not connected");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write HID indicator characteristic (err %d)", err);
|
||||
if (!peripherals[i].data_xfer_handle) {
|
||||
LOG_ERR("Handle not discovered");
|
||||
continue;
|
||||
}
|
||||
|
||||
int err = bt_gatt_write_without_response(peripherals[i].conn,
|
||||
peripherals[i].data_xfer_handle, &payload,
|
||||
sizeof(struct zmk_split_data_xfer_data), true);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Failed to write the data transfer characteristic (err %d)", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static K_WORK_DEFINE(split_central_update_indicators, split_central_update_indicators_callback);
|
||||
K_WORK_DEFINE(split_central_data_xfer_work, split_central_data_xfer_callback);
|
||||
|
||||
int zmk_split_bt_update_hid_indicator(zmk_hid_indicators_t indicators) {
|
||||
hid_indicators = indicators;
|
||||
return k_work_submit_to_queue(&split_central_split_run_q, &split_central_update_indicators);
|
||||
static int split_bt_data_xfer_payload(struct zmk_split_data_xfer_data payload) {
|
||||
LOG_DBG("");
|
||||
|
||||
int err = k_msgq_put(&zmk_split_central_data_xfer_msgq, &payload, K_MSEC(100));
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case -EAGAIN: {
|
||||
LOG_WRN("Consumer message queue full, popping first message and queueing again");
|
||||
struct zmk_split_data_xfer_data discarded_report;
|
||||
k_msgq_get(&zmk_split_central_data_xfer_msgq, &discarded_report, K_NO_WAIT);
|
||||
return split_bt_data_xfer_payload(payload);
|
||||
}
|
||||
default:
|
||||
LOG_WRN("Failed to queue data to send (%d)", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
k_work_submit_to_queue(&split_central_data_xfer_q, &split_central_data_xfer_work);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int zmk_split_central_send_data(enum data_tag tag, uint8_t size, uint8_t *data) {
|
||||
if (size > ZMK_SPLIT_DATA_XFER_MAX_LEN) {
|
||||
LOG_ERR("Payload too large. Size: %d", size);
|
||||
return -EFBIG;
|
||||
}
|
||||
struct zmk_split_data_xfer_data payload;
|
||||
payload.data_tag = tag;
|
||||
payload.data_size = size;
|
||||
memcpy(payload.data, data, size);
|
||||
LOG_HEXDUMP_DBG(&payload, sizeof(struct zmk_split_data_xfer_data), "sending :");
|
||||
return split_bt_data_xfer_payload(payload);
|
||||
}
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
|
||||
|
||||
static int zmk_split_bt_central_init(void) {
|
||||
int zmk_split_bt_central_init(const struct device *_arg) {
|
||||
k_work_queue_start(&split_central_split_run_q, split_central_split_run_q_stack,
|
||||
K_THREAD_STACK_SIZEOF(split_central_split_run_q_stack),
|
||||
CONFIG_ZMK_BLE_THREAD_PRIORITY, NULL);
|
||||
k_work_queue_start(&split_central_data_xfer_q, split_central_data_xfer_q_stack,
|
||||
K_THREAD_STACK_SIZEOF(split_central_data_xfer_q_stack),
|
||||
CONFIG_ZMK_BLE_THREAD_PRIORITY, NULL);
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
|
||||
return IS_ENABLED(CONFIG_ZMK_BLE_CLEAR_BONDS_ON_START) ? 0 : start_scanning();
|
||||
|
|
|
@ -49,6 +49,7 @@ static uint8_t num_of_positions = ZMK_KEYMAP_LEN;
|
|||
static uint8_t position_state[POS_STATE_LEN];
|
||||
|
||||
static struct zmk_split_run_behavior_payload behavior_run_payload;
|
||||
static struct zmk_split_data_xfer_data data_xfer_payload;
|
||||
|
||||
static ssize_t split_svc_pos_state(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
|
@ -101,6 +102,28 @@ static ssize_t split_svc_run_behavior(struct bt_conn *conn, const struct bt_gatt
|
|||
return len;
|
||||
}
|
||||
|
||||
static ssize_t split_svc_data_xfer(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
const void *buf, uint16_t len, uint16_t offset, uint8_t flags) {
|
||||
struct zmk_split_data_xfer_data *payload = attrs->user_data;
|
||||
uint16_t end_addr = offset + len;
|
||||
|
||||
LOG_DBG("offset %d len %d", offset, len);
|
||||
|
||||
memcpy(payload + offset, buf, len);
|
||||
|
||||
// If whole packet transferred correctly
|
||||
if ((end_addr == sizeof(struct zmk_split_data_xfer_data))) {
|
||||
// Raise new data transfer event with received data
|
||||
LOG_HEXDUMP_DBG(&payload, sizeof(struct zmk_split_data_xfer_data), "receiving :");
|
||||
LOG_DBG("Size correct, raising evt, %d", end_addr);
|
||||
struct zmk_split_data_xfer_event event;
|
||||
memcpy(&event.data_xfer, payload, sizeof(struct zmk_split_data_xfer_data));
|
||||
ZMK_EVENT_RAISE(new_zmk_split_data_xfer_event(event));
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t split_svc_num_of_positions(struct bt_conn *conn, const struct bt_gatt_attr *attrs,
|
||||
void *buf, uint16_t len, uint16_t offset) {
|
||||
return bt_gatt_attr_read(conn, attrs, buf, len, offset, attrs->user_data, sizeof(uint8_t));
|
||||
|
@ -147,6 +170,9 @@ BT_GATT_SERVICE_DEFINE(
|
|||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID),
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
split_svc_run_behavior, &behavior_run_payload),
|
||||
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_DATA_XFER_UUID),
|
||||
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
|
||||
split_svc_data_xfer, &data_xfer_payload),
|
||||
BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL,
|
||||
&num_of_positions),
|
||||
#if ZMK_KEYMAP_HAS_SENSORS
|
||||
|
|
Loading…
Add table
Reference in a new issue