Merge pull request #1 from petejohanson/stephen/split-encoder

split encoder (1841)
This commit is contained in:
j-w-e 2023-08-14 22:11:27 +02:00 committed by GitHub
commit fd80f40609
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 46 deletions

View file

@ -8,6 +8,10 @@
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#define ZMK_LAYER_CHILD_LEN_PLUS_ONE(node) 1 +
#define ZMK_KEYMAP_LAYERS_LEN \
(DT_FOREACH_CHILD(DT_INST(0, zmk_keymap), ZMK_LAYER_CHILD_LEN_PLUS_ONE) 0)
typedef uint32_t zmk_keymap_layers_state_t; typedef uint32_t zmk_keymap_layers_state_t;
uint8_t zmk_keymap_layer_default(); uint8_t zmk_keymap_layer_default();

View file

@ -24,7 +24,9 @@ struct zmk_sensor_config {
uint16_t triggers_per_rotation; uint16_t triggers_per_rotation;
}; };
// This struct is also used for data transfer for splits, so any changes to the size, layout, etc
// is a breaking change for the split GATT service protocol.
struct zmk_sensor_channel_data { struct zmk_sensor_channel_data {
enum sensor_channel channel;
struct sensor_value value; struct sensor_value value;
}; enum sensor_channel channel;
} __packed;

View file

