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:
fluffyironboots 2022-11-04 06:42:40 +01:00 committed by GitHub
parent cfde66128c
commit 060f12ae7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 208 additions and 56 deletions

View File

@ -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;
}
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);
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(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);
lv_arc_set_angles(arc,adjusted_angle_offset,raw_angle_offset);
}
spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
}
} 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_);

View File

@ -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_;

View File

@ -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