diff --git a/app/module/drivers/sensor/CMakeLists.txt b/app/module/drivers/sensor/CMakeLists.txt index cd1a1c450..370fe15a6 100644 --- a/app/module/drivers/sensor/CMakeLists.txt +++ b/app/module/drivers/sensor/CMakeLists.txt @@ -4,3 +4,5 @@ add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_EC11 ec11) add_subdirectory_ifdef(CONFIG_ZMK_MAX17048 max17048) + +add_subdirectory_ifdef(CONFIG_ZMK_SENSOR_ENCODER_MOCK encoder_mock) diff --git a/app/module/drivers/sensor/Kconfig b/app/module/drivers/sensor/Kconfig index ad570c58c..c5f4a721f 100644 --- a/app/module/drivers/sensor/Kconfig +++ b/app/module/drivers/sensor/Kconfig @@ -7,4 +7,6 @@ rsource "battery/Kconfig" rsource "ec11/Kconfig" rsource "max17048/Kconfig" +rsource "encoder_mock/Kconfig" + endif # SENSOR diff --git a/app/module/drivers/sensor/encoder_mock/CMakeLists.txt b/app/module/drivers/sensor/encoder_mock/CMakeLists.txt new file mode 100644 index 000000000..8271067ea --- /dev/null +++ b/app/module/drivers/sensor/encoder_mock/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT + +zephyr_include_directories(.) + +zephyr_library() + +zephyr_library_sources(encoder_mock.c) \ No newline at end of file diff --git a/app/module/drivers/sensor/encoder_mock/Kconfig b/app/module/drivers/sensor/encoder_mock/Kconfig new file mode 100644 index 000000000..9b9dd00ad --- /dev/null +++ b/app/module/drivers/sensor/encoder_mock/Kconfig @@ -0,0 +1,7 @@ +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT + +config ZMK_SENSOR_ENCODER_MOCK + bool "Mock Encoder Sensor" + default y + depends on DT_HAS_ZMK_SENSOR_ENCODER_MOCK_ENABLED diff --git a/app/module/drivers/sensor/encoder_mock/encoder_mock.c b/app/module/drivers/sensor/encoder_mock/encoder_mock.c new file mode 100644 index 000000000..283ee9161 --- /dev/null +++ b/app/module/drivers/sensor/encoder_mock/encoder_mock.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_sensor_encoder_mock + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +struct enc_mock_config { + uint16_t startup_delay; + uint16_t event_period; + bool exit_after; + const int16_t *events; + size_t events_len; +}; + +struct enc_mock_data { + const struct sensor_trigger *trigger; + sensor_trigger_handler_t handler; + + size_t event_index; + struct k_work_delayable work; + const struct device *dev; +}; + +static void enc_mock_work_cb(struct k_work *work) { + struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work); + struct enc_mock_data *data = CONTAINER_OF(dwork, struct enc_mock_data, work); + + const struct device *dev = data->dev; + + data->handler(dev, data->trigger); +} + +static int enc_mock_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) { + struct enc_mock_data *drv_data = dev->data; + const struct enc_mock_config *drv_cfg = dev->config; + + drv_data->trigger = trig; + drv_data->handler = handler; + + int ret = k_work_schedule(&drv_data->work, K_MSEC(drv_cfg->startup_delay)); + if (ret < 0) { + LOG_WRN("Failed to schedule next mock sensor event %d", ret); + return ret; + } + + return 0; +} + +static int enc_mock_sample_fetch(const struct device *dev, enum sensor_channel chan) { + struct enc_mock_data *drv_data = dev->data; + const struct enc_mock_config *drv_cfg = dev->config; + + drv_data->event_index++; + + if (drv_data->event_index < drv_cfg->events_len - 1) { + k_work_schedule(&drv_data->work, K_MSEC(drv_cfg->event_period)); + } else if (drv_cfg->exit_after) { + exit(0); + } + return 0; +} + +static int enc_mock_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) { + struct enc_mock_data *drv_data = dev->data; + const struct enc_mock_config *drv_cfg = dev->config; + + val->val1 = drv_cfg->events[drv_data->event_index]; + + return 0; +} + +static const struct sensor_driver_api enc_mock_driver_api = { + .trigger_set = enc_mock_trigger_set, + .sample_fetch = enc_mock_sample_fetch, + .channel_get = enc_mock_channel_get, +}; + +int enc_mock_init(const struct device *dev) { + struct enc_mock_data *drv_data = dev->data; + + drv_data->dev = dev; + drv_data->event_index = -1; + + k_work_init_delayable(&drv_data->work, enc_mock_work_cb); + + return 0; +} + +#define ENC_MOCK_INST(n) \ + struct enc_mock_data enc_mock_data_##n = {}; \ + const int16_t mock_data_##n[] = DT_INST_PROP(n, events); \ + const struct enc_mock_config enc_mock_cfg_##n = { \ + .events = mock_data_##n, \ + .events_len = DT_INST_PROP_LEN(n, events), \ + .startup_delay = DT_INST_PROP(n, event_startup_delay), \ + .event_period = DT_INST_PROP(n, event_period), \ + .exit_after = DT_INST_PROP(n, exit_after), \ + }; \ + DEVICE_DT_INST_DEFINE(n, enc_mock_init, NULL, &enc_mock_data_##n, &enc_mock_cfg_##n, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &enc_mock_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(ENC_MOCK_INST) diff --git a/app/module/dts/bindings/sensor/zmk,sensor-encoder-mock.yaml b/app/module/dts/bindings/sensor/zmk,sensor-encoder-mock.yaml new file mode 100644 index 000000000..74ff1f5bd --- /dev/null +++ b/app/module/dts/bindings/sensor/zmk,sensor-encoder-mock.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2024 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: | + Allows defining a mock sensor driver that simulates periodic encoder rotation events. + +compatible: "zmk,sensor-encoder-mock" + +properties: + event-startup-delay: + type: int + default: 0 + description: Milliseconds to delay before starting generating the events + event-period: + type: int + description: Milliseconds between each generated event + events: + type: array + description: List of angle events to generate + exit-after: + type: boolean diff --git a/app/tests/ble/split/peripheral-encoder/events.patterns b/app/tests/ble/split/peripheral-encoder/events.patterns new file mode 100644 index 000000000..f8cf363c2 --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/events.patterns @@ -0,0 +1 @@ +s/^d_02: @[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9][0-9][0-9][0-9] .{19}/profile 0 /p diff --git a/app/tests/ble/split/peripheral-encoder/nrf52_bsim.conf b/app/tests/ble/split/peripheral-encoder/nrf52_bsim.conf new file mode 100644 index 000000000..ba7e6a1cc --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/nrf52_bsim.conf @@ -0,0 +1,2 @@ +CONFIG_ZMK_SPLIT=y +CONFIG_SENSOR=y \ No newline at end of file diff --git a/app/tests/ble/split/peripheral-encoder/nrf52_bsim.keymap b/app/tests/ble/split/peripheral-encoder/nrf52_bsim.keymap new file mode 100644 index 000000000..635b2a259 --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/nrf52_bsim.keymap @@ -0,0 +1,23 @@ +#include +#include +#include + +#include "shared.dtsi" + +&kscan { + /delete-property/ exit-after; + events = <>; +}; +/ { + keymap { + compatible = "zmk,keymap"; + + default_layer { + bindings = < + &kp A &kp B + &bt BT_SEL 0 &bt BT_CLR>; + + sensor-bindings = <&inc_dec_kp A B>; + }; + }; +}; diff --git a/app/tests/ble/split/peripheral-encoder/peripheral.overlay b/app/tests/ble/split/peripheral-encoder/peripheral.overlay new file mode 100644 index 000000000..145c30e20 --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/peripheral.overlay @@ -0,0 +1,18 @@ + +#include + +#include "shared.dtsi" + +&kscan { + events = <>; + + /delete-property/ exit-after; +}; + +&mock_encoder { + status = "okay"; + + event-startup-delay = <2000>; + event-period = <2000>; + events = <18 (-18)>; +}; diff --git a/app/tests/ble/split/peripheral-encoder/shared.dtsi b/app/tests/ble/split/peripheral-encoder/shared.dtsi new file mode 100644 index 000000000..7ae4eded4 --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/shared.dtsi @@ -0,0 +1,12 @@ +/ { + mock_encoder: mock_encoder { + compatible = "zmk,sensor-encoder-mock"; + status = "disabled"; + }; + + sensors: sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&mock_encoder>; + triggers-per-rotation = <20>; + }; +}; \ No newline at end of file diff --git a/app/tests/ble/split/peripheral-encoder/siblings.txt b/app/tests/ble/split/peripheral-encoder/siblings.txt new file mode 100644 index 000000000..52dfbb788 --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/siblings.txt @@ -0,0 +1,2 @@ +./ble_test_central.exe -d=2 +./tests_ble_split_peripheral-encoder_peripheral.exe -d=3 diff --git a/app/tests/ble/split/peripheral-encoder/snapshot.log b/app/tests/ble/split/peripheral-encoder/snapshot.log new file mode 100644 index 000000000..c3804f64a --- /dev/null +++ b/app/tests/ble/split/peripheral-encoder/snapshot.log @@ -0,0 +1,23 @@ +profile 0 bt_id: No static addresses stored in controller +profile 0 ble_central: main: [Bluetooth initialized] +profile 0 ble_central: start_scan: [Scanning successfully started] +profile 0 ble_central: device_found: [DEVICE]: FD:9E:B2:48:47:39 (random), AD evt type 0, AD data len 15, RSSI -56 +profile 0 ble_central: eir_found: [AD]: 25 data_len 2 +profile 0 ble_central: eir_found: [AD]: 1 data_len 1 +profile 0 ble_central: eir_found: [AD]: 2 data_len 4 +profile 0 ble_central: connected: [Connected]: FD:9E:B2:48:47:39 (random) +profile 0 ble_central: connected: [Setting the security for the connection] +profile 0 ble_central: pairing_complete: Pairing complete +profile 0 ble_central: discover_conn: [Discovery started for conn] +profile 0 ble_central: discover_func: [ATTRIBUTE] handle 23 +profile 0 ble_central: discover_func: [ATTRIBUTE] handle 28 +profile 0 ble_central: discover_func: [ATTRIBUTE] handle 30 +profile 0 ble_central: discover_func: [SUBSCRIBED] +profile 0 ble_central: notify_func: payload +profile 0 00 00 04 00 00 00 00 00 |........ +profile 0 ble_central: notify_func: payload +profile 0 00 00 00 00 00 00 00 00 |........ +profile 0 ble_central: notify_func: payload +profile 0 00 00 05 00 00 00 00 00 |........ +profile 0 ble_central: notify_func: payload +profile 0 00 00 00 00 00 00 00 00 |........