mirror of
https://github.com/scottbez1/smartknob.git
synced 2025-09-26 23:09:27 +08:00
Demo improvements: LED hue config, press state reporting, custom display modes for timeline demo
This commit is contained in:
parent
1e2b1983c2
commit
f63deabf10
@ -1,6 +1,7 @@
|
||||
#if SK_DISPLAY
|
||||
#include "display_task.h"
|
||||
#include "semaphore_guard.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "font/roboto_light_60.h"
|
||||
|
||||
@ -19,6 +20,15 @@ DisplayTask::~DisplayTask() {
|
||||
vSemaphoreDelete(mutex_);
|
||||
}
|
||||
|
||||
static void drawPlayButton(TFT_eSprite& spr, int x, int y, int width, int height, uint16_t color) {
|
||||
spr.fillTriangle(
|
||||
x, y - height / 2,
|
||||
x, y + height / 2,
|
||||
x + width, y,
|
||||
color
|
||||
);
|
||||
}
|
||||
|
||||
void DisplayTask::run() {
|
||||
tft_.begin();
|
||||
tft_.invertDisplay(1);
|
||||
@ -56,43 +66,6 @@ void DisplayTask::run() {
|
||||
spr_.fillSprite(TFT_BLACK);
|
||||
|
||||
int32_t num_positions = state.config.max_position - state.config.min_position + 1;
|
||||
if (num_positions > 1) {
|
||||
int32_t height = (state.current_position - state.config.min_position) * TFT_HEIGHT / (state.config.max_position - state.config.min_position);
|
||||
spr_.fillRect(0, TFT_HEIGHT - height, TFT_WIDTH, height, FILL_COLOR);
|
||||
}
|
||||
|
||||
spr_.setFreeFont(&Roboto_Light_60);
|
||||
spr_.drawNumber(state.current_position, TFT_WIDTH / 2, TFT_HEIGHT / 2 - VALUE_OFFSET, 1);
|
||||
spr_.setFreeFont(&DESCRIPTION_FONT);
|
||||
int32_t line_y = TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET;
|
||||
char* start = state.config.text;
|
||||
char* end = start + strlen(state.config.text);
|
||||
while (start < end) {
|
||||
char* newline = strchr(start, '\n');
|
||||
if (newline == nullptr) {
|
||||
newline = end;
|
||||
}
|
||||
|
||||
char buf[sizeof(state.config.text)] = {};
|
||||
strncat(buf, start, min(sizeof(buf) - 1, (size_t)(newline - start)));
|
||||
spr_.drawString(String(buf), TFT_WIDTH / 2, line_y, 1);
|
||||
start = newline + 1;
|
||||
line_y += spr_.fontHeight(1);
|
||||
}
|
||||
|
||||
float left_bound = PI / 2;
|
||||
|
||||
if (num_positions > 0) {
|
||||
float range_radians = (state.config.max_position - state.config.min_position) * state.config.position_width_radians;
|
||||
left_bound = PI / 2 + range_radians / 2;
|
||||
float right_bound = PI / 2 - range_radians / 2;
|
||||
spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(left_bound), TFT_HEIGHT/2 - RADIUS * sinf(left_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(left_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(left_bound), TFT_WHITE);
|
||||
spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(right_bound), TFT_HEIGHT/2 - RADIUS * sinf(right_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(right_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(right_bound), TFT_WHITE);
|
||||
}
|
||||
if (DRAW_ARC) {
|
||||
spr_.drawCircle(TFT_WIDTH/2, TFT_HEIGHT/2, RADIUS, TFT_DARKGREY);
|
||||
}
|
||||
|
||||
float adjusted_sub_position = state.sub_position_unit * state.config.position_width_radians;
|
||||
if (num_positions > 0) {
|
||||
if (state.current_position == state.config.min_position && state.sub_position_unit < 0) {
|
||||
@ -102,25 +75,114 @@ void DisplayTask::run() {
|
||||
}
|
||||
}
|
||||
|
||||
float left_bound = PI / 2;
|
||||
float right_bound = 0;
|
||||
if (num_positions > 0) {
|
||||
float range_radians = (state.config.max_position - state.config.min_position) * state.config.position_width_radians;
|
||||
left_bound = PI / 2 + range_radians / 2;
|
||||
right_bound = PI / 2 - range_radians / 2;
|
||||
}
|
||||
float raw_angle = left_bound - (state.current_position - state.config.min_position) * state.config.position_width_radians;
|
||||
float adjusted_angle = raw_angle - adjusted_sub_position;
|
||||
|
||||
bool sk_demo_mode = strncmp(state.config.text, "SKDEMO_", 7) == 0;
|
||||
|
||||
if (num_positions > 0 && ((state.current_position == state.config.min_position && state.sub_position_unit < 0) || (state.current_position == state.config.max_position && state.sub_position_unit > 0))) {
|
||||
if (!sk_demo_mode) {
|
||||
if (num_positions > 1) {
|
||||
int32_t height = (state.current_position - state.config.min_position) * TFT_HEIGHT / (state.config.max_position - state.config.min_position);
|
||||
spr_.fillRect(0, TFT_HEIGHT - height, TFT_WIDTH, height, FILL_COLOR);
|
||||
}
|
||||
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(raw_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(raw_angle), 5, DOT_COLOR);
|
||||
if (raw_angle < adjusted_angle) {
|
||||
for (float r = raw_angle; r <= adjusted_angle; r += 2 * PI / 180) {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
|
||||
spr_.setFreeFont(&Roboto_Light_60);
|
||||
spr_.drawNumber(state.current_position, TFT_WIDTH / 2, TFT_HEIGHT / 2 - VALUE_OFFSET, 1);
|
||||
spr_.setFreeFont(&DESCRIPTION_FONT);
|
||||
int32_t line_y = TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET;
|
||||
char* start = state.config.text;
|
||||
char* end = start + strlen(state.config.text);
|
||||
while (start < end) {
|
||||
char* newline = strchr(start, '\n');
|
||||
if (newline == nullptr) {
|
||||
newline = end;
|
||||
}
|
||||
|
||||
char buf[sizeof(state.config.text)] = {};
|
||||
strncat(buf, start, min(sizeof(buf) - 1, (size_t)(newline - start)));
|
||||
spr_.drawString(String(buf), TFT_WIDTH / 2, line_y, 1);
|
||||
start = newline + 1;
|
||||
line_y += spr_.fontHeight(1);
|
||||
}
|
||||
|
||||
if (num_positions > 0) {
|
||||
spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(left_bound), TFT_HEIGHT/2 - RADIUS * sinf(left_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(left_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(left_bound), TFT_WHITE);
|
||||
spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(right_bound), TFT_HEIGHT/2 - RADIUS * sinf(right_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(right_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(right_bound), TFT_WHITE);
|
||||
}
|
||||
if (DRAW_ARC) {
|
||||
spr_.drawCircle(TFT_WIDTH/2, TFT_HEIGHT/2, RADIUS, TFT_DARKGREY);
|
||||
}
|
||||
|
||||
if (num_positions > 0 && ((state.current_position == state.config.min_position && state.sub_position_unit < 0) || (state.current_position == state.config.max_position && state.sub_position_unit > 0))) {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(raw_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(raw_angle), 5, DOT_COLOR);
|
||||
if (raw_angle < adjusted_angle) {
|
||||
for (float r = raw_angle; r <= adjusted_angle; r += 2 * PI / 180) {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
|
||||
}
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
|
||||
} else {
|
||||
for (float r = raw_angle; r >= adjusted_angle; r -= 2 * PI / 180) {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
|
||||
}
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
|
||||
}
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
|
||||
} else {
|
||||
for (float r = raw_angle; r >= adjusted_angle; r -= 2 * PI / 180) {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
|
||||
}
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 5, DOT_COLOR);
|
||||
}
|
||||
} else {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 5, DOT_COLOR);
|
||||
if (strncmp(state.config.text, "SKDEMO_Scroll", 13) == 0) {
|
||||
spr_.fillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, spr_.color565(150, 0, 0));
|
||||
spr_.setFreeFont(&Roboto_Thin_24);
|
||||
spr_.drawString("Scroll", TFT_WIDTH / 2, TFT_HEIGHT / 2, 1);
|
||||
bool detent = false;
|
||||
for (uint8_t i = 0; i < state.config.detent_positions_count; i++) {
|
||||
if (state.config.detent_positions[i] == state.current_position) {
|
||||
detent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 16) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 16) * sinf(adjusted_angle), detent ? 8 : 5, TFT_WHITE);
|
||||
} else if (strncmp(state.config.text, "SKDEMO_Frames", 13) == 0) {
|
||||
int32_t width = (state.current_position - state.config.min_position) * TFT_WIDTH / (state.config.max_position - state.config.min_position);
|
||||
spr_.fillRect(0, 0, width, TFT_HEIGHT, spr_.color565(0, 150, 0));
|
||||
spr_.setFreeFont(&Roboto_Light_60);
|
||||
spr_.drawNumber(state.current_position, TFT_WIDTH / 2, TFT_HEIGHT / 2, 1);
|
||||
spr_.setFreeFont(&Roboto_Thin_24);
|
||||
spr_.drawString("Frame", TFT_WIDTH / 2, TFT_HEIGHT / 2 - DESCRIPTION_Y_OFFSET - VALUE_OFFSET, 1);
|
||||
} else if (strncmp(state.config.text, "SKDEMO_Speed", 12) == 0) {
|
||||
spr_.fillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, spr_.color565(0, 0, 150));
|
||||
|
||||
float normalizedFractional = sgn(state.sub_position_unit) *
|
||||
CLAMP(lerp(state.sub_position_unit * sgn(state.sub_position_unit), 0.1, 0.9, 0, 1), (float)0, (float)1);
|
||||
float normalized = state.current_position + normalizedFractional;
|
||||
float speed = sgn(normalized) * powf(2, fabsf(normalized) - 1);
|
||||
float roundedSpeed = truncf(speed * 10) / 10;
|
||||
|
||||
spr_.setFreeFont(&Roboto_Thin_24);
|
||||
if (roundedSpeed == 0) {
|
||||
spr_.drawString("Paused", TFT_WIDTH / 2, TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET + VALUE_OFFSET, 1);
|
||||
|
||||
spr_.fillRect(TFT_WIDTH / 2 + 5, TFT_HEIGHT / 2 - 20, 10, 40, TFT_WHITE);
|
||||
spr_.fillRect(TFT_WIDTH / 2 - 5 - 10, TFT_HEIGHT / 2 - 20, 10, 40, TFT_WHITE);
|
||||
} else {
|
||||
char buf[10];
|
||||
snprintf(buf, sizeof(buf), "%0.1fx", roundedSpeed);
|
||||
spr_.drawString(buf, TFT_WIDTH / 2, TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET + VALUE_OFFSET, 1);
|
||||
|
||||
uint16_t x = TFT_WIDTH / 2;
|
||||
for (uint8_t i = 0; i < max(1, abs(state.current_position)); i++) {
|
||||
drawPlayButton(spr_, x, TFT_HEIGHT / 2, sgn(roundedSpeed) * 20, 40, TFT_WHITE);
|
||||
x += sgn(roundedSpeed) * 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spr_.pushSprite(0, 0);
|
||||
@ -129,7 +191,7 @@ void DisplayTask::run() {
|
||||
SemaphoreGuard lock(mutex_);
|
||||
ledcWrite(LEDC_CHANNEL_LCD_BACKLIGHT, brightness_);
|
||||
}
|
||||
delay(2);
|
||||
delay(5);
|
||||
}
|
||||
}
|
||||
|
||||
|
8
firmware/src/interface_callbacks.h
Normal file
8
firmware/src/interface_callbacks.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "proto_gen/smartknob.pb.h"
|
||||
|
||||
typedef std::function<void(PB_SmartKnobConfig&)> ConfigCallback;
|
||||
typedef std::function<void(void)> MotorCalibrationCallback;
|
@ -40,6 +40,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
// pb_size_t detent_positions_count;
|
||||
// int32_t detent_positions[5];
|
||||
// float snap_point_bias;
|
||||
// int8_t led_hue;
|
||||
|
||||
{
|
||||
0,
|
||||
@ -55,6 +56,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
200,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -70,6 +72,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -85,6 +88,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
73,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -100,6 +104,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
157,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -115,6 +120,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
45,
|
||||
},
|
||||
{
|
||||
127,
|
||||
@ -130,6 +136,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
219,
|
||||
},
|
||||
{
|
||||
127,
|
||||
@ -145,6 +152,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
25,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -160,6 +168,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
200,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -175,6 +184,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
0,
|
||||
{},
|
||||
0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -190,6 +200,7 @@ static PB_SmartKnobConfig configs[] = {
|
||||
4,
|
||||
{2, 10, 21, 22},
|
||||
0,
|
||||
73,
|
||||
},
|
||||
{
|
||||
0,
|
||||
@ -204,7 +215,8 @@ static PB_SmartKnobConfig configs[] = {
|
||||
"Return-to-center\nwith detents",
|
||||
0,
|
||||
{},
|
||||
0.4
|
||||
0.4,
|
||||
157,
|
||||
},
|
||||
};
|
||||
|
||||
@ -213,12 +225,17 @@ InterfaceTask::InterfaceTask(const uint8_t task_core, MotorTask& motor_task, Dis
|
||||
stream_(),
|
||||
motor_task_(motor_task),
|
||||
display_task_(display_task),
|
||||
plaintext_protocol_(stream_, motor_task_),
|
||||
proto_protocol_(stream_, motor_task_) {
|
||||
plaintext_protocol_(stream_, [this] () {
|
||||
motor_task_.runCalibration();
|
||||
}),
|
||||
proto_protocol_(stream_, [this] (PB_SmartKnobConfig& config) {
|
||||
applyConfig(config, true);
|
||||
}) {
|
||||
#if SK_DISPLAY
|
||||
assert(display_task != nullptr);
|
||||
#endif
|
||||
|
||||
|
||||
log_queue_ = xQueueCreate(10, sizeof(std::string *));
|
||||
assert(log_queue_ != NULL);
|
||||
|
||||
@ -257,7 +274,7 @@ void InterfaceTask::run() {
|
||||
}
|
||||
#endif
|
||||
|
||||
motor_task_.setConfig(configs[0]);
|
||||
applyConfig(configs[0], false);
|
||||
motor_task_.addListener(knob_state_queue_);
|
||||
|
||||
plaintext_protocol_.init([this] () {
|
||||
@ -291,15 +308,15 @@ void InterfaceTask::run() {
|
||||
});
|
||||
|
||||
// Start in legacy protocol mode
|
||||
SerialProtocol* current_protocol = &plaintext_protocol_;
|
||||
current_protocol_ = &plaintext_protocol_;
|
||||
|
||||
ProtocolChangeCallback protocol_change_callback = [this, ¤t_protocol] (uint8_t protocol) {
|
||||
ProtocolChangeCallback protocol_change_callback = [this] (uint8_t protocol) {
|
||||
switch (protocol) {
|
||||
case SERIAL_PROTOCOL_LEGACY:
|
||||
current_protocol = &plaintext_protocol_;
|
||||
current_protocol_ = &plaintext_protocol_;
|
||||
break;
|
||||
case SERIAL_PROTOCOL_PROTO:
|
||||
current_protocol = &proto_protocol_;
|
||||
current_protocol_ = &proto_protocol_;
|
||||
break;
|
||||
default:
|
||||
log("Unknown protocol requested");
|
||||
@ -312,16 +329,15 @@ void InterfaceTask::run() {
|
||||
|
||||
// Interface loop:
|
||||
while (1) {
|
||||
PB_SmartKnobState state;
|
||||
if (xQueueReceive(knob_state_queue_, &state, 0) == pdTRUE) {
|
||||
current_protocol->handleState(state);
|
||||
if (xQueueReceive(knob_state_queue_, &latest_state_, 0) == pdTRUE) {
|
||||
publishState();
|
||||
}
|
||||
|
||||
current_protocol->loop();
|
||||
current_protocol_->loop();
|
||||
|
||||
std::string* log_string;
|
||||
while (xQueueReceive(log_queue_, &log_string, 0) == pdTRUE) {
|
||||
current_protocol->log(log_string->c_str());
|
||||
current_protocol_->log(log_string->c_str());
|
||||
delete log_string;
|
||||
}
|
||||
|
||||
@ -360,7 +376,7 @@ void InterfaceTask::changeConfig(bool next) {
|
||||
|
||||
snprintf(buf_, sizeof(buf_), "Changing config to %d -- %s", current_config_, configs[current_config_].text);
|
||||
log(buf_);
|
||||
motor_task_.setConfig(configs[current_config_]);
|
||||
applyConfig(configs[current_config_], false);
|
||||
}
|
||||
|
||||
void InterfaceTask::updateHardware() {
|
||||
@ -380,6 +396,7 @@ void InterfaceTask::updateHardware() {
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool pressed;
|
||||
#if SK_STRAIN
|
||||
if (scale.wait_ready_timeout(100)) {
|
||||
strain_reading_ = scale.read();
|
||||
@ -396,23 +413,26 @@ void InterfaceTask::updateHardware() {
|
||||
|
||||
// Ignore readings that are way out of expected bounds
|
||||
if (-1 < press_value_unit && press_value_unit < 2) {
|
||||
static bool pressed;
|
||||
static uint8_t press_count;
|
||||
if (!pressed && press_value_unit > 0.75) {
|
||||
press_count++;
|
||||
if (press_count > 2) {
|
||||
static uint8_t press_readings;
|
||||
if (!pressed && press_value_unit > 1) {
|
||||
press_readings++;
|
||||
if (press_readings > 2) {
|
||||
motor_task_.playHaptic(true);
|
||||
pressed = true;
|
||||
changeConfig(true);
|
||||
press_count_++;
|
||||
publishState();
|
||||
if (!remote_controlled_) {
|
||||
changeConfig(true);
|
||||
}
|
||||
}
|
||||
} else if (pressed && press_value_unit < 0.25) {
|
||||
press_count++;
|
||||
if (press_count > 2) {
|
||||
} else if (pressed && press_value_unit < 0.5) {
|
||||
press_readings++;
|
||||
if (press_readings > 2) {
|
||||
motor_task_.playHaptic(false);
|
||||
pressed = false;
|
||||
}
|
||||
} else {
|
||||
press_count = 0;
|
||||
press_readings = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -440,7 +460,7 @@ void InterfaceTask::updateHardware() {
|
||||
|
||||
#if SK_LEDS
|
||||
for (uint8_t i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i].setHSV(200 * CLAMP(press_value_unit, (float)0, (float)1), 255, brightness >> 8);
|
||||
leds[i].setHSV(latest_config_.led_hue, 255 - 180*CLAMP(press_value_unit, (float)0, (float)1) - 75*pressed, brightness >> 8);
|
||||
|
||||
// Gamma adjustment
|
||||
leds[i].r = dim8_video(leds[i].r);
|
||||
@ -455,3 +475,15 @@ void InterfaceTask::setConfiguration(Configuration* configuration) {
|
||||
SemaphoreGuard lock(mutex_);
|
||||
configuration_ = configuration;
|
||||
}
|
||||
|
||||
void InterfaceTask::publishState() {
|
||||
// Apply local state before publishing to serial
|
||||
latest_state_.press_nonce = press_count_;
|
||||
current_protocol_->handleState(latest_state_);
|
||||
}
|
||||
|
||||
void InterfaceTask::applyConfig(PB_SmartKnobConfig& config, bool from_remote) {
|
||||
remote_controlled_ = from_remote;
|
||||
latest_config_ = config;
|
||||
motor_task_.setConfig(config);
|
||||
}
|
||||
|
@ -44,7 +44,13 @@ class InterfaceTask : public Task<InterfaceTask>, public Logger {
|
||||
uint8_t strain_calibration_step_ = 0;
|
||||
int32_t strain_reading_ = 0;
|
||||
|
||||
SerialProtocol* current_protocol_ = nullptr;
|
||||
bool remote_controlled_ = false;
|
||||
int current_config_ = 0;
|
||||
uint8_t press_count_ = 1;
|
||||
|
||||
PB_SmartKnobState latest_state_ = {};
|
||||
PB_SmartKnobConfig latest_config_ = {};
|
||||
|
||||
QueueHandle_t log_queue_;
|
||||
QueueHandle_t knob_state_queue_;
|
||||
@ -53,4 +59,6 @@ class InterfaceTask : public Task<InterfaceTask>, public Logger {
|
||||
|
||||
void changeConfig(bool next);
|
||||
void updateHardware();
|
||||
void publishState();
|
||||
void applyConfig(PB_SmartKnobConfig& config, bool from_remote);
|
||||
};
|
||||
|
@ -295,8 +295,6 @@ void MotorTask::run() {
|
||||
last_publish = millis();
|
||||
}
|
||||
|
||||
motor.monitor();
|
||||
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ typedef struct _PB_SmartKnobConfig {
|
||||
pb_size_t detent_positions_count;
|
||||
int32_t detent_positions[5];
|
||||
float snap_point_bias;
|
||||
/* *
|
||||
Hue (0-255) for all 8 ring LEDs, if supported. Note: this will likely be replaced
|
||||
with more configurability in a future protocol version. */
|
||||
int16_t led_hue;
|
||||
} PB_SmartKnobConfig;
|
||||
|
||||
typedef struct _PB_SmartKnobState {
|
||||
@ -45,6 +49,8 @@ typedef struct _PB_SmartKnobState {
|
||||
float sub_position_unit;
|
||||
bool has_config;
|
||||
PB_SmartKnobConfig config;
|
||||
/* * Value that changes each time the knob is pressed */
|
||||
uint8_t press_nonce;
|
||||
} PB_SmartKnobState;
|
||||
|
||||
/* Message FROM the SmartKnob to the host */
|
||||
@ -103,8 +109,8 @@ extern "C" {
|
||||
#define PB_ToSmartknob_init_default {0, 0, 0, {PB_RequestState_init_default}}
|
||||
#define PB_Ack_init_default {0}
|
||||
#define PB_Log_init_default {""}
|
||||
#define PB_SmartKnobState_init_default {0, 0, false, PB_SmartKnobConfig_init_default}
|
||||
#define PB_SmartKnobConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0}
|
||||
#define PB_SmartKnobState_init_default {0, 0, false, PB_SmartKnobConfig_init_default, 0}
|
||||
#define PB_SmartKnobConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0, 0}
|
||||
#define PB_RequestState_init_default {0}
|
||||
#define PB_PersistentConfiguration_init_default {0, false, PB_MotorCalibration_init_default, false, PB_StrainCalibration_init_default}
|
||||
#define PB_MotorCalibration_init_default {0, 0, 0, 0}
|
||||
@ -113,8 +119,8 @@ extern "C" {
|
||||
#define PB_ToSmartknob_init_zero {0, 0, 0, {PB_RequestState_init_zero}}
|
||||
#define PB_Ack_init_zero {0}
|
||||
#define PB_Log_init_zero {""}
|
||||
#define PB_SmartKnobState_init_zero {0, 0, false, PB_SmartKnobConfig_init_zero}
|
||||
#define PB_SmartKnobConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0}
|
||||
#define PB_SmartKnobState_init_zero {0, 0, false, PB_SmartKnobConfig_init_zero, 0}
|
||||
#define PB_SmartKnobConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, {0, 0, 0, 0, 0}, 0, 0}
|
||||
#define PB_RequestState_init_zero {0}
|
||||
#define PB_PersistentConfiguration_init_zero {0, false, PB_MotorCalibration_init_zero, false, PB_StrainCalibration_init_zero}
|
||||
#define PB_MotorCalibration_init_zero {0, 0, 0, 0}
|
||||
@ -135,9 +141,11 @@ extern "C" {
|
||||
#define PB_SmartKnobConfig_text_tag 10
|
||||
#define PB_SmartKnobConfig_detent_positions_tag 11
|
||||
#define PB_SmartKnobConfig_snap_point_bias_tag 12
|
||||
#define PB_SmartKnobConfig_led_hue_tag 13
|
||||
#define PB_SmartKnobState_current_position_tag 1
|
||||
#define PB_SmartKnobState_sub_position_unit_tag 2
|
||||
#define PB_SmartKnobState_config_tag 3
|
||||
#define PB_SmartKnobState_press_nonce_tag 4
|
||||
#define PB_FromSmartKnob_protocol_version_tag 1
|
||||
#define PB_FromSmartKnob_ack_tag 2
|
||||
#define PB_FromSmartKnob_log_tag 3
|
||||
@ -191,7 +199,8 @@ X(a, STATIC, SINGULAR, STRING, msg, 1)
|
||||
#define PB_SmartKnobState_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, current_position, 1) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, sub_position_unit, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, config, 3)
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, config, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, press_nonce, 4)
|
||||
#define PB_SmartKnobState_CALLBACK NULL
|
||||
#define PB_SmartKnobState_DEFAULT NULL
|
||||
#define PB_SmartKnobState_config_MSGTYPE PB_SmartKnobConfig
|
||||
@ -208,7 +217,8 @@ X(a, STATIC, SINGULAR, FLOAT, endstop_strength_unit, 8) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, snap_point, 9) \
|
||||
X(a, STATIC, SINGULAR, STRING, text, 10) \
|
||||
X(a, STATIC, REPEATED, INT32, detent_positions, 11) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, snap_point_bias, 12)
|
||||
X(a, STATIC, SINGULAR, FLOAT, snap_point_bias, 12) \
|
||||
X(a, STATIC, SINGULAR, INT32, led_hue, 13)
|
||||
#define PB_SmartKnobConfig_CALLBACK NULL
|
||||
#define PB_SmartKnobConfig_DEFAULT NULL
|
||||
|
||||
@ -270,10 +280,10 @@ extern const pb_msgdesc_t PB_StrainCalibration_msg;
|
||||
#define PB_MotorCalibration_size 15
|
||||
#define PB_PersistentConfiguration_size 47
|
||||
#define PB_RequestState_size 0
|
||||
#define PB_SmartKnobConfig_size 173
|
||||
#define PB_SmartKnobState_size 192
|
||||
#define PB_SmartKnobConfig_size 184
|
||||
#define PB_SmartKnobState_size 206
|
||||
#define PB_StrainCalibration_size 22
|
||||
#define PB_ToSmartknob_size 185
|
||||
#define PB_ToSmartknob_size 196
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
@ -40,7 +40,7 @@ void SerialProtocolPlaintext::loop() {
|
||||
demo_config_change_callback_();
|
||||
}
|
||||
} else if (b == 'C') {
|
||||
motor_task_.runCalibration();
|
||||
motor_calibration_callback_();
|
||||
} else if (b == 'S') {
|
||||
if (strain_calibration_callback_) {
|
||||
strain_calibration_callback_();
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "../proto_gen/smartknob.pb.h"
|
||||
|
||||
#include "interface_callbacks.h"
|
||||
#include "motor_task.h"
|
||||
#include "serial_protocol.h"
|
||||
#include "uart_stream.h"
|
||||
@ -11,7 +12,7 @@ typedef std::function<void(void)> StrainCalibrationCallback;
|
||||
|
||||
class SerialProtocolPlaintext : public SerialProtocol {
|
||||
public:
|
||||
SerialProtocolPlaintext(Stream& stream, MotorTask& motor_task) : SerialProtocol(), stream_(stream), motor_task_(motor_task) {}
|
||||
SerialProtocolPlaintext(Stream& stream, MotorCalibrationCallback motor_calibration_callback) : SerialProtocol(), stream_(stream), motor_calibration_callback_(motor_calibration_callback) {}
|
||||
~SerialProtocolPlaintext(){}
|
||||
void log(const char* msg) override;
|
||||
void loop() override;
|
||||
@ -21,7 +22,7 @@ class SerialProtocolPlaintext : public SerialProtocol {
|
||||
|
||||
private:
|
||||
Stream& stream_;
|
||||
MotorTask& motor_task_;
|
||||
MotorCalibrationCallback motor_calibration_callback_;
|
||||
PB_SmartKnobState latest_state_ = {};
|
||||
DemoConfigChangeCallback demo_config_change_callback_;
|
||||
StrainCalibrationCallback strain_calibration_callback_;
|
||||
|
@ -14,10 +14,10 @@ static SerialProtocolProtobuf* singleton_for_packet_serial = 0;
|
||||
static const uint16_t MIN_STATE_INTERVAL_MILLIS = 5;
|
||||
static const uint16_t PERIODIC_STATE_INTERVAL_MILLIS = 5000;
|
||||
|
||||
SerialProtocolProtobuf::SerialProtocolProtobuf(Stream& stream, MotorTask& motor_task) :
|
||||
SerialProtocolProtobuf::SerialProtocolProtobuf(Stream& stream, ConfigCallback config_callback) :
|
||||
SerialProtocol(),
|
||||
stream_(stream),
|
||||
motor_task_(motor_task),
|
||||
config_callback_(config_callback),
|
||||
packet_serial_() {
|
||||
packet_serial_.setStream(&stream);
|
||||
|
||||
@ -125,7 +125,7 @@ void SerialProtocolProtobuf::handlePacket(const uint8_t* buffer, size_t size) {
|
||||
|
||||
switch (pb_rx_buffer_.which_payload) {
|
||||
case PB_ToSmartknob_smartknob_config_tag: {
|
||||
motor_task_.setConfig(pb_rx_buffer_.payload.smartknob_config);
|
||||
config_callback_(pb_rx_buffer_.payload.smartknob_config);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -4,21 +4,22 @@
|
||||
|
||||
#include "../proto_gen/smartknob.pb.h"
|
||||
|
||||
#include "interface_callbacks.h"
|
||||
#include "motor_task.h"
|
||||
#include "serial_protocol.h"
|
||||
#include "uart_stream.h"
|
||||
|
||||
class SerialProtocolProtobuf : public SerialProtocol {
|
||||
public:
|
||||
SerialProtocolProtobuf(Stream& stream, MotorTask& motor_task);
|
||||
~SerialProtocolProtobuf(){}
|
||||
SerialProtocolProtobuf(Stream& stream, ConfigCallback config_callback);
|
||||
~SerialProtocolProtobuf(){};
|
||||
void log(const char* msg) override;
|
||||
void loop() override;
|
||||
void handleState(const PB_SmartKnobState& state) override;
|
||||
|
||||
private:
|
||||
Stream& stream_;
|
||||
MotorTask& motor_task_;
|
||||
ConfigCallback config_callback_;
|
||||
|
||||
PB_FromSmartKnob pb_tx_buffer_;
|
||||
PB_ToSmartknob pb_rx_buffer_;
|
||||
|
@ -9,3 +9,7 @@ template <typename T> T CLAMP(const T& value, const T& low, const T& high)
|
||||
#define COUNT_OF(A) (sizeof(A) / sizeof(A[0]))
|
||||
|
||||
float lerp(const float value, const float inMin, const float inMax, const float min, const float max);
|
||||
|
||||
template <typename T> int sgn(T val) {
|
||||
return (T(0) < val) - (val < T(0));
|
||||
}
|
||||
|
@ -41,6 +41,9 @@ message SmartKnobState {
|
||||
int32 current_position = 1;
|
||||
float sub_position_unit = 2;
|
||||
SmartKnobConfig config = 3;
|
||||
|
||||
/** Value that changes each time the knob is pressed */
|
||||
uint32 press_nonce = 4 [(nanopb).int_size = IS_8];
|
||||
}
|
||||
|
||||
|
||||
@ -66,6 +69,12 @@ message SmartKnobConfig {
|
||||
string text = 10 [(nanopb).max_length = 50];
|
||||
repeated int32 detent_positions = 11 [(nanopb).max_count = 5];
|
||||
float snap_point_bias = 12;
|
||||
|
||||
/**
|
||||
* Hue (0-255) for all 8 ring LEDs, if supported. Note: this will likely be replaced
|
||||
* with more configurability in a future protocol version.
|
||||
*/
|
||||
int32 led_hue = 13 [(nanopb).int_size = IS_16];
|
||||
}
|
||||
|
||||
message RequestState {}
|
||||
|
@ -21,6 +21,7 @@ const defaultConfig: Config = {
|
||||
text: 'Hello from\nweb serial!',
|
||||
detentPositions: [],
|
||||
snapPointBias: 0,
|
||||
ledHue: 0,
|
||||
}
|
||||
|
||||
export type AppProps = object
|
||||
@ -125,6 +126,7 @@ export const App: React.FC<AppProps> = () => {
|
||||
text: pendingSmartKnobConfig.text,
|
||||
detentPositions: [],
|
||||
snapPointBias: parseFloat(pendingSmartKnobConfig.snapPointBias) || 0,
|
||||
ledHue: 0,
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
@ -17,9 +17,9 @@ const MAX_ZOOM = 60
|
||||
const PIXELS_PER_POSITION = 5
|
||||
|
||||
enum Mode {
|
||||
Scroll = 'Scroll',
|
||||
Frames = 'Frames',
|
||||
Speed = 'Speed',
|
||||
Scroll = 'SKDEMO_Scroll',
|
||||
Frames = 'SKDEMO_Frames',
|
||||
Speed = 'SKDEMO_Speed',
|
||||
}
|
||||
|
||||
type PlaybackState = {
|
||||
@ -58,6 +58,7 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
text: Mode.Scroll,
|
||||
detentPositions: [],
|
||||
snapPointBias: 0,
|
||||
ledHue: 0,
|
||||
|
||||
zoomTimelinePixelsPerFrame: 0.1,
|
||||
})
|
||||
@ -78,6 +79,7 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
smartKnobConfig.text,
|
||||
smartKnobConfig.detentPositions,
|
||||
smartKnobConfig.snapPointBias,
|
||||
smartKnobConfig.ledHue,
|
||||
])
|
||||
const [playbackState, setPlaybackState] = useState<PlaybackState>({
|
||||
speed: 0,
|
||||
@ -139,6 +141,7 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
text: Mode.Scroll,
|
||||
detentPositions: findNClosest(Object.keys(detentPositions).map(parseInt), position, 5),
|
||||
snapPointBias: 0,
|
||||
ledHue: 0,
|
||||
|
||||
zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame,
|
||||
}
|
||||
@ -158,6 +161,7 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
text: Mode.Frames,
|
||||
detentPositions: [],
|
||||
snapPointBias: 0,
|
||||
ledHue: (120 * 255) / 360,
|
||||
|
||||
zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame,
|
||||
}
|
||||
@ -177,6 +181,7 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
text: Mode.Speed,
|
||||
detentPositions: [],
|
||||
snapPointBias: 0.4,
|
||||
ledHue: (240 * 255) / 360,
|
||||
|
||||
zoomTimelinePixelsPerFrame: curConfig.zoomTimelinePixelsPerFrame,
|
||||
}
|
||||
@ -187,6 +192,21 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
},
|
||||
[detentPositions, info.totalFrames, playbackState],
|
||||
)
|
||||
const nextMode = useCallback(() => {
|
||||
const curMode = smartKnobConfig.text as unknown as Mode
|
||||
console.log('nextMode', curMode)
|
||||
if (curMode === Mode.Scroll) {
|
||||
changeMode(Mode.Frames)
|
||||
} else if (curMode === Mode.Frames) {
|
||||
changeMode(Mode.Speed)
|
||||
} else if (curMode === Mode.Speed) {
|
||||
changeMode(Mode.Scroll)
|
||||
} else {
|
||||
exhaustiveCheck(curMode)
|
||||
}
|
||||
}, [smartKnobConfig.text, changeMode])
|
||||
|
||||
// Initialize to Scroll mode
|
||||
useEffect(() => {
|
||||
changeMode(Mode.Scroll)
|
||||
}, [])
|
||||
@ -288,6 +308,21 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
interfaceState.zoomTimelinePixelsPerFrame,
|
||||
])
|
||||
|
||||
// Change mode when pressed
|
||||
const receivedPressNonceRef = useRef<boolean>(false)
|
||||
const previousPressNonceRef = useRef<number>(0)
|
||||
useEffect(() => {
|
||||
if (previousPressNonceRef.current !== smartKnobState.pressNonce) {
|
||||
if (!receivedPressNonceRef.current) {
|
||||
// Ignore first nonce change
|
||||
receivedPressNonceRef.current = true
|
||||
} else {
|
||||
nextMode()
|
||||
}
|
||||
}
|
||||
previousPressNonceRef.current = smartKnobState.pressNonce
|
||||
}, [smartKnobState.pressNonce, nextMode])
|
||||
|
||||
const refreshInterval = 20
|
||||
const updateFrame = useCallback(() => {
|
||||
const fps = info.frameRate * playbackState.speed
|
||||
@ -326,6 +361,9 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
const connectToSerial = async () => {
|
||||
try {
|
||||
if (navigator.serial) {
|
||||
previousPressNonceRef.current = 0
|
||||
receivedPressNonceRef.current = false
|
||||
|
||||
const serialPort = await navigator.serial.requestPort({
|
||||
filters: SmartKnobWebSerial.USB_DEVICE_FILTERS,
|
||||
})
|
||||
@ -376,9 +414,9 @@ export const App: React.FC<AppProps> = ({info}) => {
|
||||
}}
|
||||
aria-label="Mode"
|
||||
>
|
||||
{Object.keys(Mode).map((mode) => (
|
||||
<ToggleButton value={mode} key={mode}>
|
||||
{mode}
|
||||
{Object.entries(Mode).map((mode_entry) => (
|
||||
<ToggleButton value={mode_entry[1]} key={mode_entry[1]}>
|
||||
{mode_entry[0]}
|
||||
</ToggleButton>
|
||||
))}
|
||||
</ToggleButtonGroup>
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const exhaustiveCheck = (x: never): never => {
|
||||
throw new Error("Didn't expect to get here", x)
|
||||
throw new Error(`Unexpected value: ${x}`, x)
|
||||
}
|
||||
|
||||
export const isSome = <T>(v: T | null | undefined): v is T => {
|
||||
|
@ -6,12 +6,7 @@ export class SmartKnobWebSerial extends SmartKnobCore {
|
||||
|
||||
constructor(port: SerialPort, onMessage: MessageCallback) {
|
||||
super(onMessage, (packet: Uint8Array) => {
|
||||
this.writer?.write(packet).catch((e) => {
|
||||
console.error('Error writing serial', e)
|
||||
this.port?.close()
|
||||
this.port = null
|
||||
this.portAvailable = false
|
||||
})
|
||||
this.writer?.write(packet).catch(this.onError)
|
||||
})
|
||||
this.port = port
|
||||
this.portAvailable = true
|
||||
@ -33,7 +28,9 @@ export class SmartKnobWebSerial extends SmartKnobCore {
|
||||
|
||||
const reader = this.port.readable.getReader()
|
||||
try {
|
||||
this.writer = this.port.writable.getWriter()
|
||||
const writer = this.port.writable.getWriter()
|
||||
writer.write(Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0])).catch(this.onError)
|
||||
this.writer = writer
|
||||
try {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
@ -47,11 +44,19 @@ export class SmartKnobWebSerial extends SmartKnobCore {
|
||||
}
|
||||
} finally {
|
||||
console.log('Releasing writer')
|
||||
this.writer?.releaseLock()
|
||||
writer?.releaseLock()
|
||||
}
|
||||
} finally {
|
||||
console.log('Releasing reader')
|
||||
reader.releaseLock()
|
||||
}
|
||||
}
|
||||
|
||||
private onError(e: unknown) {
|
||||
console.error('Error writing serial', e)
|
||||
this.port?.close()
|
||||
this.port = null
|
||||
this.portAvailable = false
|
||||
this.writer = undefined
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
|
||||
import nanopb_pb2 as nanopb__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fsmartknob.proto\x12\x02PB\x1a\x0cnanopb.proto\"\x9a\x01\n\rFromSmartKnob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x16\n\x03\x61\x63k\x18\x02 \x01(\x0b\x32\x07.PB.AckH\x00\x12\x16\n\x03log\x18\x03 \x01(\x0b\x32\x07.PB.LogH\x00\x12-\n\x0fsmartknob_state\x18\x04 \x01(\x0b\x32\x12.PB.SmartKnobStateH\x00\x42\t\n\x07payload\"\xa4\x01\n\x0bToSmartknob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\r\n\x05nonce\x18\x02 \x01(\r\x12)\n\rrequest_state\x18\x03 \x01(\x0b\x32\x10.PB.RequestStateH\x00\x12/\n\x10smartknob_config\x18\x04 \x01(\x0b\x32\x13.PB.SmartKnobConfigH\x00\x42\t\n\x07payload\"\x14\n\x03\x41\x63k\x12\r\n\x05nonce\x18\x01 \x01(\r\"\x1a\n\x03Log\x12\x13\n\x03msg\x18\x01 \x01(\tB\x06\x92?\x03p\xff\x01\"j\n\x0eSmartKnobState\x12\x18\n\x10\x63urrent_position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12#\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\x13.PB.SmartKnobConfig\"\xc9\x02\n\x0fSmartKnobConfig\x12\x10\n\x08position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12\x1d\n\x0eposition_nonce\x18\x03 \x01(\rB\x05\x92?\x02\x38\x08\x12\x14\n\x0cmin_position\x18\x04 \x01(\x05\x12\x14\n\x0cmax_position\x18\x05 \x01(\x05\x12\x1e\n\x16position_width_radians\x18\x06 \x01(\x02\x12\x1c\n\x14\x64\x65tent_strength_unit\x18\x07 \x01(\x02\x12\x1d\n\x15\x65ndstop_strength_unit\x18\x08 \x01(\x02\x12\x12\n\nsnap_point\x18\t \x01(\x02\x12\x13\n\x04text\x18\n \x01(\tB\x05\x92?\x02p2\x12\x1f\n\x10\x64\x65tent_positions\x18\x0b \x03(\x05\x42\x05\x92?\x02\x10\x05\x12\x17\n\x0fsnap_point_bias\x18\x0c \x01(\x02\"\x0e\n\x0cRequestState\"v\n\x17PersistentConfiguration\x12\x0f\n\x07version\x18\x01 \x01(\r\x12#\n\x05motor\x18\x02 \x01(\x0b\x32\x14.PB.MotorCalibration\x12%\n\x06strain\x18\x03 \x01(\x0b\x32\x15.PB.StrainCalibration\"p\n\x10MotorCalibration\x12\x12\n\ncalibrated\x18\x01 \x01(\x08\x12\x1e\n\x16zero_electrical_offset\x18\x02 \x01(\x02\x12\x14\n\x0c\x64irection_cw\x18\x03 \x01(\x08\x12\x12\n\npole_pairs\x18\x04 \x01(\r\"<\n\x11StrainCalibration\x12\x12\n\nidle_value\x18\x01 \x01(\x05\x12\x13\n\x0bpress_delta\x18\x02 \x01(\x05\x62\x06proto3')
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fsmartknob.proto\x12\x02PB\x1a\x0cnanopb.proto\"\x9a\x01\n\rFromSmartKnob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\x16\n\x03\x61\x63k\x18\x02 \x01(\x0b\x32\x07.PB.AckH\x00\x12\x16\n\x03log\x18\x03 \x01(\x0b\x32\x07.PB.LogH\x00\x12-\n\x0fsmartknob_state\x18\x04 \x01(\x0b\x32\x12.PB.SmartKnobStateH\x00\x42\t\n\x07payload\"\xa4\x01\n\x0bToSmartknob\x12\x1f\n\x10protocol_version\x18\x01 \x01(\rB\x05\x92?\x02\x38\x08\x12\r\n\x05nonce\x18\x02 \x01(\r\x12)\n\rrequest_state\x18\x03 \x01(\x0b\x32\x10.PB.RequestStateH\x00\x12/\n\x10smartknob_config\x18\x04 \x01(\x0b\x32\x13.PB.SmartKnobConfigH\x00\x42\t\n\x07payload\"\x14\n\x03\x41\x63k\x12\r\n\x05nonce\x18\x01 \x01(\r\"\x1a\n\x03Log\x12\x13\n\x03msg\x18\x01 \x01(\tB\x06\x92?\x03p\xff\x01\"\x86\x01\n\x0eSmartKnobState\x12\x18\n\x10\x63urrent_position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12#\n\x06\x63onfig\x18\x03 \x01(\x0b\x32\x13.PB.SmartKnobConfig\x12\x1a\n\x0bpress_nonce\x18\x04 \x01(\rB\x05\x92?\x02\x38\x08\"\xe1\x02\n\x0fSmartKnobConfig\x12\x10\n\x08position\x18\x01 \x01(\x05\x12\x19\n\x11sub_position_unit\x18\x02 \x01(\x02\x12\x1d\n\x0eposition_nonce\x18\x03 \x01(\rB\x05\x92?\x02\x38\x08\x12\x14\n\x0cmin_position\x18\x04 \x01(\x05\x12\x14\n\x0cmax_position\x18\x05 \x01(\x05\x12\x1e\n\x16position_width_radians\x18\x06 \x01(\x02\x12\x1c\n\x14\x64\x65tent_strength_unit\x18\x07 \x01(\x02\x12\x1d\n\x15\x65ndstop_strength_unit\x18\x08 \x01(\x02\x12\x12\n\nsnap_point\x18\t \x01(\x02\x12\x13\n\x04text\x18\n \x01(\tB\x05\x92?\x02p2\x12\x1f\n\x10\x64\x65tent_positions\x18\x0b \x03(\x05\x42\x05\x92?\x02\x10\x05\x12\x17\n\x0fsnap_point_bias\x18\x0c \x01(\x02\x12\x16\n\x07led_hue\x18\r \x01(\x05\x42\x05\x92?\x02\x38\x10\"\x0e\n\x0cRequestState\"v\n\x17PersistentConfiguration\x12\x0f\n\x07version\x18\x01 \x01(\r\x12#\n\x05motor\x18\x02 \x01(\x0b\x32\x14.PB.MotorCalibration\x12%\n\x06strain\x18\x03 \x01(\x0b\x32\x15.PB.StrainCalibration\"p\n\x10MotorCalibration\x12\x12\n\ncalibrated\x18\x01 \x01(\x08\x12\x1e\n\x16zero_electrical_offset\x18\x02 \x01(\x02\x12\x14\n\x0c\x64irection_cw\x18\x03 \x01(\x08\x12\x12\n\npole_pairs\x18\x04 \x01(\r\"<\n\x11StrainCalibration\x12\x12\n\nidle_value\x18\x01 \x01(\x05\x12\x13\n\x0bpress_delta\x18\x02 \x01(\x05\x62\x06proto3')
|
||||
|
||||
|
||||
|
||||
@ -108,12 +108,16 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_TOSMARTKNOB.fields_by_name['protocol_version']._serialized_options = b'\222?\0028\010'
|
||||
_LOG.fields_by_name['msg']._options = None
|
||||
_LOG.fields_by_name['msg']._serialized_options = b'\222?\003p\377\001'
|
||||
_SMARTKNOBSTATE.fields_by_name['press_nonce']._options = None
|
||||
_SMARTKNOBSTATE.fields_by_name['press_nonce']._serialized_options = b'\222?\0028\010'
|
||||
_SMARTKNOBCONFIG.fields_by_name['position_nonce']._options = None
|
||||
_SMARTKNOBCONFIG.fields_by_name['position_nonce']._serialized_options = b'\222?\0028\010'
|
||||
_SMARTKNOBCONFIG.fields_by_name['text']._options = None
|
||||
_SMARTKNOBCONFIG.fields_by_name['text']._serialized_options = b'\222?\002p2'
|
||||
_SMARTKNOBCONFIG.fields_by_name['detent_positions']._options = None
|
||||
_SMARTKNOBCONFIG.fields_by_name['detent_positions']._serialized_options = b'\222?\002\020\005'
|
||||
_SMARTKNOBCONFIG.fields_by_name['led_hue']._options = None
|
||||
_SMARTKNOBCONFIG.fields_by_name['led_hue']._serialized_options = b'\222?\0028\020'
|
||||
_FROMSMARTKNOB._serialized_start=38
|
||||
_FROMSMARTKNOB._serialized_end=192
|
||||
_TOSMARTKNOB._serialized_start=195
|
||||
@ -122,16 +126,16 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_ACK._serialized_end=381
|
||||
_LOG._serialized_start=383
|
||||
_LOG._serialized_end=409
|
||||
_SMARTKNOBSTATE._serialized_start=411
|
||||
_SMARTKNOBSTATE._serialized_end=517
|
||||
_SMARTKNOBCONFIG._serialized_start=520
|
||||
_SMARTKNOBCONFIG._serialized_end=849
|
||||
_REQUESTSTATE._serialized_start=851
|
||||
_REQUESTSTATE._serialized_end=865
|
||||
_PERSISTENTCONFIGURATION._serialized_start=867
|
||||
_PERSISTENTCONFIGURATION._serialized_end=985
|
||||
_MOTORCALIBRATION._serialized_start=987
|
||||
_MOTORCALIBRATION._serialized_end=1099
|
||||
_STRAINCALIBRATION._serialized_start=1101
|
||||
_STRAINCALIBRATION._serialized_end=1161
|
||||
_SMARTKNOBSTATE._serialized_start=412
|
||||
_SMARTKNOBSTATE._serialized_end=546
|
||||
_SMARTKNOBCONFIG._serialized_start=549
|
||||
_SMARTKNOBCONFIG._serialized_end=902
|
||||
_REQUESTSTATE._serialized_start=904
|
||||
_REQUESTSTATE._serialized_end=918
|
||||
_PERSISTENTCONFIGURATION._serialized_start=920
|
||||
_PERSISTENTCONFIGURATION._serialized_end=1038
|
||||
_MOTORCALIBRATION._serialized_start=1040
|
||||
_MOTORCALIBRATION._serialized_end=1152
|
||||
_STRAINCALIBRATION._serialized_start=1154
|
||||
_STRAINCALIBRATION._serialized_end=1214
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
@ -34,6 +34,7 @@ sys.path.append(os.path.join(software_root, 'proto_gen'))
|
||||
from proto_gen import smartknob_pb2
|
||||
|
||||
SMARTKNOB_BAUD = 921600
|
||||
PROTOBUF_PROTOCOL_VERSION = 1
|
||||
|
||||
|
||||
class Smartknob(object):
|
||||
@ -92,6 +93,8 @@ class Smartknob(object):
|
||||
message = smartknob_pb2.FromSmartKnob()
|
||||
message.ParseFromString(payload)
|
||||
self._logger.debug(message)
|
||||
if message.protocol_version != PROTOBUF_PROTOCOL_VERSION:
|
||||
self._logger.warn(f'Invalid protocol version. Expected {PROTOBUF_PROTOCOL_VERSION}, received {message.protocol_version}')
|
||||
|
||||
payload_type = message.WhichOneof('payload')
|
||||
|
||||
@ -145,6 +148,7 @@ class Smartknob(object):
|
||||
nonce = self._next_nonce
|
||||
self._next_nonce += 1
|
||||
|
||||
message.protocol_version = PROTOBUF_PROTOCOL_VERSION
|
||||
message.nonce = nonce
|
||||
|
||||
payload = bytearray(message.SerializeToString())
|
||||
|
Loading…
Reference in New Issue
Block a user