@ -6,8 +6,18 @@
#pragma once #pragma once
#include <zmk/events/sensor_event.h>
#include <zmk/sensors.h>
#define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9 #define ZMK_SPLIT_RUN_BEHAVIOR_DEV_LEN 9
struct sensor_event {
uint8_t sensor_index;
uint8_t channel_data_size;
struct zmk_sensor_channel_data channel_data[ZMK_SENSOR_EVENT_MAX_CHANNELS];
} __packed;
struct zmk_split_run_behavior_data { struct zmk_split_run_behavior_data {
uint8_t position; uint8_t position;
uint8_t state; uint8_t state;
@ -22,3 +32,6 @@ struct zmk_split_run_behavior_payload {
int zmk_split_bt_position_pressed(uint8_t position); int zmk_split_bt_position_pressed(uint8_t position);
int zmk_split_bt_position_released(uint8_t position); int zmk_split_bt_position_released(uint8_t position);
int zmk_split_bt_sensor_triggered(uint8_t sensor_number,
const struct zmk_sensor_channel_data channel_data[],
size_t channel_data_size);

View file

@ -16,3 +16,4 @@
#define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000) #define ZMK_SPLIT_BT_SERVICE_UUID ZMK_BT_SPLIT_UUID(0x00000000)
#define ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000001) #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_RUN_BEHAVIOR_UUID ZMK_BT_SPLIT_UUID(0x00000002)
#define ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID ZMK_BT_SPLIT_UUID(0x00000003)

View file

@ -28,7 +28,7 @@ int zmk_behavior_sensor_rotate_common_accept_data(
if (value.val1 == 0) { if (value.val1 == 0) {
triggers = value.val2; triggers = value.val2;
} else { } else {
struct sensor_value remainder = data->remainder[sensor_index]; struct sensor_value remainder = data->remainder[sensor_index][event.layer];
remainder.val1 += value.val1; remainder.val1 += value.val1;
remainder.val2 += value.val2; remainder.val2 += value.val2;
@ -42,15 +42,16 @@ int zmk_behavior_sensor_rotate_common_accept_data(
triggers = remainder.val1 / trigger_degrees; triggers = remainder.val1 / trigger_degrees;
remainder.val1 %= trigger_degrees; remainder.val1 %= trigger_degrees;
data->remainder[sensor_index] = remainder; data->remainder[sensor_index][event.layer] = remainder;
} }
LOG_DBG( LOG_DBG(
"val1: %d, val2: %d, remainder: %d/%d triggers: %d inc keycode 0x%02X dec keycode 0x%02X", "val1: %d, val2: %d, remainder: %d/%d triggers: %d inc keycode 0x%02X dec keycode 0x%02X",
value.val1, value.val2, data->remainder[sensor_index].val1, value.val1, value.val2, data->remainder[sensor_index][event.layer].val1,
data->remainder[sensor_index].val2, triggers, binding->param1, binding->param2); data->remainder[sensor_index][event.layer].val2, triggers, binding->param1,
binding->param2);
data->triggers[sensor_index] = triggers; data->triggers[sensor_index][event.layer] = triggers;
return 0; return 0;
} }
@ -64,11 +65,11 @@ int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *bindi
const int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position); const int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) { if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) {
data->triggers[sensor_index] = 0; data->triggers[sensor_index][event.layer] = 0;
return ZMK_BEHAVIOR_TRANSPARENT; return ZMK_BEHAVIOR_TRANSPARENT;
} }
int triggers = data->triggers[sensor_index]; int triggers = data->triggers[sensor_index][event.layer];
struct zmk_behavior_binding triggered_binding; struct zmk_behavior_binding triggered_binding;
if (triggers > 0) { if (triggers > 0) {

View file

@ -6,6 +6,7 @@
#include <drivers/behavior.h> #include <drivers/behavior.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/keymap.h>
#include <zmk/sensors.h> #include <zmk/sensors.h>
struct behavior_sensor_rotate_config { struct behavior_sensor_rotate_config {
@ -16,8 +17,8 @@ struct behavior_sensor_rotate_config {
}; };
struct behavior_sensor_rotate_data { struct behavior_sensor_rotate_data {
struct sensor_value remainder[ZMK_KEYMAP_SENSORS_LEN]; struct sensor_value remainder[ZMK_KEYMAP_SENSORS_LEN][ZMK_KEYMAP_LAYERS_LEN];
int triggers[ZMK_KEYMAP_SENSORS_LEN]; int triggers[ZMK_KEYMAP_SENSORS_LEN][ZMK_KEYMAP_LAYERS_LEN];
}; };
int zmk_behavior_sensor_rotate_common_accept_data( int zmk_behavior_sensor_rotate_common_accept_data(

View file

@ -31,10 +31,6 @@ static uint8_t _zmk_keymap_layer_default = 0;
#define DT_DRV_COMPAT zmk_keymap #define DT_DRV_COMPAT zmk_keymap
#define LAYER_CHILD_LEN(node) 1 +
#define ZMK_KEYMAP_NODE DT_DRV_INST(0)
#define ZMK_KEYMAP_LAYERS_LEN (DT_INST_FOREACH_CHILD(0, LAYER_CHILD_LEN) 0)
#define BINDING_WITH_COMMA(idx, drv_inst) ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) #define BINDING_WITH_COMMA(idx, drv_inst) ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst)
#define TRANSFORMED_LAYER(node) \ #define TRANSFORMED_LAYER(node) \

View file

@ -29,7 +29,7 @@ struct sensors_item_cfg {
{ \ { \
.dev = DEVICE_DT_GET_OR_NULL(node), \ .dev = DEVICE_DT_GET_OR_NULL(node), \
.trigger = {.type = SENSOR_TRIG_DATA_READY, .chan = SENSOR_CHAN_ROTATION}, \ .trigger = {.type = SENSOR_TRIG_DATA_READY, .chan = SENSOR_CHAN_ROTATION}, \
.config = &configs[idx] \ .config = &configs[idx], .sensor_index = idx \
} }
#define SENSOR_ITEM(idx, _i) _SENSOR_ITEM(idx, ZMK_KEYMAP_SENSORS_BY_IDX(idx)) #define SENSOR_ITEM(idx, _i) _SENSOR_ITEM(idx, ZMK_KEYMAP_SENSORS_BY_IDX(idx))
@ -112,7 +112,7 @@ static void zmk_sensors_trigger_handler(const struct device *dev,
int sensor_index = test_item - sensors; int sensor_index = test_item - sensors;
if (sensor_index < 0 || sensor_index >= ARRAY_SIZE(sensors)) { if (sensor_index < 0 || sensor_index >= ARRAY_SIZE(sensors)) {
LOG_ERR("Invalid sensor item triggered our callback"); LOG_ERR("Invalid sensor item triggered our callback (%d)", sensor_index);
return; return;
} }
@ -127,8 +127,6 @@ static void zmk_sensors_trigger_handler(const struct device *dev,
static void zmk_sensors_init_item(uint8_t i) { static void zmk_sensors_init_item(uint8_t i) {
LOG_DBG("Init sensor at index %d", i); LOG_DBG("Init sensor at index %d", i);
sensors[i].sensor_index = i;
if (!sensors[i].dev) { if (!sensors[i].dev) {
LOG_DBG("No local device for %d", i); LOG_DBG("No local device for %d", i);
return; return;

View file

@ -21,10 +21,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/stdlib.h> #include <zmk/stdlib.h>
#include <zmk/ble.h> #include <zmk/ble.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/sensors.h>
#include <zmk/split/bluetooth/uuid.h> #include <zmk/split/bluetooth/uuid.h>
#include <zmk/split/bluetooth/service.h> #include <zmk/split/bluetooth/service.h>
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/sensor_event.h>
static int start_scanning(void); static int start_scanning(void);
@ -41,6 +43,7 @@ struct peripheral_slot {
struct bt_conn *conn; struct bt_conn *conn;
struct bt_gatt_discover_params discover_params; struct bt_gatt_discover_params discover_params;
struct bt_gatt_subscribe_params subscribe_params; struct bt_gatt_subscribe_params subscribe_params;
struct bt_gatt_subscribe_params sensor_subscribe_params;
struct bt_gatt_discover_params sub_discover_params; struct bt_gatt_discover_params sub_discover_params;
uint16_t run_behavior_handle; uint16_t run_behavior_handle;
uint8_t position_state[POSITION_STATE_DATA_LEN]; uint8_t position_state[POSITION_STATE_DATA_LEN];
@ -165,6 +168,46 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) {
return 0; return 0;
} }
#if ZMK_KEYMAP_HAS_SENSORS
K_MSGQ_DEFINE(peripheral_sensor_event_msgq, sizeof(struct zmk_sensor_event),
CONFIG_ZMK_SPLIT_BLE_CENTRAL_POSITION_QUEUE_SIZE, 4);
void peripheral_sensor_event_work_callback(struct k_work *work) {
struct zmk_sensor_event ev;
while (k_msgq_get(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT) == 0) {
LOG_DBG("Trigger sensor change for %d", ev.sensor_index);
ZMK_EVENT_RAISE(new_zmk_sensor_event(ev));
}
}
K_WORK_DEFINE(peripheral_sensor_event_work, peripheral_sensor_event_work_callback);
static uint8_t split_central_sensor_notify_func(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length) {
if (!data) {
LOG_DBG("[UNSUBSCRIBED]");
params->value_handle = 0U;
return BT_GATT_ITER_STOP;
}
LOG_DBG("[SENSOR NOTIFICATION] data %p length %u", data, length);
struct sensor_event sensor_event;
memcpy(&sensor_event, data, MIN(length, sizeof(sensor_event)));
struct zmk_sensor_event ev = {.sensor_index = sensor_event.sensor_index,
.channel_data_size = sensor_event.channel_data_size,
.timestamp = k_uptime_get()};
memcpy(&ev.channel_data, sensor_event.channel_data,
sizeof(struct zmk_sensor_channel_data) * sensor_event.channel_data_size);
k_msgq_put(&peripheral_sensor_event_msgq, &ev, K_NO_WAIT);
k_work_submit(&peripheral_sensor_event_work);
return BT_GATT_ITER_CONTINUE;
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
static uint8_t split_central_notify_func(struct bt_conn *conn, static uint8_t split_central_notify_func(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params, const void *data, struct bt_gatt_subscribe_params *params, const void *data,
uint16_t length) { uint16_t length) {
@ -209,14 +252,8 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE; return BT_GATT_ITER_CONTINUE;
} }
static void split_central_subscribe(struct bt_conn *conn) { static int split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) {
struct peripheral_slot *slot = peripheral_slot_for_conn(conn); int err = bt_gatt_subscribe(conn, params);
if (slot == NULL) {
LOG_ERR("No peripheral state found for connection");
return;
}
int err = bt_gatt_subscribe(conn, &slot->subscribe_params);
switch (err) { switch (err) {
case -EALREADY: case -EALREADY:
LOG_DBG("[ALREADY SUBSCRIBED]"); LOG_DBG("[ALREADY SUBSCRIBED]");
@ -228,6 +265,8 @@ static void split_central_subscribe(struct bt_conn *conn) {
LOG_ERR("Subscribe failed (err %d)", err); LOG_ERR("Subscribe failed (err %d)", err);
break; break;
} }
return err;
} }
static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn, static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
@ -263,14 +302,34 @@ static uint8_t split_central_chrc_discovery_func(struct bt_conn *conn,
slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); slot->subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
slot->subscribe_params.notify = split_central_notify_func; slot->subscribe_params.notify = split_central_notify_func;
slot->subscribe_params.value = BT_GATT_CCC_NOTIFY; slot->subscribe_params.value = BT_GATT_CCC_NOTIFY;
split_central_subscribe(conn); split_central_subscribe(conn, &slot->subscribe_params);
#if ZMK_KEYMAP_HAS_SENSORS
} else if (bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID)) == 0) {
slot->discover_params.uuid = NULL;
slot->discover_params.start_handle = attr->handle + 2;
slot->discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
slot->sensor_subscribe_params.disc_params = &slot->sub_discover_params;
slot->sensor_subscribe_params.end_handle = slot->discover_params.end_handle;
slot->sensor_subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
slot->sensor_subscribe_params.notify = split_central_sensor_notify_func;
slot->sensor_subscribe_params.value = BT_GATT_CCC_NOTIFY;
split_central_subscribe(conn, &slot->sensor_subscribe_params);
#endif /* ZMK_KEYMAP_HAS_SENSORS */
} else if (bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid, } else if (bt_uuid_cmp(((struct bt_gatt_chrc *)attr->user_data)->uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID)) == 0) { BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID)) == 0) {
LOG_DBG("Found run behavior handle"); LOG_DBG("Found run behavior handle");
slot->discover_params.uuid = NULL;
slot->discover_params.start_handle = attr->handle + 2;
slot->run_behavior_handle = bt_gatt_attr_value_handle(attr); slot->run_behavior_handle = bt_gatt_attr_value_handle(attr);
} }
bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle); bool subscribed = (slot->run_behavior_handle && slot->subscribe_params.value_handle
#if ZMK_KEYMAP_HAS_SENSORS
&& slot->sensor_subscribe_params.value_handle
#endif /* ZMK_KEYMAP_HAS_SENSORS */
);
return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE; return subscribed ? BT_GATT_ITER_STOP : BT_GATT_ITER_CONTINUE;
} }

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <zephyr/drivers/sensor.h>
#include <zephyr/types.h> #include <zephyr/types.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/init.h> #include <zephyr/init.h>
@ -20,6 +21,22 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/matrix.h> #include <zmk/matrix.h>
#include <zmk/split/bluetooth/uuid.h> #include <zmk/split/bluetooth/uuid.h>
#include <zmk/split/bluetooth/service.h> #include <zmk/split/bluetooth/service.h>
#include <zmk/events/sensor_event.h>
#include <zmk/sensors.h>
#if ZMK_KEYMAP_HAS_SENSORS
static struct sensor_event last_sensor_event;
static ssize_t split_svc_sensor_state(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, &last_sensor_event,
sizeof(last_sensor_event));
}
static void split_svc_sensor_state_ccc(const struct bt_gatt_attr *attr, uint16_t value) {
LOG_DBG("value %d", value);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
#define POS_STATE_LEN 16 #define POS_STATE_LEN 16
@ -98,7 +115,14 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
split_svc_run_behavior, &behavior_run_payload), split_svc_run_behavior, &behavior_run_payload),
BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL, BT_GATT_DESCRIPTOR(BT_UUID_NUM_OF_DIGITALS, BT_GATT_PERM_READ, split_svc_num_of_positions, NULL,
&num_of_positions), ); &num_of_positions),
#if ZMK_KEYMAP_HAS_SENSORS
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_SENSOR_STATE_UUID),
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT,
split_svc_sensor_state, NULL, &last_sensor_event),
BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
#endif /* ZMK_KEYMAP_HAS_SENSORS */
);
K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE); K_THREAD_STACK_DEFINE(service_q_stack, CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE);
@ -151,6 +175,58 @@ int zmk_split_bt_position_released(uint8_t position) {
return send_position_state(); return send_position_state();
} }
#if ZMK_KEYMAP_HAS_SENSORS
K_MSGQ_DEFINE(sensor_state_msgq, sizeof(struct sensor_event),
CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_POSITION_QUEUE_SIZE, 4);
void send_sensor_state_callback(struct k_work *work) {
while (k_msgq_get(&sensor_state_msgq, &last_sensor_event, K_NO_WAIT) == 0) {
int err = bt_gatt_notify(NULL, &split_svc.attrs[8], &last_sensor_event,
sizeof(last_sensor_event));
if (err) {
LOG_DBG("Error notifying %d", err);
}
}
};
K_WORK_DEFINE(service_sensor_notify_work, send_sensor_state_callback);
int send_sensor_state(struct sensor_event ev) {
int err = k_msgq_put(&sensor_state_msgq, &ev, K_MSEC(100));
if (err) {
// retry...
switch (err) {
case -EAGAIN: {
LOG_WRN("Sensor state message queue full, popping first message and queueing again");
struct sensor_event discarded_state;
k_msgq_get(&sensor_state_msgq, &discarded_state, K_NO_WAIT);
return send_sensor_state(ev);
}
default:
LOG_WRN("Failed to queue sensor state to send (%d)", err);
return err;
}
}
k_work_submit_to_queue(&service_work_q, &service_sensor_notify_work);
return 0;
}
int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
const struct zmk_sensor_channel_data channel_data[],
size_t channel_data_size) {
if (channel_data_size > ZMK_SENSOR_EVENT_MAX_CHANNELS) {
return -EINVAL;
}
struct sensor_event ev =
(struct sensor_event){.sensor_index = sensor_index, .channel_data_size = channel_data_size};
memcpy(ev.channel_data, channel_data,
channel_data_size * sizeof(struct zmk_sensor_channel_data));
return send_sensor_state(ev);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
int service_init(const struct device *_arg) { int service_init(const struct device *_arg) {
static const struct k_work_queue_config queue_config = { static const struct k_work_queue_config queue_config = {
.name = "Split Peripheral Notification Queue"}; .name = "Split Peripheral Notification Queue"};

View file

@ -13,21 +13,35 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/sensor_event.h>
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/sensors.h>
#include <zmk/endpoints.h> #include <zmk/endpoints.h>
int split_listener(const zmk_event_t *eh) { int split_listener(const zmk_event_t *eh) {
LOG_DBG(""); LOG_DBG("");
const struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh); const struct zmk_position_state_changed *pos_ev;
if (ev != NULL) { if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) {
if (ev->state) { if (pos_ev->state) {
return zmk_split_bt_position_pressed(ev->position); return zmk_split_bt_position_pressed(pos_ev->position);
} else { } else {
return zmk_split_bt_position_released(ev->position); return zmk_split_bt_position_released(pos_ev->position);
} }
} }
#if ZMK_KEYMAP_HAS_SENSORS
const struct zmk_sensor_event *sensor_ev;
if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) {
return zmk_split_bt_sensor_triggered(sensor_ev->sensor_index, sensor_ev->channel_data,
sensor_ev->channel_data_size);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
return ZMK_EV_EVENT_BUBBLE; return ZMK_EV_EVENT_BUBBLE;
} }
ZMK_LISTENER(split_listener, split_listener); ZMK_LISTENER(split_listener, split_listener);
ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed); ZMK_SUBSCRIPTION(split_listener, zmk_position_state_changed);
#if ZMK_KEYMAP_HAS_SENSORS
ZMK_SUBSCRIPTION(split_listener, zmk_sensor_event);
#endif /* ZMK_KEYMAP_HAS_SENSORS */

