Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(split): Transfer arbitrary tagged data from central to peripheral #2036

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ target_sources_ifdef(CONFIG_ZMK_BATTERY_REPORTING app PRIVATE src/battery.c)
target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE src/events/hid_indicators_changed.c)

target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_data_xfer_event.c)
add_subdirectory(src/split)

target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/usb.c)
Expand Down
17 changes: 17 additions & 0 deletions app/include/zmk/events/split_data_xfer_event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <zephyr/kernel.h>
#include <zmk/event_manager.h>
#include <zmk/split/bluetooth/service.h>

struct zmk_split_data_xfer_event {
struct zmk_split_data_xfer_data data_xfer;
};

ZMK_EVENT_DECLARE(zmk_split_data_xfer_event);
14 changes: 3 additions & 11 deletions app/include/zmk/split/bluetooth/central.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
20 changes: 20 additions & 0 deletions app/include/zmk/split/bluetooth/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion app/include/zmk/split/bluetooth/uuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
63 changes: 62 additions & 1 deletion app/src/backlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,19 @@
#include <zmk/activity.h>
#include <zmk/backlight.h>
#include <zmk/usb.h>
#include <zmk/ble.h>
#include <zmk/event_manager.h>
#include <zmk/events/activity_state_changed.h>
#include <zmk/events/usb_conn_state_changed.h>
#include <zmk/events/split_peripheral_status_changed.h>
#include <zmk/workqueue.h>

// Pull data sending framework if central, data receiving eventt if peripheral(s)
#if ZMK_BLE_IS_CENTRAL
#include <zmk/split/bluetooth/central.h>
#elif IS_ENABLED(CONFIG_ZMK_SPLIT)
#include <zmk/events/split_data_xfer_event.h>
#endif

LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

Expand All @@ -42,7 +52,25 @@ struct backlight_state {
static struct backlight_state state = {.brightness = CONFIG_ZMK_BACKLIGHT_BRT_START,
.on = IS_ENABLED(CONFIG_ZMK_BACKLIGHT_ON_START)};

#if ZMK_BLE_IS_CENTRAL

static void zmk_backlight_send_state(struct k_work *work) {
LOG_HEXDUMP_DBG(&state, sizeof(struct backlight_state), "backlight state");
int err = zmk_split_central_send_data(DATA_TAG_BACKLIGHT_STATE, sizeof(struct backlight_state),
(uint8_t *)&state);
if (err) {
LOG_ERR("send failed (err %d)", err);
}
}

K_WORK_DEFINE(backlight_send_state_work, zmk_backlight_send_state);

#endif

static int zmk_backlight_update(void) {
#if ZMK_BLE_IS_CENTRAL
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &backlight_send_state_work);
#endif
uint8_t brt = zmk_backlight_get_brt();
LOG_DBG("Update backlight brightness: %d%%", brt);

Expand Down Expand Up @@ -157,7 +185,10 @@ static int backlight_auto_state(bool *prev_state, bool new_state) {
*prev_state = !new_state;
return zmk_backlight_update();
}
#endif

#if IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE) || \
IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB) || ZMK_BLE_IS_CENTRAL
static int backlight_event_listener(const zmk_event_t *eh) {

#if IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE)
Expand All @@ -174,12 +205,19 @@ static int backlight_event_listener(const zmk_event_t *eh) {
}
#endif

#if ZMK_BLE_IS_CENTRAL
if (as_zmk_split_peripheral_status_changed(eh)) {
// TODO: Have this only update when connected
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &backlight_send_state_work);
}
#endif

return -ENOTSUP;
}

ZMK_LISTENER(backlight, backlight_event_listener);
#endif // IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE) ||
// IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB)
// IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB) || ZMK_BLE_IS_CENTRAL

#if IS_ENABLED(CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE)
ZMK_SUBSCRIPTION(backlight, zmk_activity_state_changed);
Expand All @@ -189,4 +227,27 @@ ZMK_SUBSCRIPTION(backlight, zmk_activity_state_changed);
ZMK_SUBSCRIPTION(backlight, zmk_usb_conn_state_changed);
#endif

#if ZMK_BLE_IS_CENTRAL
ZMK_SUBSCRIPTION(backlight, zmk_split_peripheral_status_changed);
#endif

#if IS_ENABLED(CONFIG_ZMK_SPLIT) && !IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)

static int backlight_data_event_listener(const zmk_event_t *eh) {
const struct zmk_split_data_xfer_event *ev = as_zmk_split_data_xfer_event(eh);
if (ev->data_xfer.data_tag == DATA_TAG_BACKLIGHT_STATE) {
LOG_DBG("Backlight Data received of size: %d", ev->data_xfer.data_size);
LOG_HEXDUMP_DBG(ev->data_xfer.data, sizeof(struct zmk_split_data_xfer_event),
"received event:");
memcpy(&state, ev->data_xfer.data, sizeof(struct backlight_state));
LOG_HEXDUMP_DBG(&state, sizeof(struct backlight_state), "backlight state");
zmk_backlight_update_and_save();
}
return 0;
}

