From ddb7a58bf27a66d979857f4b34020608e147e1ee Mon Sep 17 00:00:00 2001 From: XiNGRZ Date: Wed, 1 Feb 2023 16:51:01 +0800 Subject: [PATCH] knob_app: Manage per-layer Knob preferences --- config/app/usb_comm/handler/handler_knob.c | 92 ++++++++++++ config/app/usb_comm/handler/handler_version.c | 1 + config/boards/arm/hw75_dynamic/app/knob_app.c | 142 +++++++++++++++++- config/boards/arm/hw75_dynamic/app/knob_app.h | 11 ++ .../sensor/knob/include/knob/drivers/knob.h | 4 + .../sensor/knob/include/knob/drivers/motor.h | 2 + config/drivers/sensor/knob/knob.c | 17 +++ config/drivers/sensor/knob/motor.c | 7 + config/proto/usb_comm.proto | 15 ++ 9 files changed, 289 insertions(+), 2 deletions(-) diff --git a/config/app/usb_comm/handler/handler_knob.c b/config/app/usb_comm/handler/handler_knob.c index 6c876876..268f9aa8 100644 --- a/config/app/usb_comm/handler/handler_knob.c +++ b/config/app/usb_comm/handler/handler_knob.c @@ -12,6 +12,8 @@ #include +#include + #define KNOB_NODE DT_ALIAS(knob) #define MOTOR_NODE DT_PHANDLE(KNOB_NODE, motor) @@ -45,6 +47,53 @@ static bool handle_motor_get_state(const usb_comm_MessageH2D *h2d, usb_comm_Mess USB_COMM_HANDLER_DEFINE(usb_comm_Action_MOTOR_GET_STATE, usb_comm_MessageD2H_motor_state_tag, handle_motor_get_state); +static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) +{ + char *str = *arg; + if (!pb_encode_tag_for_field(stream, field)) { + return false; + } + return pb_encode_string(stream, (uint8_t *)str, strlen(str)); +} + +static bool write_prefs(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) +{ + const struct knob_pref *prefs; + const char **names; + int layers = knob_app_get_prefs(&prefs, &names); + if (!layers) { + return false; + } + + enum knob_mode mode = knob_get_mode(knob); + int ppr = knob_get_encoder_ppr(knob); + float torque_limit = motor_get_torque_limit(motor); + + usb_comm_KnobConfig_Pref pref = usb_comm_KnobConfig_Pref_init_zero; + for (int i = 0; i < layers; i++) { + if (!pb_encode_tag_for_field(stream, field)) { + return false; + } + + pref.layer_id = i; + pref.layer_name.funcs.encode = write_string; + pref.layer_name.arg = (void *)names[i]; + pref.active = prefs[i].active; + pref.mode = (usb_comm_KnobConfig_Mode)(prefs[i].active ? prefs[i].mode : mode); + pref.has_mode = true; + pref.ppr = prefs[i].active ? prefs[i].ppr : ppr; + pref.has_ppr = true; + pref.torque_limit = prefs[i].active ? prefs[i].torque_limit : torque_limit; + pref.has_torque_limit = true; + + if (!pb_encode_submessage(stream, usb_comm_KnobConfig_Pref_fields, &pref)) { + return false; + } + } + + return true; +} + static bool handle_knob_get_config(const usb_comm_MessageH2D *h2d, usb_comm_MessageD2H *d2h, const void *bytes, uint32_t bytes_len) { @@ -56,6 +105,7 @@ static bool handle_knob_get_config(const usb_comm_MessageH2D *h2d, usb_comm_Mess res->demo = knob_app_get_demo(); res->mode = (usb_comm_KnobConfig_Mode)knob_get_mode(knob); + res->prefs.funcs.encode = write_prefs; return true; } @@ -84,3 +134,45 @@ static bool handle_knob_set_config(const usb_comm_MessageH2D *h2d, usb_comm_Mess USB_COMM_HANDLER_DEFINE(usb_comm_Action_KNOB_SET_CONFIG, usb_comm_MessageD2H_knob_config_tag, handle_knob_set_config); + +static bool handle_knob_update_pref(const usb_comm_MessageH2D *h2d, usb_comm_MessageD2H *d2h, + const void *bytes, uint32_t bytes_len) +{ + const usb_comm_KnobConfig_Pref *req = &h2d->payload.knob_pref; + usb_comm_KnobConfig_Pref *res = &d2h->payload.knob_pref; + + { + struct knob_pref pref = { + .active = req->active, + .mode = req->has_mode ? (enum knob_mode)req->mode : KNOB_ENCODER, + .ppr = req->has_ppr ? req->ppr : 0, + .torque_limit = req->has_torque_limit ? req->torque_limit : 0, + }; + knob_app_set_pref(req->layer_id, &pref); + } + + { + enum knob_mode mode = knob_get_mode(knob); + int ppr = knob_get_encoder_ppr(knob); + float torque_limit = motor_get_torque_limit(motor); + + const struct knob_pref *pref = knob_app_get_pref(req->layer_id); + if (pref == NULL) { + return false; + } + + res->layer_id = req->layer_id; + res->active = pref->active; + res->mode = (usb_comm_KnobConfig_Mode)(pref->active ? pref->mode : mode); + res->has_mode = true; + res->ppr = pref->active ? pref->ppr : ppr; + res->has_ppr = true; + res->torque_limit = pref->active ? pref->torque_limit : torque_limit; + res->has_torque_limit = true; + } + + return true; +} + +USB_COMM_HANDLER_DEFINE(usb_comm_Action_KNOB_UPDATE_PREF, usb_comm_MessageD2H_knob_pref_tag, + handle_knob_update_pref); diff --git a/config/app/usb_comm/handler/handler_version.c b/config/app/usb_comm/handler/handler_version.c index f64dcd21..9926123e 100644 --- a/config/app/usb_comm/handler/handler_version.c +++ b/config/app/usb_comm/handler/handler_version.c @@ -60,6 +60,7 @@ static bool handle_version(const usb_comm_MessageH2D *h2d, usb_comm_MessageD2H * #ifdef CONFIG_HW75_USB_COMM_FEATURE_KNOB res->features.has_knob = res->features.knob = true; + res->features.has_knob_prefs = res->features.knob_prefs = true; #endif // CONFIG_HW75_USB_COMM_FEATURE_KNOB return true; diff --git a/config/boards/arm/hw75_dynamic/app/knob_app.c b/config/boards/arm/hw75_dynamic/app/knob_app.c index 7fa0aa4e..e8d75777 100644 --- a/config/boards/arm/hw75_dynamic/app/knob_app.c +++ b/config/boards/arm/hw75_dynamic/app/knob_app.c @@ -5,6 +5,7 @@ #include #include +#include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -13,8 +14,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include +#include #include #include +#include #include #include "knob_app.h" @@ -25,11 +28,22 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define KNOB_APP_THREAD_STACK_SIZE 1024 #define KNOB_APP_THREAD_PRIORITY 10 +#define KEYMAP_NODE DT_INST(0, zmk_keymap) +#define KEYMAP_LAYER_CHILD_LEN(node) 1 + +#define KEYMAP_LAYERS_NUM (DT_FOREACH_CHILD(KEYMAP_NODE, KEYMAP_LAYER_CHILD_LEN) 0) + +#define LAYER_LABEL(node) \ + COND_CODE_0(DT_NODE_HAS_PROP(node, label), (NULL), (DT_PROP(node, label))), + +static const char *layer_names[KEYMAP_LAYERS_NUM] = { DT_FOREACH_CHILD(KEYMAP_NODE, LAYER_LABEL) }; + static const struct device *knob = DEVICE_DT_GET(KNOB_NODE); static const struct device *motor = DEVICE_DT_GET(MOTOR_NODE); static bool motor_demo = false; +static struct knob_pref knob_prefs[KEYMAP_LAYERS_NUM]; + static struct k_work_delayable knob_enable_report_work; static void knob_app_enable_report_delayed_work(struct k_work *work) @@ -55,6 +69,8 @@ static struct k_work_q knob_work_q; ZMK_EVENT_IMPL(app_knob_state_changed); +static void knob_app_apply_pref(uint8_t layer_id); + static void knob_app_calibrate(struct k_work *work) { ZMK_EVENT_RAISE(new_app_knob_state_changed((struct app_knob_state_changed){ @@ -65,8 +81,7 @@ static void knob_app_calibrate(struct k_work *work) int ret = motor_calibrate_auto(motor); if (ret == 0) { - knob_set_mode(knob, KNOB_ENCODER); - knob_app_enable_report_delayed(); + knob_app_apply_pref(zmk_keymap_highest_layer_active()); ZMK_EVENT_RAISE(new_app_knob_state_changed((struct app_knob_state_changed){ .enable = true, @@ -116,6 +131,110 @@ void knob_app_set_demo(bool demo) })); } +#ifdef CONFIG_SETTINGS +static int knob_app_settings_load_cb(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + const char *next; + int ret; + + if (settings_name_steq(name, "prefs", &next) && !next) { + if (len != sizeof(knob_prefs)) { + return -EINVAL; + } + + ret = read_cb(cb_arg, &knob_prefs, sizeof(knob_prefs)); + if (ret >= 0) { + return 0; + } + + LOG_DBG("Loaded knob prefs"); + + return ret; + } + + return -ENOENT; +} + +static void knob_app_save_prefs_work(struct k_work *work) +{ + ARG_UNUSED(work); + int ret = settings_save_one("app/knob/prefs", &knob_prefs, sizeof(knob_prefs)); + if (ret != 0) { + LOG_ERR("Failed saving prefs: %d", ret); + } else { + LOG_DBG("Saved knob prefs"); + } +} + +static struct k_work_delayable knob_app_save_work; +#endif + +int knob_app_save_prefs(void) +{ +#ifdef CONFIG_SETTINGS + int ret = k_work_reschedule(&knob_app_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); + return MIN(ret, 0); +#else + return 0; +#endif +} + +int knob_app_get_prefs(const struct knob_pref **prefs, const char ***names) +{ + *prefs = &knob_prefs[0]; + if (names != NULL) { + *names = &layer_names[0]; + } + return KEYMAP_LAYERS_NUM; +} + +const struct knob_pref *knob_app_get_pref(uint8_t layer_id) +{ + if (layer_id >= KEYMAP_LAYERS_NUM) { + return NULL; + } + return &knob_prefs[layer_id]; +} + +static void knob_app_apply_pref(uint8_t layer_id) +{ + knob_app_disable_report(); + + struct knob_pref *pref = &knob_prefs[layer_id]; + if (pref->active) { + if (knob_get_mode(knob) != pref->mode) { + knob_set_mode(knob, pref->mode); + } + knob_set_encoder_ppr(knob, pref->ppr); + motor_set_torque_limit(motor, pref->torque_limit); + } else { + if (knob_get_mode(knob) != KNOB_ENCODER) { + knob_set_mode(knob, KNOB_ENCODER); + } + knob_set_encoder_ppr(knob, 24); + motor_set_torque_limit(motor, 0.3f); + } + + knob_app_enable_report_delayed(); + + LOG_DBG("Applied knob prefs for layer %d, pref active: %d", layer_id, pref->active); +} + +void knob_app_set_pref(uint8_t layer_id, struct knob_pref *pref) +{ + if (layer_id >= KEYMAP_LAYERS_NUM) { + return; + } + + memcpy(&knob_prefs[layer_id], pref, sizeof(struct knob_pref)); + knob_app_save_prefs(); + + if (layer_id == zmk_keymap_highest_layer_active()) { + knob_app_apply_pref(layer_id); + } +} + static int knob_app_event_listener(const zmk_event_t *eh) { if (!knob || !motor) { @@ -137,6 +256,9 @@ static int knob_app_event_listener(const zmk_event_t *eh) .calibration = KNOB_CALIBRATE_OK, })); + return 0; + } else if (as_zmk_layer_state_changed(eh)) { + knob_app_apply_pref(zmk_keymap_highest_layer_active()); return 0; } @@ -146,6 +268,21 @@ static int knob_app_event_listener(const zmk_event_t *eh) static int knob_app_init(const struct device *dev) { ARG_UNUSED(dev); + int ret; + +#ifdef CONFIG_SETTINGS + ret = settings_subsys_init(); + if (ret) { + LOG_ERR("Failed to initializing settings subsys: %d", ret); + } + + ret = settings_load_subtree_direct("app/knob", knob_app_settings_load_cb, NULL); + if (ret) { + LOG_ERR("Failed to load knob settings: %d", ret); + } + + k_work_init_delayable(&knob_app_save_work, knob_app_save_prefs_work); +#endif k_work_init_delayable(&knob_enable_report_work, knob_app_enable_report_delayed_work); @@ -160,5 +297,6 @@ static int knob_app_init(const struct device *dev) ZMK_LISTENER(knob_app, knob_app_event_listener); ZMK_SUBSCRIPTION(knob_app, zmk_activity_state_changed); +ZMK_SUBSCRIPTION(knob_app, zmk_layer_state_changed); SYS_INIT(knob_app_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/config/boards/arm/hw75_dynamic/app/knob_app.h b/config/boards/arm/hw75_dynamic/app/knob_app.h index 31afcb67..de76f160 100644 --- a/config/boards/arm/hw75_dynamic/app/knob_app.h +++ b/config/boards/arm/hw75_dynamic/app/knob_app.h @@ -7,5 +7,16 @@ #include +struct knob_pref { + bool active; + enum knob_mode mode; + int ppr; + float torque_limit; +}; + bool knob_app_get_demo(void); void knob_app_set_demo(bool demo); + +int knob_app_get_prefs(const struct knob_pref **prefs, const char ***names); +const struct knob_pref *knob_app_get_pref(uint8_t layer_id); +void knob_app_set_pref(uint8_t layer_id, struct knob_pref *pref); diff --git a/config/drivers/sensor/knob/include/knob/drivers/knob.h b/config/drivers/sensor/knob/include/knob/drivers/knob.h index 71d7293d..8ae3be23 100644 --- a/config/drivers/sensor/knob/include/knob/drivers/knob.h +++ b/config/drivers/sensor/knob/include/knob/drivers/knob.h @@ -38,6 +38,10 @@ void knob_set_encoder_report(const struct device *dev, bool report); bool knob_get_encoder_report(const struct device *dev); +void knob_set_encoder_ppr(const struct device *dev, int ppr); + +int knob_get_encoder_ppr(const struct device *dev); + void knob_set_position_limit(const struct device *dev, float min, float max); void knob_get_position_limit(const struct device *dev, float *min, float *max); diff --git a/config/drivers/sensor/knob/include/knob/drivers/motor.h b/config/drivers/sensor/knob/include/knob/drivers/motor.h index 805f6ca2..7d5f3eb1 100644 --- a/config/drivers/sensor/knob/include/knob/drivers/motor.h +++ b/config/drivers/sensor/knob/include/knob/drivers/motor.h @@ -49,6 +49,8 @@ void motor_set_enable(const struct device *dev, bool enable); void motor_set_torque_limit(const struct device *dev, float limit); +float motor_get_torque_limit(const struct device *dev); + void motor_set_angle_pid(const struct device *dev, float p, float i, float d); void motor_set_velocity_pid(const struct device *dev, float p, float i, float d); diff --git a/config/drivers/sensor/knob/knob.c b/config/drivers/sensor/knob/knob.c index fa43adb2..97a4eca8 100644 --- a/config/drivers/sensor/knob/knob.c +++ b/config/drivers/sensor/knob/knob.c @@ -36,6 +36,7 @@ struct knob_data { float position_max; bool encoder_report; + int encoder_ppr; float encoder_rpp; float last_angle; @@ -285,6 +286,21 @@ bool knob_get_encoder_report(const struct device *dev) return data->encoder_report; } +void knob_set_encoder_ppr(const struct device *dev, int ppr) +{ + struct knob_data *data = dev->data; + if (ppr > 0) { + data->encoder_ppr = ppr; + data->encoder_rpp = PI2 / (float)ppr; + } +} + +int knob_get_encoder_ppr(const struct device *dev) +{ + struct knob_data *data = dev->data; + return data->encoder_ppr; +} + void knob_set_position_limit(const struct device *dev, float min, float max) { struct knob_data *data = dev->data; @@ -390,6 +406,7 @@ int knob_init(const struct device *dev) .position_min = deg_to_rad(190), \ .position_max = deg_to_rad(290), \ .encoder_report = false, \ + .encoder_ppr = DT_INST_PROP(n, ppr), \ .encoder_rpp = PI2 / (float)DT_INST_PROP(n, ppr), \ }; \ \ diff --git a/config/drivers/sensor/knob/motor.c b/config/drivers/sensor/knob/motor.c index 14777d25..ef43801e 100644 --- a/config/drivers/sensor/knob/motor.c +++ b/config/drivers/sensor/knob/motor.c @@ -296,6 +296,13 @@ void motor_set_torque_limit(const struct device *dev, float limit) data->pid_velocity.limit = data->voltage_limit; } +float motor_get_torque_limit(const struct device *dev) +{ + struct motor_data *data = dev->data; + + return data->voltage_limit; +} + void motor_set_angle_pid(const struct device *dev, float p, float i, float d) { struct motor_data *data = dev->data; diff --git a/config/proto/usb_comm.proto b/config/proto/usb_comm.proto index ddef40af..cd7cd911 100644 --- a/config/proto/usb_comm.proto +++ b/config/proto/usb_comm.proto @@ -15,6 +15,7 @@ enum Action { MOTOR_GET_STATE = 2; KNOB_GET_CONFIG = 3; KNOB_SET_CONFIG = 4; + KNOB_UPDATE_PREF = 9; RGB_CONTROL = 5; RGB_GET_STATE = 6; RGB_SET_STATE = 8; @@ -32,6 +33,7 @@ message MessageH2D { Nop nop = 2; KnobConfig knob_config = 3; + KnobConfig.Pref knob_pref = 6; RgbControl rgb_control = 4; RgbState rgb_state = 7; RgbIndicator rgb_indicator = 8; @@ -48,6 +50,7 @@ message MessageD2H Version version = 3; MotorState motor_state = 4; KnobConfig knob_config = 5; + KnobConfig.Pref knob_pref = 8; RgbState rgb_state = 6; RgbIndicator rgb_indicator = 9; EinkImage eink_image = 7; @@ -72,6 +75,7 @@ message Version optional bool rgb_indicator = 6; optional bool eink = 2; optional bool knob = 3; + optional bool knob_prefs = 4; } } @@ -96,6 +100,7 @@ message KnobConfig { required bool demo = 1; required Mode mode = 2; + repeated Pref prefs = 5; enum Mode { DISABLE = 0; @@ -106,6 +111,16 @@ message KnobConfig SPIN = 5; RATCHET = 6; } + + message Pref + { + required uint32 layer_id = 1; + optional string layer_name = 2; + required bool active = 3; + optional Mode mode = 4; + optional uint32 ppr = 5; + optional float torque_limit = 6; + } } message RgbControl