View file

@ -5,10 +5,6 @@ sidebar_label: Encoders
Existing support for encoders in ZMK is focused around the five pin EC11 rotary encoder with push button design used in the majority of current keyboard and macropad designs. Existing support for encoders in ZMK is focused around the five pin EC11 rotary encoder with push button design used in the majority of current keyboard and macropad designs.
:::note
Encoders are currently only support on the left/central sides of splits. For progress on this, see [#728](https://github.com/zmkfirmware/zmk/pull/728).
:::
## Enabling EC11 Encoders ## Enabling EC11 Encoders
To enable encoders for boards that have existing encoder support, uncomment the `CONFIG_EC11=y` and `CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y` lines in your board's .conf file in your `zmk-config/config` folder. Save and push your changes, then download and flash the new firmware. To enable encoders for boards that have existing encoder support, uncomment the `CONFIG_EC11=y` and `CONFIG_EC11_TRIGGER_GLOBAL_THREAD=y` lines in your board's .conf file in your `zmk-config/config` folder. Save and push your changes, then download and flash the new firmware.

View file

@ -23,11 +23,11 @@ ZMK is currently missing some features found in other popular firmware. This tab
| Split Keyboard Support | ✅ | ✅ | ✅ | | Split Keyboard Support | ✅ | ✅ | ✅ |
| [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ | | [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ |
| [Hold-Tap](behaviors/hold-tap.md) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md/#layer-tap)) | ✅ | ✅ | ✅ | | [Hold-Tap](behaviors/hold-tap.md) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md/#layer-tap)) | ✅ | ✅ | ✅ |
| [Tap-Dance](behaviors/tap-dance.md) | ✅ | ✅[^3] | ✅ | | [Tap-Dance](behaviors/tap-dance.md) | ✅ | ✅[^2] | ✅ |
| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ | | [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ |
| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ | | [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ |
| [Encoders](features/encoders.md)[^1] | ✅ | ✅ | ✅ | | [Encoders](features/encoders.md) | ✅ | ✅ | ✅ |
| [Display Support](features/displays.md)[^2] | 🚧 | 🚧 | ✅ | | [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ |
| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ | | [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ |
| [Backlight](features/backlight.md) | ✅ | ✅ | ✅ | | [Backlight](features/backlight.md) | ✅ | ✅ | ✅ |
| One Shot Keys | ✅ | ✅ | ✅ | | One Shot Keys | ✅ | ✅ | ✅ |
@ -43,8 +43,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
| AVR/8 Bit | | | ✅ | | AVR/8 Bit | | | ✅ |
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | | | [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | |
[^3]: Tap-Dances are limited to single and double-tap on BlueMicro [^2]: Tap-Dances are limited to single and double-tap on BlueMicro
[^2]: Encoders are not currently supported on peripheral side splits.
[^1]: OLEDs are currently proof of concept in ZMK. [^1]: OLEDs are currently proof of concept in ZMK.
## Code Of Conduct ## Code Of Conduct