Skip to content

Commit

Permalink
knob_app: Manage per-layer Knob preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
xingrz committed May 12, 2023
1 parent 76380cf commit ddb7a58
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 2 deletions.
92 changes: 92 additions & 0 deletions config/app/usb_comm/handler/handler_knob.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <knob_app.h>

#include <pb_encode.h>

#define KNOB_NODE DT_ALIAS(knob)
#define MOTOR_NODE DT_PHANDLE(KNOB_NODE, motor)

Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
1 change: 1 addition & 0 deletions config/app/usb_comm/handler/handler_version.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
142 changes: 140 additions & 2 deletions config/boards/arm/hw75_dynamic/app/knob_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/settings/settings.h>

#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
Expand All @@ -13,8 +14,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <knob/drivers/motor.h>

#include <zmk/activity.h>
#include <zmk/keymap.h>
#include <zmk/event_manager.h>
#include <zmk/events/activity_state_changed.h>
#include <zmk/events/layer_state_changed.h>
#include <app/events/knob_state_changed.h>

#include "knob_app.h"
Expand All @@ -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)
Expand All @@ -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){
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}

Expand All @@ -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);

Expand All @@ -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);
11 changes: 11 additions & 0 deletions config/boards/arm/hw75_dynamic/app/knob_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,16 @@

#include <stdbool.h>

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);
4 changes: 4 additions & 0 deletions config/drivers/sensor/knob/include/knob/drivers/knob.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions config/drivers/sensor/knob/include/knob/drivers/motor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit ddb7a58

Please sign in to comment.