mirror of
https://github.com/scottbez1/smartknob.git
synced 2025-09-26 23:09:27 +08:00
Protobuf and lvgl (#102)
* Protobuf and lvgl * Update platformio.ini Remove LV_TICK_CUSTOM_* build flags (they're implied by LV_TICK_CUSTOM=1 by default, I believe) and fix indents Co-authored-by: FLuffyIronBoots <johannesjaeger1234@gmail.com> Co-authored-by: Scott Bezek <scottbez1@gmail.com>
This commit is contained in:
parent
cfde66128c
commit
060f12ae7b
@ -6,7 +6,16 @@
|
||||
|
||||
static const uint8_t LEDC_CHANNEL_LCD_BACKLIGHT = 0;
|
||||
|
||||
DisplayTask::DisplayTask(const uint8_t task_core) : Task{"Display", 2048, 1, task_core} {
|
||||
TFT_eSPI tft_ = TFT_eSPI();
|
||||
|
||||
static lv_disp_draw_buf_t disp_buf;
|
||||
uint32_t size_in_px = DISP_BUF_SIZE;
|
||||
static lv_color_t buf[DISP_BUF_SIZE];
|
||||
|
||||
static lv_point_t points_left_bound[2] = {{0,0},{0,0}};
|
||||
static lv_point_t points_right_bound[2] = {{0,0},{0,0}};
|
||||
|
||||
DisplayTask::DisplayTask(const uint8_t task_core) : Task{"Display", 4096, 1, task_core} {
|
||||
knob_state_queue_ = xQueueCreate(1, sizeof(PB_SmartKnobState));
|
||||
assert(knob_state_queue_ != NULL);
|
||||
|
||||
@ -19,6 +28,20 @@ DisplayTask::~DisplayTask() {
|
||||
vSemaphoreDelete(mutex_);
|
||||
}
|
||||
|
||||
void disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
|
||||
{
|
||||
uint32_t w = (area->x2 - area->x1 + 1);
|
||||
uint32_t h = (area->y2 - area->y1 + 1);
|
||||
uint32_t wh = w*h;
|
||||
|
||||
tft_.startWrite();
|
||||
tft_.setAddrWindow(area->x1, area->y1, w, h);
|
||||
tft_.pushColors(&color_p->full, w * h, true);
|
||||
tft_.endWrite();
|
||||
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
void DisplayTask::run() {
|
||||
tft_.begin();
|
||||
tft_.invertDisplay(1);
|
||||
@ -29,54 +52,147 @@ void DisplayTask::run() {
|
||||
ledcAttachPin(PIN_LCD_BACKLIGHT, LEDC_CHANNEL_LCD_BACKLIGHT);
|
||||
ledcWrite(LEDC_CHANNEL_LCD_BACKLIGHT, UINT16_MAX);
|
||||
|
||||
spr_.setColorDepth(8);
|
||||
lv_init();
|
||||
|
||||
lv_disp_draw_buf_init(&disp_buf, buf, NULL, size_in_px);
|
||||
|
||||
lv_disp_drv_t disp_drv;
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
|
||||
log("Display Driver Init");
|
||||
disp_drv.hor_res = 240;
|
||||
disp_drv.ver_res = 240;
|
||||
disp_drv.flush_cb = disp_flush;
|
||||
disp_drv.draw_buf = &disp_buf;
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
log("Display Driver Register");
|
||||
|
||||
|
||||
|
||||
//Create lvgl objects
|
||||
screen = lv_scr_act();
|
||||
|
||||
lv_obj_set_style_bg_color(screen,lv_color_hex(0x383838),0);
|
||||
lv_obj_set_style_bg_opa( screen,LV_OPA_COVER,0);
|
||||
lv_obj_set_style_bg_grad_color(screen,lv_color_hex(0x7e4dfa),0);
|
||||
lv_obj_set_style_bg_grad_dir(screen,LV_GRAD_DIR_VER,0);
|
||||
lv_obj_set_style_bg_grad_stop(screen,TFT_HEIGHT,0);
|
||||
lv_obj_set_style_bg_main_stop(screen,TFT_HEIGHT,0); //Same value for gradient and main gives sharp line on Gradient
|
||||
|
||||
|
||||
//Testing with gauge. Not really suitable after a short test. Maybe create own lvgl Widget that is more usable with the smartknob
|
||||
/**gauge = lv_gauge_create(screen,NULL);
|
||||
lv_obj_clean_style_list(gauge,LV_GAUGE_PART_MAIN);
|
||||
lv_obj_set_size(gauge,210,210);
|
||||
lv_obj_align(gauge,screen,LV_ALIGN_CENTER,0,0);
|
||||
lv_gauge_set_range(gauge,0,10);
|
||||
lv_gauge_set_scale(gauge,360,10,10);
|
||||
lv_gauge_set_value(gauge,0,4);**/
|
||||
|
||||
/*Create line style*/
|
||||
static lv_style_t style_line;
|
||||
lv_style_init(&style_line);
|
||||
lv_style_set_line_width(&style_line, 4);
|
||||
lv_style_set_line_color(&style_line, lv_color_white());
|
||||
lv_style_set_line_rounded(&style_line, true);
|
||||
|
||||
line_left_bound = lv_line_create(screen);
|
||||
lv_obj_add_style(line_left_bound,&style_line, 0);
|
||||
lv_obj_set_pos(line_left_bound,0,0);
|
||||
|
||||
|
||||
line_right_bound = lv_line_create(screen);
|
||||
lv_obj_add_style(line_right_bound,&style_line, 0);
|
||||
lv_obj_set_pos(line_right_bound,0,0);
|
||||
|
||||
arc_dot = lv_arc_create(screen);//use this arc only for the knob
|
||||
//lv_style_reset(arc_dot,LV_PART_MAIN);//we dont need a background
|
||||
//lv_obj_set_style_bg_opa();
|
||||
lv_obj_remove_style(arc_dot,NULL,0);
|
||||
lv_arc_set_bg_angles(arc_dot,0,0);
|
||||
lv_obj_set_style_line_width(arc_dot,20,0);
|
||||
lv_obj_set_style_pad_all(arc_dot,10,0);//line width and padding are used in calculation for the knob and arc so we have to set them
|
||||
//lv_obj_set_style_bg_opa(arc_dot,LV_ARC_PART_BG,LV_STATE_DEFAULT,LV_OPA_TRANSP);
|
||||
//lv_obj_set_style_bg_opa(arc_dot,LV_ARC_PART_INDIC,LV_STATE_DEFAULT,LV_OPA_TRANSP);
|
||||
lv_obj_remove_style(arc_dot,NULL,LV_PART_INDICATOR | LV_STATE_DEFAULT);
|
||||
|
||||
lv_obj_set_size(arc_dot,TFT_WIDTH,TFT_HEIGHT);
|
||||
lv_obj_align(arc_dot,LV_ALIGN_CENTER,0,0);
|
||||
//lv_arc_set_adjustable(arc_dot,true);//knob showing
|
||||
lv_obj_add_flag(arc_dot,LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_add_flag(arc_dot, LV_OBJ_FLAG_CLICKABLE);
|
||||
|
||||
lv_arc_set_angles(arc_dot,0,0);
|
||||
//lv_arc_set_bg_angles(arc_dot,150,260);
|
||||
//lv_arc_set_value(arc_dot,20);
|
||||
lv_obj_set_style_line_width(arc_dot,10,LV_PART_INDICATOR|LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_pad_all(arc_dot,5,LV_PART_KNOB|LV_STATE_DEFAULT);
|
||||
lv_arc_set_rotation(arc_dot, 270);
|
||||
|
||||
arc = lv_arc_create(screen);
|
||||
//lv_obj_clean_style_list(arc,LV_ARC_PART_BG);
|
||||
lv_obj_remove_style(arc,NULL,LV_PART_MAIN | LV_STATE_DEFAULT);
|
||||
lv_arc_set_bg_angles(arc,0,0);
|
||||
lv_obj_set_style_line_width(arc,20,0);
|
||||
lv_obj_set_style_pad_all(arc,10,0);
|
||||
lv_obj_set_size(arc,TFT_WIDTH,TFT_HEIGHT);
|
||||
lv_obj_align(arc,LV_ALIGN_CENTER,0,0);
|
||||
//lv_arc_set_adjustable(arc,false);//knob hidden
|
||||
lv_obj_add_flag(arc,LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_arc_set_angles(arc,0,0);
|
||||
lv_obj_set_style_line_width(arc,10,LV_PART_INDICATOR|LV_STATE_DEFAULT);
|
||||
//lv_obj_set_style_pad_all(arc,LV_ARC_PART_KNOB,LV_STATE_DEFAULT,5);
|
||||
lv_arc_set_rotation(arc, 270);
|
||||
|
||||
//lv_obj_set_style_line_color(arc_dot,LV_ARC_PART_INDIC,LV_STATE_DEFAULT,LV_COLOR_TEAL);
|
||||
//lv_obj_set_style_line_color(arc_dot,LV_ARC_PART_KNOB,LV_STATE_DEFAULT,LV_COLOR_CYAN);
|
||||
//lv_obj_set_style_pad_all(arc_dot,LV_ARC_PART_INDIC,LV_STATE_DEFAULT,8);
|
||||
//lv_obj_set_style_size(arc_dot,LV_ARC_PART_KNOB,LV_STATE_DEFAULT,10);
|
||||
|
||||
|
||||
|
||||
label_cur_pos = lv_label_create(screen);
|
||||
//lv_obj_set_pos(label_cur_pos,TFT_WIDTH/2,TFT_HEIGHT/2-VALUE_OFFSET);
|
||||
//lv_label_set_align(label_cur_pos,LV_LABEL_ALIGN_CENTER);
|
||||
lv_obj_set_style_text_align(label_cur_pos,LV_ALIGN_CENTER,LV_PART_MAIN);
|
||||
lv_obj_align(label_cur_pos,LV_ALIGN_CENTER,0,-VALUE_OFFSET);
|
||||
lv_label_set_text(label_cur_pos,"0");
|
||||
lv_obj_set_style_text_color(label_cur_pos,lv_color_white(),0);
|
||||
lv_obj_set_style_text_font(label_cur_pos,&lv_font_montserrat_28,0);
|
||||
|
||||
label_desc = lv_label_create(screen);
|
||||
//lv_obj_set_pos(label_desc,TFT_WIDTH/2,TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET);
|
||||
lv_obj_set_style_text_align(label_desc,LV_ALIGN_CENTER,LV_PART_MAIN);
|
||||
lv_obj_align(label_desc,LV_ALIGN_CENTER,0,DESCRIPTION_Y_OFFSET);
|
||||
lv_label_set_text(label_desc,"");
|
||||
lv_obj_set_style_text_color(label_desc,lv_color_white(),0);
|
||||
lv_obj_set_style_text_font(label_desc,&lv_font_montserrat_16,0);
|
||||
|
||||
if (spr_.createSprite(TFT_WIDTH, TFT_HEIGHT) == nullptr) {
|
||||
log("ERROR: sprite allocation failed!");
|
||||
tft_.fillScreen(TFT_RED);
|
||||
} else {
|
||||
log("Sprite created!");
|
||||
tft_.fillScreen(TFT_PURPLE);
|
||||
}
|
||||
spr_.setTextColor(0xFFFF, TFT_BLACK);
|
||||
|
||||
PB_SmartKnobState state;
|
||||
|
||||
const int RADIUS = TFT_WIDTH / 2;
|
||||
const uint16_t FILL_COLOR = spr_.color565(90, 18, 151);
|
||||
const uint16_t DOT_COLOR = spr_.color565(80, 100, 200);
|
||||
|
||||
spr_.setTextDatum(CC_DATUM);
|
||||
spr_.setTextColor(TFT_WHITE);
|
||||
|
||||
while(1) {
|
||||
if (xQueueReceive(knob_state_queue_, &state, portMAX_DELAY) == pdFALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spr_.fillSprite(TFT_BLACK);
|
||||
if (state.config.num_positions > 1) {
|
||||
int32_t height = state.current_position * TFT_HEIGHT / (state.config.num_positions - 1);
|
||||
spr_.fillRect(0, TFT_HEIGHT - height, TFT_WIDTH, height, FILL_COLOR);
|
||||
int32_t height = state.current_position * TFT_HEIGHT / (state.config.num_positions-1);
|
||||
lv_obj_set_style_bg_grad_stop(screen,TFT_HEIGHT-height,0);
|
||||
lv_obj_set_style_bg_main_stop(screen,TFT_HEIGHT-height,0); //Same value for gradient and main gives sharp line on Gradient
|
||||
}
|
||||
|
||||
spr_.setFreeFont(&Roboto_Light_60);
|
||||
spr_.drawString(String() + 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);
|
||||
}
|
||||
lv_label_set_text_fmt(label_cur_pos,"%d" , state.current_position);
|
||||
lv_obj_set_style_text_align(label_cur_pos,LV_ALIGN_CENTER,LV_PART_MAIN);
|
||||
lv_obj_align(label_cur_pos,LV_ALIGN_CENTER,0,-VALUE_OFFSET);
|
||||
|
||||
lv_label_set_text(label_desc,state.config.text);
|
||||
lv_obj_set_style_text_align(label_desc,LV_ALIGN_CENTER,LV_PART_MAIN);
|
||||
lv_obj_align(label_desc,LV_ALIGN_CENTER,0,DESCRIPTION_Y_OFFSET);
|
||||
|
||||
float left_bound = PI / 2;
|
||||
|
||||
@ -84,11 +200,32 @@ void DisplayTask::run() {
|
||||
float range_radians = (state.config.num_positions - 1) * 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);
|
||||
points_left_bound[0] = {
|
||||
(lv_coord_t)(TFT_WIDTH/2 + RADIUS * cosf(left_bound)),
|
||||
(lv_coord_t)(TFT_HEIGHT/2 - RADIUS * sinf(left_bound))
|
||||
};
|
||||
points_left_bound[1] = {
|
||||
(lv_coord_t)(TFT_WIDTH/2 + (RADIUS - 10) * cosf(left_bound)),
|
||||
(lv_coord_t)(TFT_HEIGHT/2 - (RADIUS - 10) * sinf(left_bound))
|
||||
};
|
||||
lv_line_set_points(line_left_bound,points_left_bound,2);
|
||||
points_right_bound[0] = {
|
||||
(lv_coord_t)(TFT_WIDTH/2 + RADIUS * cosf(right_bound)),
|
||||
(lv_coord_t)(TFT_HEIGHT/2 - RADIUS * sinf(right_bound))
|
||||
};
|
||||
points_right_bound[1] = {
|
||||
(lv_coord_t)(TFT_WIDTH/2 + (RADIUS - 10) * cosf(right_bound)),
|
||||
(lv_coord_t)(TFT_HEIGHT/2 - (RADIUS - 10) * sinf(right_bound))
|
||||
};
|
||||
lv_line_set_points(line_right_bound,points_right_bound,2);
|
||||
}
|
||||
if (DRAW_ARC) {
|
||||
spr_.drawCircle(TFT_WIDTH/2, TFT_HEIGHT/2, RADIUS, TFT_DARKGREY);
|
||||
else{
|
||||
points_left_bound[0] = {0,0};
|
||||
points_left_bound[1] = {0,0};
|
||||
lv_line_set_points(line_left_bound,points_left_bound,2);
|
||||
points_right_bound[0] = {0,0};
|
||||
points_right_bound[1] = {0,0};
|
||||
lv_line_set_points(line_right_bound,points_right_bound,2);
|
||||
}
|
||||
|
||||
float adjusted_sub_position = state.sub_position_unit * state.config.position_width_radians;
|
||||
@ -102,26 +239,24 @@ void DisplayTask::run() {
|
||||
|
||||
float raw_angle = left_bound - state.current_position * state.config.position_width_radians;
|
||||
float adjusted_angle = raw_angle - adjusted_sub_position;
|
||||
int32_t raw_angle_offset = (int)(-((raw_angle*(180/PI))-90))%360;
|
||||
int32_t adjusted_angle_offset = (int)(-((adjusted_angle*(180/PI))-90))%360;
|
||||
|
||||
if (state.config.num_positions > 0 && ((state.current_position == 0 && state.sub_position_unit < 0) || (state.current_position == state.config.num_positions - 1 && state.sub_position_unit > 0))) {
|
||||
lv_arc_set_angles(arc_dot,raw_angle_offset,raw_angle_offset);
|
||||
|
||||
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);
|
||||
if(adjusted_sub_position!=0){
|
||||
lv_obj_add_flag(arc,LV_OBJ_FLAG_HIDDEN);
|
||||
if (raw_angle_offset < adjusted_angle_offset) {
|
||||
lv_arc_set_angles(arc,raw_angle_offset,adjusted_angle_offset);
|
||||
} 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);
|
||||
lv_arc_set_angles(arc,adjusted_angle_offset,raw_angle_offset);
|
||||
}
|
||||
} else {
|
||||
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 5, DOT_COLOR);
|
||||
}
|
||||
else{
|
||||
lv_obj_clear_flag(arc,LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
spr_.pushSprite(0, 0);
|
||||
lv_task_handler();
|
||||
|
||||
{
|
||||
SemaphoreGuard lock(mutex_);
|
||||
|
@ -4,11 +4,14 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <TFT_eSPI.h>
|
||||
#include "lvgl.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "proto_gen/smartknob.pb.h"
|
||||
#include "task.h"
|
||||
|
||||
#define DISP_BUF_SIZE (TFT_WIDTH * 10)
|
||||
|
||||
class DisplayTask : public Task<DisplayTask> {
|
||||
friend class Task<DisplayTask>; // Allow base Task to invoke protected run()
|
||||
|
||||
@ -25,10 +28,16 @@ class DisplayTask : public Task<DisplayTask> {
|
||||
void run();
|
||||
|
||||
private:
|
||||
TFT_eSPI tft_ = TFT_eSPI();
|
||||
|
||||
/** Full-size sprite used as a framebuffer */
|
||||
TFT_eSprite spr_ = TFT_eSprite(&tft_);
|
||||
lv_obj_t * screen;
|
||||
lv_obj_t * label_cur_pos;
|
||||
lv_obj_t * label_desc;
|
||||
lv_obj_t * arc;
|
||||
lv_obj_t * gauge;
|
||||
lv_obj_t * roller;
|
||||
lv_obj_t * line_left_bound;
|
||||
lv_obj_t * line_right_bound;
|
||||
lv_obj_t * arc_dot;
|
||||
|
||||
QueueHandle_t knob_state_queue_;
|
||||
|
||||
|
@ -47,6 +47,7 @@ lib_deps =
|
||||
; registry for the runtime. The submodule is available for manually updating
|
||||
; the pre-compiled (checked in) .pb.h/c files when proto files change, but is
|
||||
; otherwise not used during application firmware compilation.
|
||||
lvgl/lvgl@^8.3.0
|
||||
|
||||
build_flags =
|
||||
${base_config.build_flags}
|
||||
@ -109,3 +110,10 @@ build_flags =
|
||||
; GPIO >= 34 are input only
|
||||
; (SOC_GPIO_VALID_GPIO_MASK & ~(0ULL | _FL_BIT(34) | _FL_BIT(35) | _FL_BIT(36) | _FL_BIT(37) | _FL_BIT(38) | _FL_BIT(39)))
|
||||
-DSOC_GPIO_VALID_OUTPUT_GPIO_MASK=0x30EFFFFFF
|
||||
-DLV_LVGL_H_INCLUDE_SIMPLE=1
|
||||
-DLV_CONF_SKIP=1
|
||||
-DLV_COLOR_DEPTH=16
|
||||
-DLV_TICK_CUSTOM=1
|
||||
-DLV_THEME_DEFAULT_DARK=1
|
||||
-DLV_FONT_MONTSERRAT_16=1
|
||||
-DLV_FONT_MONTSERRAT_28=1
|
||||
|
Loading…
Reference in New Issue
Block a user