ZMK_LISTENER(backlight_data, backlight_data_event_listener);
ZMK_SUBSCRIPTION(backlight_data, zmk_split_data_xfer_event);
#endif

SYS_INIT(zmk_backlight_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
2 changes: 1 addition & 1 deletion app/src/behaviors/behavior_backlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ static const struct behavior_driver_api behavior_backlight_driver_api = {
on_keymap_binding_convert_central_state_dependent_params,
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
.locality = BEHAVIOR_LOCALITY_CENTRAL,
};

BEHAVIOR_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, POST_KERNEL,
Expand Down
2 changes: 1 addition & 1 deletion app/src/behaviors/behavior_rgb_underglow.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
on_keymap_binding_convert_central_state_dependent_params,
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
.locality = BEHAVIOR_LOCALITY_CENTRAL,
};

BEHAVIOR_DT_INST_DEFINE(0, behavior_rgb_underglow_init, NULL, NULL, NULL, POST_KERNEL,
Expand Down
10 changes: 10 additions & 0 deletions app/src/events/split_data_xfer_event.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (c) 2023 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <zephyr/kernel.h>
#include <zmk/events/split_data_xfer_event.h>

ZMK_EVENT_IMPL(zmk_split_data_xfer_event);
24 changes: 20 additions & 4 deletions app/src/hid_indicators.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <zmk/events/hid_indicators_changed.h>
#include <zmk/events/endpoint_changed.h>
#include <zmk/split/bluetooth/central.h>
#include <zmk/events/split_peripheral_status_changed.h>
#include <zmk/workqueue.h>

LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

Expand All @@ -27,14 +29,23 @@ zmk_hid_indicators_t zmk_hid_indicators_get_profile(struct zmk_endpoint_instance
return hid_indicators[profile];
}

static void zmk_hid_indicators_send_state(struct k_work *work) {
zmk_hid_indicators_t indicators = zmk_hid_indicators_get_current_profile();
int err = zmk_split_central_send_data(DATA_TAG_HID_INDICATORS_STATE,
sizeof(zmk_hid_indicators_t), (uint8_t *)&indicators);
if (err) {
LOG_ERR("HID indicators send failed (err %d)", err);
}
}

K_WORK_DEFINE(hid_indicators_send_state_work, zmk_hid_indicators_send_state);

static void raise_led_changed_event(struct k_work *_work) {
const zmk_hid_indicators_t indicators = zmk_hid_indicators_get_current_profile();

raise_zmk_hid_indicators_changed((struct zmk_hid_indicators_changed){.indicators = indicators});

#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) && IS_ENABLED(CONFIG_ZMK_SPLIT_BLE)
zmk_split_bt_update_hid_indicator(indicators);
#endif
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &hid_indicators_send_state_work);
}

static K_WORK_DEFINE(led_changed_work, raise_led_changed_event);
Expand All @@ -60,9 +71,14 @@ void zmk_hid_indicators_process_report(struct zmk_hid_led_report_body *report,
}

static int profile_listener(const zmk_event_t *eh) {
raise_led_changed_event(NULL);
if (as_zmk_endpoint_changed(eh)) {
raise_led_changed_event(NULL);
} else if (as_zmk_split_peripheral_status_changed(eh)) {
k_work_submit_to_queue(zmk_workqueue_lowprio_work_q(), &hid_indicators_send_state_work);
}
return 0;
}

static ZMK_LISTENER(profile_listener, profile_listener);
static ZMK_SUBSCRIPTION(profile_listener, zmk_endpoint_changed);
static ZMK_SUBSCRIPTION(profile_listener, zmk_split_peripheral_status_changed);
10 changes: 7 additions & 3 deletions app/src/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ static inline int set_layer_state(uint8_t layer, bool state) {
// Don't send state changes unless there was an actual change
if (old_state != _zmk_keymap_layer_state) {
LOG_DBG("layer_changed: layer %d state %d", layer, state);
ret = raise_layer_state_changed(layer, state);
if (ret < 0) {
LOG_WRN("Failed to raise layer state changed (%d)", ret);
raise_layer_state_changed(layer, state);
#if ZMK_BLE_IS_CENTRAL
int err = zmk_split_central_send_data(DATA_TAG_KEYMAP_STATE, sizeof(uint32_t),
(uint8_t *)&_zmk_keymap_layer_state);
if (err) {
LOG_ERR("Keymap send failed (err %d)", err);
}
#endif
}

return ret;
Expand Down
Loading
Loading