Преглед на файлове

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>
fluffyironboots преди 3 години
родител
ревизия
060f12ae7b
променени са 3 файла, в които са добавени 208 реда и са изтрити 56 реда
  1. 188 53
      firmware/src/display_task.cpp
  2. 12 3
      firmware/src/display_task.h
  3. 8 0
      platformio.ini

+ 188 - 53
firmware/src/display_task.cpp

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

+ 12 - 3
firmware/src/display_task.h

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

+ 8 - 0
platformio.ini

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