Distorted display

I am trying to build a digital clock - this is my very first LVGL project (I have decades of development experience, though, with C and many other languages.)

My approach to LVGL seems to be misguided on this project, as my results seem to involve a corrupted display or similar no-no that I assume is due to LVGL unfamiliarity.

This example code demonstrates the issue, which manifests with these displays in order, and increasingly corrupt after that)…

The display is actually a Waveshare ESP32-S3-Touch-AMOLED-1.75 board, and the environment is Arduino 2.3.3 with LVGL 8.4.0. I have not found any tutorials that explain how to get started, only examples from which (I suppose) I should be able to get the gist of what’s happening. I have always found it hard to learn by example because examples rarely explain the reasons behind the code, and the choices that had to be made to get there.

So I’ve made this effort, but clearly there are some things I don’t understand. Glaringly, of course, is the question of why the display gets (or looks) corrupted. But also, I haven’t grasped the use of lv_timer_handler() and lv_obj_update_layout(entity). And probably other things are misused or abused below.

I am not looking for someone to correct this; rather, I want to understand how I have gone wrong and what the right approach would be. And if someone wants to point out ways my approach could improve, I’m eagerly open to that.

Can you help?

#include "rc.h"

lv_disp_draw_buf_t draw_buf;
lv_color_t buf[LCD_WIDTH * LCD_HEIGHT / 10];
LV_FONT_DECLARE(DigiClock_120); 

lv_obj_t *label;  // Global label object
SensorPCF85063 rtc;

Arduino_DataBus *bus = new Arduino_ESP32QSPI(
  LCD_CS /* CS */, LCD_SCLK /* SCK */, LCD_SDIO0 /* SDIO0 */, LCD_SDIO1 /* SDIO1 */,
  LCD_SDIO2 /* SDIO2 */, LCD_SDIO3 /* SDIO3 */);

Arduino_CO5300 *gfx = new Arduino_CO5300(
  bus, LCD_RESET /* RST */, 0 /* rotation */, LCD_WIDTH /* width */, LCD_HEIGHT /* height */, 6, 0, 0, 0);

void setup() {

  Serial.begin(115200); /* prepare for possible serial debug */
  while (! Serial) {} // Wait for serial connection

  gfx->begin();
  gfx->setBrightness(128);

  lv_init();

#if LV_USE_LOG != 0
  lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

  lv_disp_draw_buf_init(&draw_buf, buf, NULL, LCD_WIDTH * LCD_HEIGHT / 10);

  /*Initialize the display*/
  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  /*Change the following line to your display resolution*/
  disp_drv.hor_res = LCD_WIDTH;
  disp_drv.ver_res = LCD_HEIGHT;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.rounder_cb = example_lvgl_rounder_cb;
  disp_drv.draw_buf = &draw_buf;
  disp_drv.sw_rotate = 1 ;
  disp_drv.rotated = LV_DISP_ROT_NONE ;
  lv_disp_drv_register(&disp_drv);

  lv_obj_t * scr = lv_scr_act();
  lv_obj_set_style_bg_color(scr, lv_color_hex(0x000000), LV_PART_MAIN);    // The page is black
  lv_obj_set_style_text_color(scr, lv_color_hex(0xffffff), LV_PART_MAIN);  // The ink is white
  lv_disp_set_rotation(NULL, LV_DISP_ROT_270) ; // We set "no rotation" and then "rotate 270" because straight-in 270 did not work for me

  /*Initialize the (dummy) input device driver*/
  static lv_indev_drv_t indev_drv;
  lv_indev_drv_init(&indev_drv);
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  lv_indev_drv_register(&indev_drv);

  const esp_timer_create_args_t lvgl_tick_timer_args = {
    .callback = &example_increase_lvgl_tick,
    .name = "lvgl_tick"
  };

  const esp_timer_create_args_t reboot_timer_args = {
    .callback = &example_increase_reboot,
    .name = "reboot"
  };

  #define EXAMPLE_LVGL_TICK_PERIOD_MS 2
  esp_timer_handle_t lvgl_tick_timer = NULL;
  esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer);
  esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);

  label = lv_label_create(lv_scr_act());
  lv_obj_set_style_text_font(label, &DigiClock_120, LV_PART_MAIN);  
  lv_obj_set_style_bg_color(label, lv_color_hex(0x000000), LV_PART_MAIN);    // The page is black
  lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), LV_PART_MAIN);  // The ink is white
  lv_label_set_text(label,"Init...");
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
  lv_obj_update_layout(label) ;
  Serial.println("LCD Width = " + String(LCD_WIDTH) + ", Height = " + String(LCD_HEIGHT)) ;

  lv_obj_set_style_text_font(label, &DigiClock_120, LV_PART_MAIN);
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
  lv_obj_update_layout(label) ;

  if (!rtc.begin(Wire, IIC_SDA, IIC_SCL)) {
    Serial.println("Failed to find PCF85063 SDA/SCL - check your wiring!");
    Serial.print("IIC_SDA: ") ;
    Serial.print(IIC_SDA) ;
    Serial.print(", IIC_SCL: ") ;
    Serial.println(IIC_SCL) ;
    while (1) {
      delay(1000);
    }
  }
  Serial.print("The value of LV_COLOR_16_SWAP is ") ;
  Serial.println(LV_COLOR_16_SWAP) ;

  rtc.setDateTime(2026, 3, 3, 15, 20, 45) ;
  int i = 3 ;
  while (i) {
    lv_label_set_text_fmt(label, "Setup\nEnded\n%d", i) ;
    lv_timer_handler() ;
    delay(1000) ;
    i-- ;
  }
}



