From 41d2c5cfd48ffedcbaa3200da827e556ccc4330d Mon Sep 17 00:00:00 2001 From: Nick Conway Date: Mon, 23 May 2022 16:33:08 -0400 Subject: [PATCH] Add configurable sensor bindings --- app/CMakeLists.txt | 2 + app/Kconfig | 12 +++ .../behaviors/sensor_rotate_key_press.dtsi | 3 +- .../zmk,behavior-sensor-rotate-var.yaml | 25 ++++++ .../behaviors/zmk,behavior-sensor-rotate.yaml | 21 +++++ app/src/behaviors/behavior_sensor_rotate.c | 86 +++++++++++++++++++ .../behaviors/behavior_sensor_rotate_var.c | 79 +++++++++++++++++ docs/docs/behaviors/sensor-rotate.md | 33 +++++++ docs/docs/features/encoders.md | 8 +- docs/sidebars.js | 1 + 10 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-var.yaml create mode 100644 app/dts/bindings/behaviors/zmk,behavior-sensor-rotate.yaml create mode 100644 app/src/behaviors/behavior_sensor_rotate.c create mode 100644 app/src/behaviors/behavior_sensor_rotate_var.c create mode 100644 docs/docs/behaviors/sensor-rotate.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4b61fc72..6e8cf4e7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -52,6 +52,8 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_to_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c) + target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behavior_queue.c) diff --git a/app/Kconfig b/app/Kconfig index 9902046d..001ca619 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -477,6 +477,18 @@ choice CBPRINTF_IMPLEMENTATION endchoice +DT_COMPAT_ZMK_BEHAVIOR_SENSOR_ROTATE := zmk,behavior-sensor-rotate + +config ZMK_BEHAVIOR_SENSOR_ROTATE + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_SENSOR_ROTATE)) + +DT_COMPAT_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR := zmk,behavior-sensor-rotate-var + +config ZMK_BEHAVIOR_SENSOR_ROTATE_VAR + bool + default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR)) + module = ZMK module-str = zmk source "subsys/logging/Kconfig.template.log_config" diff --git a/app/dts/behaviors/sensor_rotate_key_press.dtsi b/app/dts/behaviors/sensor_rotate_key_press.dtsi index d3f084b0..bfb0f190 100644 --- a/app/dts/behaviors/sensor_rotate_key_press.dtsi +++ b/app/dts/behaviors/sensor_rotate_key_press.dtsi @@ -8,9 +8,10 @@ behaviors { /* DEPRECATED: `inc_dec_cp` will be removed in the future */ /omit-if-no-ref/ inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press { - compatible = "zmk,behavior-sensor-rotate-key-press"; + compatible = "zmk,behavior-sensor-rotate-var"; label = "ENC_KEY_PRESS"; #sensor-binding-cells = <2>; + bindings = <&kp>, <&kp>; }; }; }; diff --git a/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-var.yaml b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-var.yaml new file mode 100644 index 00000000..0da3b4db --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate-var.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Sensor rotate behavior + +compatible: "zmk,behavior-sensor-rotate-var" + +properties: + label: + type: string + required: true + "#sensor-binding-cells": + type: int + required: true + const: 2 + bindings: + type: phandles + required: true + tap-ms: + type: int + default: 5 + +sensor-binding-cells: + - param1 + - param2 diff --git a/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate.yaml b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate.yaml new file mode 100644 index 00000000..d20777b8 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-sensor-rotate.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Sensor rotate behavior + +compatible: "zmk,behavior-sensor-rotate" + +properties: + label: + type: string + required: true + "#sensor-binding-cells": + type: int + required: true + const: 0 + bindings: + type: phandle-array + required: true + tap-ms: + type: int + default: 5 diff --git a/app/src/behaviors/behavior_sensor_rotate.c b/app/src/behaviors/behavior_sensor_rotate.c new file mode 100644 index 00000000..9f78d087 --- /dev/null +++ b/app/src/behaviors/behavior_sensor_rotate.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_sensor_rotate + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct behavior_sensor_rotate_config { + struct zmk_behavior_binding cw_binding; + struct zmk_behavior_binding ccw_binding; + int tap_ms; +}; + +static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, + const struct device *sensor, int64_t timestamp) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_sensor_rotate_config *cfg = dev->config; + + struct sensor_value value; + int err; + + err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value); + + if (err) { + LOG_WRN("Failed to get sensor rotation value: %d", err); + return err; + } + + struct zmk_behavior_binding *triggered_binding; + switch (value.val1) { + case 1: + triggered_binding = (struct zmk_behavior_binding *)&cfg->cw_binding; + break; + case -1: + triggered_binding = (struct zmk_behavior_binding *)&cfg->ccw_binding; + break; + default: + return -ENOTSUP; + } + + LOG_DBG("Sensor binding: %s", log_strdup(binding->behavior_dev)); + + zmk_behavior_queue_add(0, *triggered_binding, true, cfg->tap_ms); + zmk_behavior_queue_add(0, *triggered_binding, false, 0); + + return ZMK_BEHAVIOR_OPAQUE; +} + +static const struct behavior_driver_api behavior_sensor_rotate_driver_api = { + .sensor_binding_triggered = on_sensor_binding_triggered}; + +static int behavior_sensor_rotate_init(const struct device *dev) { return 0; }; + +#define _TRANSFORM_ENTRY(idx, node) \ + { \ + .behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \ + .param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \ + (DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \ + .param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \ + (DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \ + } + +#define SENSOR_ROTATE_INST(n) \ + static struct behavior_sensor_rotate_config behavior_sensor_rotate_config_##n = { \ + .cw_binding = _TRANSFORM_ENTRY(0, n), \ + .ccw_binding = _TRANSFORM_ENTRY(1, n), \ + .tap_ms = DT_INST_PROP_OR(n, tap_ms, 5), \ + }; \ + DEVICE_DT_INST_DEFINE( \ + n, behavior_sensor_rotate_init, NULL, NULL, &behavior_sensor_rotate_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST) diff --git a/app/src/behaviors/behavior_sensor_rotate_var.c b/app/src/behaviors/behavior_sensor_rotate_var.c new file mode 100644 index 00000000..4a1c7921 --- /dev/null +++ b/app/src/behaviors/behavior_sensor_rotate_var.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_sensor_rotate_var + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct behavior_sensor_rotate_var_config { + char *cw_behavior_dev; + char *ccw_behavior_dev; + int tap_ms; +}; + +static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding, + const struct device *sensor, int64_t timestamp) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_sensor_rotate_var_config *cfg = dev->config; + + struct sensor_value value; + int err; + + err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value); + + if (err) { + LOG_WRN("Failed to get sensor rotation value: %d", err); + return err; + } + + struct zmk_behavior_binding triggered_binding; + switch (value.val1) { + case 1: + triggered_binding.behavior_dev = cfg->cw_behavior_dev; + triggered_binding.param1 = binding->param1; + break; + case -1: + triggered_binding.behavior_dev = cfg->ccw_behavior_dev; + triggered_binding.param1 = binding->param2; + break; + default: + return -ENOTSUP; + } + + LOG_DBG("Sensor binding: %s", log_strdup(binding->behavior_dev)); + + zmk_behavior_queue_add(0, triggered_binding, true, cfg->tap_ms); + zmk_behavior_queue_add(0, triggered_binding, false, 0); + + return ZMK_BEHAVIOR_OPAQUE; +} + +static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api = { + .sensor_binding_triggered = on_sensor_binding_triggered}; + +static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0; }; + +#define SENSOR_ROTATE_VAR_INST(n) \ + static struct behavior_sensor_rotate_var_config behavior_sensor_rotate_var_config_##n = { \ + .cw_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \ + .ccw_behavior_dev = DT_LABEL(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \ + .tap_ms = DT_INST_PROP(n, tap_ms), \ + }; \ + DEVICE_DT_INST_DEFINE( \ + n, behavior_sensor_rotate_var_init, NULL, NULL, &behavior_sensor_rotate_var_config_##n, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_var_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_VAR_INST) diff --git a/docs/docs/behaviors/sensor-rotate.md b/docs/docs/behaviors/sensor-rotate.md new file mode 100644 index 00000000..c6b40f39 --- /dev/null +++ b/docs/docs/behaviors/sensor-rotate.md @@ -0,0 +1,33 @@ +--- +title: Sensor Rotation +sidebar_label: Sensor Rotation +--- + +## Summary + +The Sensor Rotation behavior triggers a different behavior, depending on whether the sensor is rotated clockwise or counter-clockwise. + +- If rotated counter-clockwise, the first behavior is triggered. +- If rotated clockwise, the second behavior is triggered. + +### Configuration + +An example implementation of an encoder that changes RGB brightness is shown below: + +``` +/ { + behaviors { + rgb_encoder: rgb_encoder { + compatible = "zmk,behavior-sensor-rotate"; + label = "RGB_ENCODER"; + #sensor-binding-cells = <0>; + bindings = <&rgb_ug RGB_BRD>, <&rgb_ug RGB_BRI>; + }; + }; + + keymap { + ... + sensor-bindings = <&rgb_encoder> + }; +}; +``` diff --git a/docs/docs/features/encoders.md b/docs/docs/features/encoders.md index 225ee6f3..29906c90 100644 --- a/docs/docs/features/encoders.md +++ b/docs/docs/features/encoders.md @@ -23,17 +23,17 @@ Keyboards and macropads with encoder support will typically take the two EC11 pi ### Rotation -Rotation is handled separately as a type of sensor. The behavior for this is set in `sensor-bindings`, which is defined in each keymap layer in the following format: +Rotation is handled separately as a type of sensor. The behavior for this is set in `sensor-bindings`. See [Sensor Rotation](../behaviors/sensor-rotate.md) for customizing this behavior. ``` -sensor-bindings = ; +sensor-bindings = ; ``` -- `BINDING`, for now, has only one behavior available; `&inc_dec_kp` for key presses (see [Key Press](../behaviors/key-press.md) for details on available keycodes). +- `BINDING` is either a user-defined behavior, or `&inc_dec_kp` for key presses (see [Key Press](../behaviors/key-press.md) for details on available keycodes). - `CW_KEY` is the keycode activated by a clockwise turn. - `CCW_KEY` is the keycode activated by a counter-clockwise turn. -Additional encoders can be configured by adding more `BINDING CW_KEY CCW_KEY` sets immediately after the first. +Additional encoders can be configured by adding more bindings immediately after the first. As an example, a complete `sensor-bindings` for a Kyria with two encoders could look like: diff --git a/docs/sidebars.js b/docs/sidebars.js index 7b445a29..444515d4 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -34,6 +34,7 @@ module.exports = { "behaviors/tap-dance", "behaviors/caps-word", "behaviors/key-repeat", + "behaviors/sensor-rotate", "behaviors/reset", "behaviors/bluetooth", "behaviors/outputs",