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>
#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;
uint8_t zmk_keymap_layer_default();

View file

@ -24,7 +24,9 @@ struct zmk_sensor_config {
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 {
enum sensor_channel channel;
struct sensor_value value;
};
enum sensor_channel channel;
} __packed;

View file

@ -6,8 +6,18 @@
#pragma once
#include <zmk/events/sensor_event.h>
#include <zmk/sensors.h>
#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 {
uint8_t position;
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_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_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)

View file

@ -28,7 +28,7 @@ int zmk_behavior_sensor_rotate_common_accept_data(
if (value.val1 == 0) {
triggers = value.val2;
} else {
struct sensor_value remainder = data->remainder[sensor_index];
struct sensor_value remainder = data->remainder[sensor_index][event.layer];
remainder.val1 += value.val1;
remainder.val2 += value.val2;
@ -42,15 +42,16 @@ int zmk_behavior_sensor_rotate_common_accept_data(
triggers = remainder.val1 / trigger_degrees;
remainder.val1 %= trigger_degrees;
data->remainder[sensor_index] = remainder;
data->remainder[sensor_index][event.layer] = remainder;
}
LOG_DBG(
"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,
data->remainder[sensor_index].val2, triggers, binding->param1, binding->param2);
value.val1, value.val2, data->remainder[sensor_index][event.layer].val1,
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;
}
@ -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);
if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) {
data->triggers[sensor_index] = 0;
data->triggers[sensor_index][event.layer] = 0;
return ZMK_BEHAVIOR_TRANSPARENT;
}
int triggers = data->triggers[sensor_index];
int triggers = data->triggers[sensor_index][event.layer];
struct zmk_behavior_binding triggered_binding;
if (triggers > 0) {

View file

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

View file

@ -29,7 +29,7 @@ struct sensors_item_cfg {
{ \
.dev = DEVICE_DT_GET_OR_NULL(node), \
.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))
@ -112,7 +112,7 @@ static void zmk_sensors_trigger_handler(const struct device *dev,
int sensor_index = test_item - 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;
}
@ -127,8 +127,6 @@ static void zmk_sensors_trigger_handler(const struct device *dev,
static void zmk_sensors_init_item(uint8_t i) {
LOG_DBG("Init sensor at index %d", i);
sensors[i].sensor_index = i;
if (!sensors[i].dev) {
LOG_DBG("No local device for %d", i);
return;

View file

@ -21,10 +21,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/stdlib.h>
#include <zmk/ble.h>
#include <zmk/behavior.h>
#include <zmk/sensors.h>
#include <zmk/split/bluetooth/uuid.h>
#include <zmk/split/bluetooth/service.h>
#include <zmk/event_manager.h>
#include <zmk/events/position_state_changed.h>
#include <zmk/events/sensor_event.h>
static int start_scanning(void);
@ -41,6 +43,7 @@ struct peripheral_slot {
struct bt_conn *conn;
struct bt_gatt_discover_params discover_params;
struct bt_gatt_subscribe_params subscribe_params;
struct bt_gatt_subscribe_params sensor_subscribe_params;
struct bt_gatt_discover_params sub_discover_params;
uint16_t run_behavior_handle;
uint8_t position_state[POSITION_STATE_DATA_LEN];
@ -165,6 +168,46 @@ int confirm_peripheral_slot_conn(struct bt_conn *conn) {
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,
struct bt_gatt_subscribe_params *params, const void *data,
uint16_t length) {
@ -209,14 +252,8 @@ static uint8_t split_central_notify_func(struct bt_conn *conn,
return BT_GATT_ITER_CONTINUE;
}
static void split_central_subscribe(struct bt_conn *conn) {
struct peripheral_slot *slot = peripheral_slot_for_conn(conn);
if (slot == NULL) {
LOG_ERR("No peripheral state found for connection");
return;
}
int err = bt_gatt_subscribe(conn, &slot->subscribe_params);
static int split_central_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *params) {
int err = bt_gatt_subscribe(conn, params);
switch (err) {
case -EALREADY:
LOG_DBG("[ALREADY SUBSCRIBED]");
@ -228,6 +265,8 @@ static void split_central_subscribe(struct bt_conn *conn) {
LOG_ERR("Subscribe failed (err %d)", err);
break;
}
return err;
}
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.notify = split_central_notify_func;
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,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_RUN_BEHAVIOR_UUID)) == 0) {
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);
}
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;
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT
*/
#include <zephyr/drivers/sensor.h>
#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/init.h>
@ -20,6 +21,22 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/matrix.h>
#include <zmk/split/bluetooth/uuid.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
@ -98,7 +115,14 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
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,
&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);
@ -151,6 +175,58 @@ int zmk_split_bt_position_released(uint8_t position) {
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) {
static const struct k_work_queue_config queue_config = {
.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/events/position_state_changed.h>
#include <zmk/events/sensor_event.h>
#include <zmk/hid.h>
#include <zmk/sensors.h>
#include <zmk/endpoints.h>
int split_listener(const zmk_event_t *eh) {
LOG_DBG("");
const struct zmk_position_state_changed *ev = as_zmk_position_state_changed(eh);
if (ev != NULL) {
if (ev->state) {
return zmk_split_bt_position_pressed(ev->position);
const struct zmk_position_state_changed *pos_ev;
if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) {
if (pos_ev->state) {
return zmk_split_bt_position_pressed(pos_ev->position);
} 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;
}
ZMK_LISTENER(split_listener, split_listener);
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.
:::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
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 | ✅ | ✅ | ✅ |
| [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)) | ✅ | ✅ | ✅ |
| [Tap-Dance](behaviors/tap-dance.md) | ✅ | ✅[^3] | ✅ |
| [Tap-Dance](behaviors/tap-dance.md) | ✅ | ✅[^2] | ✅ |
| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ |
| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ |
| [Encoders](features/encoders.md)[^1] | ✅ | ✅ | ✅ |
| [Display Support](features/displays.md)[^2] | 🚧 | 🚧 | ✅ |
| [Encoders](features/encoders.md) | ✅ | ✅ | ✅ |
| [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ |
| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ |
| [Backlight](features/backlight.md) | ✅ | ✅ | ✅ |
| One Shot Keys | ✅ | ✅ | ✅ |
@ -43,8 +43,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
| AVR/8 Bit | | | ✅ |
| [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]: Encoders are not currently supported on peripheral side splits.
[^2]: Tap-Dances are limited to single and double-tap on BlueMicro
[^1]: OLEDs are currently proof of concept in ZMK.
## Code Of Conduct