void updateClock() {
  static int lastMinuteOrSecond = -1 ; // Determines if we need to refresh the display
  int thisMinuteOrSecond ;             // Determines if we need to refresh the display
  RTC_DateTime datetime = rtc.getDateTime();

  lv_timer_handler();
  delay(5); 

#ifdef SHOW_SECONDS
  thisMinuteOrSecond = datetime.getSecond() ;
#else
  thisMinuteOrSecond = datetime.getMinute() ;
#endif
  if (thisMinuteOrSecond != lastMinuteOrSecond) { // Displayed time has changed
    lastMinuteOrSecond = thisMinuteOrSecond ;

#ifdef SHOW_SECONDS
    lv_label_set_text_fmt(label, " %02d/%02d\n%02d:%02d:%02d"
                                  , datetime.getMonth(), datetime.getDay()
                                  , datetime.getHour(), datetime.getMinute(), datetime.getSecond()
                                      );
#else
    lv_label_set_text_fmt(label, " %02d/%02d\n %02d:%02d\n76F 24C"
                                  , datetime.getMonth(), datetime.getDay()
                                  , datetime.getHour(), datetime.getMinute()
                                        );
#endif

    // Update label with current time
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_update_layout(label) ;
    lv_timer_handler();
  }
}



void loop() {
  static int firstTime = 1 ;
  if (firstTime) {
    lv_label_set_text(label, "loop...") ;
    lv_obj_update_layout(label) ;
    lv_timer_handler() ;
    delay(2000) ;
  }
  updateClock() ;
  delay(20);
  if (firstTime) {
    lv_timer_handler() ;
    delay(2000) ;
    lv_label_set_text(label, "again") ;
    lv_obj_update_layout(label) ;
    lv_timer_handler() ;
    delay(2000) ;
    firstTime = 0 ;
  }
}




void example_lvgl_rounder_cb(struct _lv_disp_drv_t *disp_drv, lv_area_t *area) {
    area->x1 &= 0xfffffffe ; // Make it even - was:  if(area->x1 % 2 !=0)area->x1-- ;
    area->y1 &= 0xfffffffe ; // Make it even - was:  if(area->y1 % 2 !=0)area->y1-- ;
    area->x2 |= 0x00000001 ; // Make it odd  - was:  if(area->x2 %2 ==0)area->x2++ ;
    area->y2 |= 0x00000001 ; // Make it odd  - was:  if(area->y2 %2 ==0)area->y2++ ;
}


/* Display flushing */
void my_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);

#if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
  gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif

  lv_disp_flush_ready(disp);
}

void example_increase_lvgl_tick(void *arg) {
  /* Tell LVGL how many milliseconds has elapsed */
  lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

void example_increase_reboot(void *arg) {
  static uint8_t count = 0;
  count++;
  if (count == 30) {
    esp_restart();
  }
}


the rc.h file contains:

#include <lvgl.h>
#include "lv_conf.h"
#include "Arduino_GFX_Library.h"
#include "pin_config.h"
#include <Wire.h>
#include <SPI.h>
#include <Arduino.h>
#include "SensorPCF85063.hpp"

void example_lvgl_rounder_cb(struct _lv_disp_drv_t *disp_drv, lv_area_t *area) ;
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) ;
void example_increase_lvgl_tick(void *arg) ;
void example_increase_reboot(void *arg) ;

@delovelady, thanks for sharing. Is there a specific reason to use version 8.4.0? Considering this is the first project using LVGL, I suggest you use the main version and try running some examples. For this board, I found a repository hosted by Waveshare that contains examples for Arduino.

Please let me know if you have any trouble.

Thanks, @halyssonJr

It was from those examples (03…simpleTime, in fact) that the basis for this code was derived. (Recall what I said about examples not doing it for me. This is a prime example of why.)

Also, if the instructions are followed to the letter from that site, the version of LVGL that will be used, is the version I have used.

I still hope that, rather than throw other examples at me, someone might point out what is wrong with this code, which is what I thought I had asked.

@delovelady , I reviewed your code, and I noticed some inconsistencies that could be causing the distortions. Save your project as .txt, but you can rename it or just copy and paste it to your Arduino project. Please, check if it’s works.

demo.txt (5.9 KB)

Thanks for this. I need a few hours; I’m traveling now. I appreciate your efforts!@