Changing screens with physical button - kinda works?

What MCU/Processor/Board and compiler are you using?

ESP32S3-LCD-Driver from Waveshare with a 2.8" round LCD ST7701 driver

What LVGL version are you using?

8 with Arduino GFX

What do you want to achieve?

Perfect changing screens with a physical button

I’ve been able to implement a solution which “works.” It does change screens with the push of a momentary button but only sometimes and sometimes it goes half way and stalls out leaving both screens partially visible…

This is the code I’m using… it’s basic push button code to change screens… is this the best way of doing this and if not is there a better way?

const int screenButton = 40; 
int screenButtonState = 0;

void () setup {
 pinMode(screenButton, INPUT_PULLUP);
}

void loop() {
 if (screenButtonState == LOW){
   if (current_screen == 0) {
    lv_scr_load(screen2);
    current_screen = 1;
  } else {
    lv_scr_load(screen1);
    current_screen = 0;
  }
  Serial.println("Switch screens");
  delay(150);
 }

Nobody has any insights that could help…?

Geez

Yes your showed code is bad total , Go back start with learn how lvgl works.

Hey Marian_M,

I was wondering if you were going to chime in and tell me that my code is bad and that I don’t know what I’m, doing and I should go back to basics… That’s your general response right. :wink:

I’m asking to learn, to better my code and myself. Your response isn’t helpful. I understand if you don’t want to give me working code, but pointing me in the right direction so I can teach myself would be helpful. I’m sure you know how to do this and it’s pretty basic for you but for me it’s challenging and instead of stepping on me, maybe consider any other approach.

I’ve spent a lot of time on the LVGL forum and in the docs looking and trying to understand how the code works- I’m a beginner and so my understanding is thin but we’re all beginners at one point and I’d like to think that a better response might be to help me learn rather than being condescending.

But, hey it’s Marian_M, that’s what you do, right.

Thanks in advance.

When you show code we can help … your setup is empty or ?

Start for example on lvgl/examples/arduino/LVGL_Arduino/LVGL_Arduino.ino at master · lvgl/lvgl · GitHub

Here’s the full code (I was trying to condense it down for the forum so that it wasn’t super long) Ignore the commented out sections as I’m still working on those…

Thank you in advance. As I mentioned I’m new to Arduino and LVGL so this code below is a pretty big accomplishment for me, even with errors and whatnot. I’m learning and will continue, but sometimes we all need a hand…

#include <Arduino_GFX_Library.h>
#include <lvgl.h>
//#include <Ds1302.h>

Arduino_DataBus *bus = new Arduino_SWSPI(GFX_NOT_DEFINED, 42, 2, 1, GFX_NOT_DEFINED);
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
  40 /* DE */, 39 /* VSYNC */, 38 /* HSYNC */, 41 /* PCLK */,
  5 /* R0 */, 45 /* R1 */, 48 /* R2 */, 47 /* R3 */, 21 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  46 /* B0 */, 3 /* B1 */, 8 /* B2 */, 18 /* B3 */, 17 /* B4 */,
  1 /* hsync_polarity */, 22 /* hsync_front_porch */, 24 /* hsync_pulse_width */, 6 /* hsync_back_porch */,
  1 /* vsync_polarity */, 16 /* vsync_front_porch */, 4 /* vsync_pulse_width */, 10 /* vsync_back_porch */);


Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
  480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
  bus, GFX_NOT_DEFINED /* RST */, st7701_type6_init_operations, sizeof(st7701_type6_init_operations));


/* Change to your screen resolution */
static uint32_t screenWidth;
static uint32_t screenHeight;
static uint32_t bufSize;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
#ifndef DIRECT_MODE
  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
#endif  // #ifndef DIRECT_MODE

  lv_disp_flush_ready(disp);
}

// DS1302 RTC instance
//Ds1302 rtc(15, 16, 17); //RTC module pins

// Button definitions
const int minuteButton = 17;int minuteButtonState = 0; //int prevMinuteState = HIGH;
const int screenButton = 40;int screenButtonState = 0; //int prevScreenState = HIGH;

// Screens
static lv_obj_t *screen1; //gauge
static lv_obj_t *screen2; //clock
static lv_obj_t *screen3; //metrics

// Meters
static lv_obj_t *meter1; //gauge
static lv_obj_t *meter2; //clock

// Metrics Labels currently unused
lv_obj_t *labelAFR; 
lv_obj_t *labelTPS; 
lv_obj_t *labelMAP; 
lv_obj_t *labelVOLT; 
lv_obj_t *labelCLT; 
lv_obj_t *labelRPM; 
lv_obj_t *labelBARO;
lv_obj_t *labelAIRCOR; 
lv_obj_t *labelWARMCOR; 
lv_obj_t *labelBAROCOR; 

//Clock hands
lv_meter_indicator_t * indic_min;
lv_meter_indicator_t * indic_hour;

//Clock time values
int minValue = 0;
float hourValue = 0.0;

// Time tracking
unsigned long last_switch_time = 0;
int current_screen = 0;

int twelveHour = 0;

void create_screens() {
 // Screen 1 Gauge
 LV_IMG_DECLARE(face);
 screen1 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen1, 480, 480);
 lv_obj_align(screen1, LV_ALIGN_CENTER, 0, 0);

 lv_obj_set_style_bg_img_src(screen1, &face, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter1 = lv_meter_create(screen1);  // Create the meter
 lv_obj_center(meter1);  // Center it on the screen
 lv_obj_remove_style(meter1, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter1, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter1, 480, 480);
  
 LV_IMG_DECLARE(needle);

 /*Add AFR scale */
 lv_meter_scale_t * scaleAFR = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleAFR, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleAFR, 10, 18, 60, 60);

 /*Add TPS scale */
 lv_meter_scale_t * scaleTPS = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleTPS, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleTPS, 0, 100, 60, 330);

 /*Add VOLT scale */
 lv_meter_scale_t * scaleVOLT = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleVOLT, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleVOLT, 9, 15, 60, 240);

  /*Add MAP scale */
 lv_meter_scale_t * scaleMAP = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleMAP, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleMAP, 0, 100, 60, 151);
  
 /* Add AFR needle */
 lv_meter_indicator_t * indic1;
 indic1 = lv_meter_add_needle_img(meter1, scaleAFR, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic1, 14);

 /* Add TPS needle */
 lv_meter_indicator_t * indic2;
 indic2 = lv_meter_add_needle_img(meter1, scaleTPS, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic2, 100);

 /* Add VOLT needle */
 lv_meter_indicator_t * indic3;
 indic3 = lv_meter_add_needle_img(meter1, scaleVOLT, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic3, 12);

 /* Add MAP needle */
 lv_meter_indicator_t * indic4;
 indic4 = lv_meter_add_needle_img(meter1, scaleMAP, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic4, 0);
  
 // Screen 2 Clock
 LV_IMG_DECLARE(clockFace);
 screen2 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen2, 480, 480);
 lv_obj_align(screen2, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_img_src(screen2, &clockFace, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter2 = lv_meter_create(screen2);  // Create the meter
 lv_obj_center(meter2);  // Center it on the screen
 lv_obj_remove_style(meter2, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter2, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter2, 480, 480);

 /*Create a scale for the minutes*/
 /*61 ticks in a 360 degrees range (the last and the first line overlaps)*/
 lv_meter_scale_t * scale_min = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_min, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_min, 0, 60, 360, 270);

 /*Create an other scale for the hours. It's only visual and contains only major ticks*/
 lv_meter_scale_t * scale_hour = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_hour, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_hour, 1, 12, 330, 300);       /*[1..12] values in an almost full circle*/

 LV_IMG_DECLARE(minute)
 LV_IMG_DECLARE(hour)

 /*Add a the hands from images*/
 indic_min = lv_meter_add_needle_img(meter2, scale_min, &minute, -2, 11);
 indic_hour = lv_meter_add_needle_img(meter2, scale_min, &hour, -2, 11);
 lv_meter_set_indicator_value(meter2, indic_min, minValue);
 lv_meter_set_indicator_value(meter2, indic_hour, hourValue);

 // Screen 3 metrics
 screen3 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen3, 480, 480);
 lv_obj_align(screen3, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_color(screen3,lv_color_black(),LV_PART_MAIN);

 }

void switch_screens() {
  delay(200);
  if (current_screen == 0) {
    lv_scr_load(screen2);
    current_screen = 1;
  } else {
    lv_scr_load(screen1);
    current_screen = 0;
  }
  Serial.println("Switch screens");
}

void increaseMinute () {
 //Ds1302::DateTime dt;
 //rtc.setDateTime(&dt);
 //minValue = dt.minute;
 minValue = (minValue + 1) % 60;
 hourValue = (hourValue + .084);
 Serial.println(hourValue);
 if (hourValue > 60.48) {
  hourValue = 0;
 }
 lv_meter_set_indicator_value(meter2, indic_min, minValue);
 lv_meter_set_indicator_value(meter2, indic_hour, hourValue);
 lv_obj_invalidate(screen2);
 }

void setup() {
  Serial.begin(115200);
  // Serial.setDebugOutput(true);
  // while(!Serial);
  Serial.println("911 Gauge Demo");

 #ifdef GFX_EXTRA_PRE_INIT
  GFX_EXTRA_PRE_INIT();
 #endif

 // Init Display
  if (!gfx->begin()) {
    Serial.println("gfx->begin() failed!");
  }
  gfx->fillScreen(RED);

 #ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
 #endif

  lv_init();

  screenWidth = gfx->width();
  screenHeight = gfx->height();

 #ifdef DIRECT_MODE
  bufSize = screenWidth * screenHeight;
 #else
  bufSize = screenWidth * 40;
 #endif

 #ifdef ESP32
  disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  if (!disp_draw_buf) {
    // remove MALLOC_CAP_INTERNAL flag try again
    disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_8BIT);
  }
 #else
  disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * bufSize);
 #endif
  if (!disp_draw_buf) {
    Serial.println("LVGL disp_draw_buf allocate failed!");
  } else {
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, bufSize);

    /* Initialize the display */
    lv_disp_drv_init(&disp_drv);
    /* Change the following line to your display resolution */
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
 #ifdef DIRECT_MODE
    disp_drv.direct_mode = true;
 #endif
  lv_disp_drv_register(&disp_drv);

  /* 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);

 // Set up buttons
 pinMode(minuteButton, INPUT_PULLUP);
 pinMode(screenButton, INPUT_PULLUP);

 // initialize the RTC
 //rtc.init();

 // Create the two screens
 create_screens();

 // Set screen1 as the initial screen
 lv_scr_load(screen1);

 Serial.println("Setup done");
  }
}

void loop() {
 lv_timer_handler(); /* let the GUI do its work */

 // read the buttons
 minuteButtonState = digitalRead(minuteButton);
 screenButtonState = digitalRead(screenButton);

 // Handle minute button presses
 if (minuteButtonState == LOW){
   increaseMinute();
 }

 //Change screens
 if (screenButtonState == LOW){
  delay(200);
  switch_screens();
  Serial.println("Screen Button Pressed");
 }

 #ifdef DIRECT_MODE
 #if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
 #else
  gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
 #endif
 #endif  // #ifdef DIRECT_MODE

 #ifdef CANVAS
  gfx->flush();
 #endif

  delay(5);
}

full code is best , next time try comment

void setup()
{
..... init all lv and hw
lv_scr_load(screen1);
}

and leave loop clean

void uitasks()
{
 // read the buttons in lv doc too you can use indev buttons and events
 minuteButtonState = digitalRead(minuteButton);
 screenButtonState = digitalRead(screenButton);

 // Handle minute button presses when you hold button this isnt good ... indev events need timing and invalidate screen in increaseMinute is too not required
 if (minuteButtonState == LOW){
   increaseMinute();
 }

 //Change screens
 if (screenButtonState == LOW){
   if(lv_scr_act() == screen1) lv_scr_load(screen2);
   else lv_scr_load(screen1);
  Serial.println("Screen Button Pressed");
 }
}
uint32_t looper5;
void loop() {
 lv_timer_handler(); /* let the GUI do its work most important LVGL func ! no block */
 delay(5);
 looper5++;
 if((looper5%10) == 0) uitasks();   //!never use delay in or for critical only delay lower as refresh display-5 as set in lvconf (default 30-5) or indev time what is less
}

and check lvconf TICK setup. For ESP is ok set custom 1.

@mmar22

I’ve modified my code based on your suggestions- I see why they make sense. Unfortunately, I’m still getting the same behavior: the screens will change but sometimes I see half of screen1 and half of screen2… See the photos (don’t worry about the color of the screen being off)

The button has a 10k resistor between the pin and the button…


#include <Arduino_GFX_Library.h>
#include <lvgl.h>
//#include <Ds1302.h>

Arduino_DataBus *bus = new Arduino_SWSPI(GFX_NOT_DEFINED, 42, 2, 1, GFX_NOT_DEFINED);
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
  40 /* DE */, 39 /* VSYNC */, 38 /* HSYNC */, 41 /* PCLK */,
  5 /* R0 */, 45 /* R1 */, 48 /* R2 */, 47 /* R3 */, 21 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  46 /* B0 */, 3 /* B1 */, 8 /* B2 */, 18 /* B3 */, 17 /* B4 */,
  1 /* hsync_polarity */, 22 /* hsync_front_porch */, 24 /* hsync_pulse_width */, 6 /* hsync_back_porch */,
  1 /* vsync_polarity */, 16 /* vsync_front_porch */, 4 /* vsync_pulse_width */, 10 /* vsync_back_porch */);


Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
  480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
  bus, GFX_NOT_DEFINED /* RST */, st7701_type6_init_operations, sizeof(st7701_type6_init_operations));


/* Change to your screen resolution */
static uint32_t screenWidth;
static uint32_t screenHeight;
static uint32_t bufSize;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
#ifndef DIRECT_MODE
  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
#endif  // #ifndef DIRECT_MODE

  lv_disp_flush_ready(disp);
}

// DS1302 RTC instance
//Ds1302 rtc(15, 16, 17); //RTC module pins

// Button definitions
const int minuteButton = 17;int minuteButtonState = 0; //int prevMinuteState = HIGH;
const int screenButton = 40;int screenButtonState = 0; //int prevScreenState = HIGH;

// Screens
static lv_obj_t *screen1; //gauge
static lv_obj_t *screen2; //clock
static lv_obj_t *screen3; //metrics

// Meters
static lv_obj_t *meter1; //gauge
static lv_obj_t *meter2; //clock

// Metrics Labels
lv_obj_t *labelAFR; 
lv_obj_t *labelTPS; 
lv_obj_t *labelMAP; 
lv_obj_t *labelVOLT; 
lv_obj_t *labelCLT; 
lv_obj_t *labelRPM; 
lv_obj_t *labelBARO;
lv_obj_t *labelAIRCOR; 
lv_obj_t *labelWARMCOR; 
lv_obj_t *labelBAROCOR; 

//Clock hands
lv_meter_indicator_t * indic_min;
lv_meter_indicator_t * indic_hour;

//Clock time values
int minValue = 0;
float hourValue = 0.0;

// Time tracking
unsigned long last_switch_time = 0;
int current_screen = 0;

int twelveHour = 0;

uint32_t looper5;

void create_screens() {
 // Screen 1 Gauge
 LV_IMG_DECLARE(face);
 screen1 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen1, 480, 480);
 lv_obj_align(screen1, LV_ALIGN_CENTER, 0, 0);

 //Swap gauge faces during nightime?
 // Ds1302::DateTime now;
 //  rtc.getDateTime(&now);
 //  if (now.hour > 12) {
 //   12Hour = now.hour-12;
 //   if (12Hour > 6) {
 //     LV_IMG_DECLARE(face);
 //   } else {
 //     LV_IMG_DECLARE(faceNight); 
 //   }
 //  }

 lv_obj_set_style_bg_img_src(screen1, &face, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter1 = lv_meter_create(screen1);  // Create the meter
 lv_obj_center(meter1);  // Center it on the screen
 lv_obj_remove_style(meter1, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter1, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter1, 480, 480);
  
 LV_IMG_DECLARE(needle);

 /*Add AFR scale */
 lv_meter_scale_t * scaleAFR = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleAFR, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleAFR, 10, 18, 60, 60);

 /*Add TPS scale */
 lv_meter_scale_t * scaleTPS = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleTPS, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleTPS, 0, 100, 60, 330);

 /*Add VOLT scale */
 lv_meter_scale_t * scaleVOLT = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleVOLT, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleVOLT, 9, 15, 60, 240);

  /*Add MAP scale */
 lv_meter_scale_t * scaleMAP = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleMAP, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleMAP, 0, 100, 60, 151);
  
 /* Add AFR needle */
 lv_meter_indicator_t * indic1;
 indic1 = lv_meter_add_needle_img(meter1, scaleAFR, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic1, 14);

 /* Add TPS needle */
 lv_meter_indicator_t * indic2;
 indic2 = lv_meter_add_needle_img(meter1, scaleTPS, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic2, 100);

 /* Add VOLT needle */
 lv_meter_indicator_t * indic3;
 indic3 = lv_meter_add_needle_img(meter1, scaleVOLT, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic3, 12);

 /* Add MAP needle */
 lv_meter_indicator_t * indic4;
 indic4 = lv_meter_add_needle_img(meter1, scaleMAP, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic4, 0);
  
 // Screen 2 Clock
 LV_IMG_DECLARE(clockFace);
 screen2 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen2, 480, 480);
 lv_obj_align(screen2, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_img_src(screen2, &clockFace, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter2 = lv_meter_create(screen2);  // Create the meter
 lv_obj_center(meter2);  // Center it on the screen
 lv_obj_remove_style(meter2, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter2, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter2, 480, 480);

 /*Create a scale for the minutes*/
 /*61 ticks in a 360 degrees range (the last and the first line overlaps)*/
 lv_meter_scale_t * scale_min = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_min, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_min, 0, 60, 360, 270);

 /*Create an other scale for the hours. It's only visual and contains only major ticks*/
 lv_meter_scale_t * scale_hour = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_hour, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_hour, 1, 12, 330, 300);       /*[1..12] values in an almost full circle*/

 LV_IMG_DECLARE(minute)
 LV_IMG_DECLARE(hour)

 /*Add a the hands from images*/
 indic_min = lv_meter_add_needle_img(meter2, scale_min, &minute, -2, 11);
 indic_hour = lv_meter_add_needle_img(meter2, scale_min, &hour, -2, 11);
 lv_meter_set_indicator_value(meter2, indic_min, minValue);
 lv_meter_set_indicator_value(meter2, indic_hour, hourValue);


 /*Create an animation to set the value*/
 lv_anim_t a;
 lv_anim_init(&a);
 lv_anim_set_values(&a, 0, 60);
 lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
 lv_anim_set_time(&a, 2000);     /*2 sec for 1 turn of the minute hand (1 hour)*/
 lv_anim_set_var(&a, indic_min);
 lv_anim_start(&a);

 lv_anim_set_var(&a, indic_hour);
 lv_anim_set_time(&a, 24000);    /*24 sec for 1 turn of the hour hand*/
 lv_anim_set_values(&a, 0, 60);
 lv_anim_start(&a);

 // Screen 3 metrics
 screen3 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen3, 480, 480);
 lv_obj_align(screen3, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_color(screen3,lv_color_black(),LV_PART_MAIN);

}

void setup() {
  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  //while(!Serial);
  Serial.println("911 Gauge");

 #ifdef GFX_EXTRA_PRE_INIT
  GFX_EXTRA_PRE_INIT();
 #endif

 // Init Display
  if (!gfx->begin()) {
    Serial.println("gfx->begin() failed!");
  }
  gfx->fillScreen(RED);

 #ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
 #endif

  lv_init();

  screenWidth = gfx->width();
  screenHeight = gfx->height();

 #ifdef DIRECT_MODE
  bufSize = screenWidth * screenHeight;
 #else
  bufSize = screenWidth * 40;
 #endif

 #ifdef ESP32
  disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  if (!disp_draw_buf) {
    // remove MALLOC_CAP_INTERNAL flag try again
    disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_8BIT);
  }
 #else
  disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * bufSize);
 #endif
  if (!disp_draw_buf) {
    Serial.println("LVGL disp_draw_buf allocate failed!");
  } else {
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, bufSize);

    /* Initialize the display */
    lv_disp_drv_init(&disp_drv);
    /* Change the following line to your display resolution */
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
 #ifdef DIRECT_MODE
    disp_drv.direct_mode = true;
 #endif
  lv_disp_drv_register(&disp_drv);

  /* 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);

 
 
 // Set up buttons
 // pinMode(hourButton, INPUT_PULLUP);
 pinMode(minuteButton, INPUT_PULLUP);
 pinMode(screenButton, INPUT_PULLUP);

 // initialize the RTC
 //rtc.init();

 // Create the two screens
 create_screens();

 // Set screen1 as the initial screen
 lv_scr_load(screen1);

 Serial.println("Setup done");
  }
}

void uitasks()
{
 // read the buttons 
 minuteButtonState = digitalRead(minuteButton);
 screenButtonState = digitalRead(screenButton);

 // Handle minute button presses 
 if (minuteButtonState == LOW){
   //Ds1302::DateTime dt;
   //rtc.setDateTime(&dt);
   //minValue = dt.minute;
   minValue = (minValue + 1) % 60;
   hourValue = (hourValue + .084);
   Serial.println(hourValue);
   if (hourValue > 60.48) {
   hourValue = 0;
 }
   lv_meter_set_indicator_value(meter2, indic_min, minValue);
   lv_meter_set_indicator_value(meter2, indic_hour, hourValue);
 }

 //Change screens
 if (screenButtonState == LOW){
   delay(200);
   if(lv_scr_act() == screen1) lv_scr_load(screen2);
   else lv_scr_load(screen1);
  Serial.println("Screen Button Pressed");
 }

 // get the current time
 //  Ds1302::DateTime now;
 //  rtc.getDateTime(&now);
 //  if (now.hour > 12) { //convert 24 hour clock to 12 hour clock
 //     twelveHour = now.hour-12;
 //  }
 //  lv_meter_set_indicator_value(meter2, indic_min, now.minute);
 //  lv_meter_set_indicator_value(meter2, indic_hour, twelveHour);

}
 
void loop() {
 lv_timer_handler(); /* let the GUI do its work */

 delay(5);
 looper5++;
 if((looper5%10) == 0) uitasks();  
 
 // Automatically switch screens every 5 seconds
 //  if (millis() - last_switch_time > 5000) {
 //    switch_screens();
 //    last_switch_time = millis();
 //  }

 #ifdef DIRECT_MODE
 #if (LV_COLOR_16_SWAP != 0)
  gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
 #else
  gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
 #endif
 #endif  // #ifdef DIRECT_MODE

 #ifdef CANVAS
  gfx->flush();
 #endif
}


If I place the below code into uitasks() the screen changes without hanging or stalling like it should… So there’s something wrong with the button press and how LVGL is receiving it?

  // Automatically switch screens every 5 seconds - This works flawlessly.  
  if (millis() - last_switch_time > 5000) {
   if(lv_scr_act() == screen1) {
     lv_scr_load(screen2);
   } else {
     lv_scr_load(screen1);
   }    last_switch_time = millis();
  }

Do you have any ‘debounce’ code implemented for your physical buttons? Momentary push buttons and arduino pins can have a repeated flickering state of on/off (chattering), so it could be chattering as you release, and repeatedly calling screen load too fast…for my hardware buttons, I have a delay before I can act on any button again. Somewhere between 250-500ms is usually sufficient - humans trying to press buttons quickly can’t manage more than 4 or 5 a second anyway, unless they’re a gamer with a sensitive mouse button :slight_smile:

uint32_t buttonDebounce = 0;

......
  //Button press code....
  if (millis() < buttonDebounce) {
    return;
  }

  buttonDebounce = millis() + 500;
  buttonAction();

See here - Arduino Button Debounce - more info

Tried to no avail.

Here’s a link to a video of the switch in action:

I even tried using the ezButton library but the same behavior was exhibited.

I’m just stumped

#include <Arduino_GFX_Library.h>
#include <lvgl.h>
//#include <Ds1302.h>

Arduino_DataBus *bus = new Arduino_SWSPI(GFX_NOT_DEFINED, 42, 2, 1, GFX_NOT_DEFINED);
Arduino_ESP32RGBPanel *rgbpanel = new Arduino_ESP32RGBPanel(
  40 /* DE */, 39 /* VSYNC */, 38 /* HSYNC */, 41 /* PCLK */,
  5 /* R0 */, 45 /* R1 */, 48 /* R2 */, 47 /* R3 */, 21 /* R4 */,
  14 /* G0 */, 13 /* G1 */, 12 /* G2 */, 11 /* G3 */, 10 /* G4 */, 9 /* G5 */,
  46 /* B0 */, 3 /* B1 */, 8 /* B2 */, 18 /* B3 */, 17 /* B4 */,
  1 /* hsync_polarity */, 22 /* hsync_front_porch */, 24 /* hsync_pulse_width */, 6 /* hsync_back_porch */,
  1 /* vsync_polarity */, 16 /* vsync_front_porch */, 4 /* vsync_pulse_width */, 10 /* vsync_back_porch */);


Arduino_RGB_Display *gfx = new Arduino_RGB_Display(
  480 /* width */, 480 /* height */, rgbpanel, 0 /* rotation */, true /* auto_flush */,
  bus, GFX_NOT_DEFINED /* RST */, st7701_type6_init_operations, sizeof(st7701_type6_init_operations));


/* Change to your screen resolution */
static uint32_t screenWidth;
static uint32_t screenHeight;
static uint32_t bufSize;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *disp_draw_buf;
static lv_disp_drv_t disp_drv;

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
#ifndef DIRECT_MODE
  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
#endif  // #ifndef DIRECT_MODE

  lv_disp_flush_ready(disp);
}

// DS1302 RTC instance
//Ds1302 rtc(15, 16, 17); //RTC module pins

// Button definitions
const int minuteButton = 17;int minuteButtonState = 0; //int prevMinuteState = HIGH;
const int screenButton = 40;int screenButtonState = 0; //int prevScreenState = HIGH;

// Screens
static lv_obj_t *screen1; //gauge
static lv_obj_t *screen2; //clock
static lv_obj_t *screen3; //metrics

// Meters
static lv_obj_t *meter1; //gauge
static lv_obj_t *meter2; //clock

// Metrics Labels
lv_obj_t *labelAFR; 
lv_obj_t *labelTPS; 
lv_obj_t *labelMAP; 
lv_obj_t *labelVOLT; 
lv_obj_t *labelCLT; 
lv_obj_t *labelRPM; 
lv_obj_t *labelBARO;
lv_obj_t *labelAIRCOR; 
lv_obj_t *labelWARMCOR; 
lv_obj_t *labelBAROCOR; 

//Clock hands
lv_meter_indicator_t * indic_min;
lv_meter_indicator_t * indic_hour;

//Clock time values
int minValue = 0;
float hourValue = 0.0;

// Time tracking
unsigned long last_switch_time = 0;
int current_screen = 0;

int twelveHour = 0;

uint32_t looper5;

uint32_t buttonDebounce = 0;


void create_screens() {
 // Screen 1 Gauge
 LV_IMG_DECLARE(face);
 screen1 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen1, 480, 480);
 lv_obj_align(screen1, LV_ALIGN_CENTER, 0, 0);

 //Swap gauge faces during nightime?
 // Ds1302::DateTime now;
 //  rtc.getDateTime(&now);
 //  if (now.hour > 12) {
 //   12Hour = now.hour-12;
 //   if (12Hour > 6) {
 //     LV_IMG_DECLARE(face);
 //   } else {
 //     LV_IMG_DECLARE(faceNight); 
 //   }
 //  }

 lv_obj_set_style_bg_img_src(screen1, &face, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter1 = lv_meter_create(screen1);  // Create the meter
 lv_obj_center(meter1);  // Center it on the screen
 lv_obj_remove_style(meter1, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter1, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter1, 480, 480);
  
 LV_IMG_DECLARE(needle);

 /*Add AFR scale */
 lv_meter_scale_t * scaleAFR = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleAFR, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleAFR, 10, 18, 60, 60);

 /*Add TPS scale */
 lv_meter_scale_t * scaleTPS = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleTPS, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleTPS, 0, 100, 60, 330);

 /*Add VOLT scale */
 lv_meter_scale_t * scaleVOLT = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleVOLT, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleVOLT, 9, 15, 60, 240);

  /*Add MAP scale */
 lv_meter_scale_t * scaleMAP = lv_meter_add_scale(meter1);
 lv_meter_set_scale_ticks(meter1, scaleMAP, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter1, scaleMAP, 0, 100, 60, 151);
  
 /* Add AFR needle */
 lv_meter_indicator_t * indic1;
 indic1 = lv_meter_add_needle_img(meter1, scaleAFR, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic1, 14);

 /* Add TPS needle */
 lv_meter_indicator_t * indic2;
 indic2 = lv_meter_add_needle_img(meter1, scaleTPS, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic2, 100);

 /* Add VOLT needle */
 lv_meter_indicator_t * indic3;
 indic3 = lv_meter_add_needle_img(meter1, scaleVOLT, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic3, 12);

 /* Add MAP needle */
 lv_meter_indicator_t * indic4;
 indic4 = lv_meter_add_needle_img(meter1, scaleMAP, &needle, 0, 0);
 lv_meter_set_indicator_value(meter1, indic4, 0);
  
 // Screen 2 Clock
 LV_IMG_DECLARE(clockFace);
 screen2 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen2, 480, 480);
 lv_obj_align(screen2, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_img_src(screen2, &clockFace, LV_PART_MAIN | LV_STATE_DEFAULT);
 meter2 = lv_meter_create(screen2);  // Create the meter
 lv_obj_center(meter2);  // Center it on the screen
 lv_obj_remove_style(meter2, NULL, LV_PART_MAIN);
 lv_obj_remove_style(meter2, NULL, LV_PART_INDICATOR);
 lv_obj_set_size(meter2, 480, 480);

 /*Create a scale for the minutes*/
 /*61 ticks in a 360 degrees range (the last and the first line overlaps)*/
 lv_meter_scale_t * scale_min = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_min, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_min, 0, 60, 360, 270);

 /*Create an other scale for the hours. It's only visual and contains only major ticks*/
 lv_meter_scale_t * scale_hour = lv_meter_add_scale(meter2);
 lv_meter_set_scale_ticks(meter2, scale_hour, 0, 0, 0, lv_color_black());
 lv_meter_set_scale_range(meter2, scale_hour, 1, 12, 330, 300);       /*[1..12] values in an almost full circle*/

 LV_IMG_DECLARE(minute)
 LV_IMG_DECLARE(hour)

 /*Add a the hands from images*/
 indic_min = lv_meter_add_needle_img(meter2, scale_min, &minute, -2, 11);
 indic_hour = lv_meter_add_needle_img(meter2, scale_min, &hour, -2, 11);
 lv_meter_set_indicator_value(meter2, indic_min, minValue);
 lv_meter_set_indicator_value(meter2, indic_hour, hourValue);


 /*Create an animation to set the value*/
 lv_anim_t a;
 lv_anim_init(&a);
 lv_anim_set_values(&a, 0, 60);
 lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
 lv_anim_set_time(&a, 2000);     /*2 sec for 1 turn of the minute hand (1 hour)*/
 lv_anim_set_var(&a, indic_min);
 lv_anim_start(&a);

 lv_anim_set_var(&a, indic_hour);
 lv_anim_set_time(&a, 24000);    /*24 sec for 1 turn of the hour hand*/
 lv_anim_set_values(&a, 0, 60);
 lv_anim_start(&a);

 // Screen 3 metrics
 screen3 = lv_obj_create(NULL);  // Create a blank 
 lv_obj_set_size(screen3, 480, 480);
 lv_obj_align(screen3, LV_ALIGN_CENTER, 0, 0);
 lv_obj_set_style_bg_color(screen3,lv_color_black(),LV_PART_MAIN);

}

void setup() {
  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  //while(!Serial);
  Serial.println("911 Gauge");

 #ifdef GFX_EXTRA_PRE_INIT
  GFX_EXTRA_PRE_INIT();
 #endif

 // Init Display
  if (!gfx->begin()) {
    Serial.println("gfx->begin() failed!");
  }
  gfx->fillScreen(RED);

 #ifdef GFX_BL
  pinMode(GFX_BL, OUTPUT);
  digitalWrite(GFX_BL, HIGH);
 #endif

  lv_init();

  screenWidth = gfx->width();
  screenHeight = gfx->height();

 #ifdef DIRECT_MODE
  bufSize = screenWidth * screenHeight;
 #else
  bufSize = screenWidth * 40;
 #endif

 #ifdef ESP32
  disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  if (!disp_draw_buf) {
    // remove MALLOC_CAP_INTERNAL flag try again
    disp_draw_buf = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * bufSize, MALLOC_CAP_8BIT);
  }
 #else
  disp_draw_buf = (lv_color_t *)malloc(sizeof(lv_color_t) * bufSize);
 #endif
  if (!disp_draw_buf) {
    Serial.println("LVGL disp_draw_buf allocate failed!");
  } else {
    lv_disp_draw_buf_init(&draw_buf, disp_draw_buf, NULL, bufSize);

    /* Initialize the display */
    lv_disp_drv_init(&disp_drv);
    /* Change the following line to your display resolution */
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
 #ifdef DIRECT_MODE
    disp_drv.direct_mode = true;
 #endif
  lv_disp_drv_register(&disp_drv);

  /* 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);

 
 
 // Set up buttons
 // pinMode(hourButton, INPUT_PULLUP);
 pinMode(minuteButton, INPUT_PULLUP);
 pinMode(screenButton, INPUT_PULLUP);

 // initialize the RTC
 //rtc.init();

 // Create the two screens
 create_screens();

 // Set screen1 as the initial screen
 lv_scr_load(screen1);

 Serial.println("Setup done");
  }
}

void changeScreens () {
   if(lv_scr_act() == screen1) {
     lv_scr_load(screen2);
   } else {
     lv_scr_load(screen1);
   }
  Serial.println("Screen Button Pressed");
}

void uitasks()
{
 // read the buttons 
 minuteButtonState = digitalRead(minuteButton);
 screenButtonState = digitalRead(screenButton);

 // Handle minute button presses 
 if (minuteButtonState == LOW){
   //Ds1302::DateTime dt;
   //rtc.setDateTime(&dt);
   //minValue = dt.minute;
   minValue = (minValue + 1) % 60;
   hourValue = (hourValue + .084);
   Serial.println(hourValue);
   if (hourValue > 60.48) {
   hourValue = 0;
 }
   lv_meter_set_indicator_value(meter2, indic_min, minValue);
   lv_meter_set_indicator_value(meter2, indic_hour, hourValue);
 }

 //Change screens
 if (screenButtonState == LOW){
  if (millis() < buttonDebounce) {
    return;
  }
  buttonDebounce = millis() + 1000;
  changeScreens();
 }

  // Automatically switch screens every 5 seconds. This works perfectly
  // if (millis() - last_switch_time > 5000) {
  //  if(lv_scr_act() == screen1) {
  //    lv_scr_load(screen2);
  //  } else {
  //    lv_scr_load(screen1);
  //  }    last_switch_time = millis();
  // }

 // get the current time
 //  Ds1302::DateTime now;
 //  rtc.getDateTime(&now);
 //  if (now.hour > 12) { //convert 24 hour clock to 12 hour clock
 //     twelveHour = now.hour-12;
 //  }
 //  lv_meter_set_indicator_value(meter2, indic_min, now.minute);
 //  lv_meter_set_indicator_value(meter2, indic_hour, twelveHour);

}
 
void loop() {
 lv_timer_handler(); /* let the GUI do its work */

 delay(5);
 looper5++;
 if((looper5%10) == 0) uitasks();  

//  #ifdef DIRECT_MODE
//  #if (LV_COLOR_16_SWAP != 0)
//   gfx->draw16bitBeRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
//  #else
//   gfx->draw16bitRGBBitmap(0, 0, (uint16_t *)disp_draw_buf, screenWidth, screenHeight);
//  #endif
//  #endif  // #ifdef DIRECT_MODE

 #ifdef CANVAS
  gfx->flush();
 #endif
}


I initially mis-read the debounce code, but I think it looks OK. That is odd. Add some serial prints before you call to changeScreens and inside it, too, see what is happening. Also, add LV_EVENT_SCREEN_LOADED and LV_EVENT_SCREEN_UNLOADED events to both screens, stick some prints in there too, to see what is happening. You could add a screenLoading bool as an additional guard, just to see what is happened. Sort of:


bool screenLoading = false;
....
lv_obj_add_event_cb(screen1, scr_loaded_event_cb, LV_EVENT_SCREEN_LOADED, NULL);
lv_obj_add_event_cb(screen2, scr_loaded_event_cb, LV_EVENT_SCREEN_LOADED, NULL);
....
void scr_loaded_event_cb(lv_event_t * e)
{
  lv_event_code_t event = lv_event_get_code(e);

  if (event == LV_EVENT_SCREEN_LOADED) {
    screenLoading = false;
    Serial.println("Screen has loaded!");
  }
}

void changeScreens () {
   if (screenLoading == true) {
     Serial.println("Screen loading - REJECTED!");
     return;
   }
   screenLoading = true;
   if(lv_scr_act() == screen1) {
     lv_scr_load(screen2);
   } else {
     lv_scr_load(screen1);
   }
  Serial.println("Screen Button Pressed");
}

Also, just try a loop printing the state of your button and press it, and see what it reports, to get a sense of if it’s chattering or not, and how much. Some buttons are better than others…of course, this could also be nothing to do with your buttons, just a path to exhaust first :slight_smile:

my example code set button checks every 50ms, you can try bigger.
But better is make button handling more robust = analyse press and release. For example

void uitasks()
{
static bool pressed=false;
 // read the buttons in lv doc too you can use indev buttons and events
 minuteButtonState = digitalRead(minuteButton);
 screenButtonState = digitalRead(screenButton);
if (screenButtonState != LOW) pressed=false;

 // Handle minute button presses when you hold button this isnt good ... indev events need timing and invalidate screen in increaseMinute is too not required
 if (minuteButtonState == LOW){
   increaseMinute();
 }

if(pressed) return;
 //Change screens
 if (screenButtonState == LOW){
   if(lv_scr_act() == screen1) lv_scr_load(screen2);
   else lv_scr_load(screen1);
  Serial.println("Screen Button Pressed");
  pressed=true;
 }
}

but maybe here is too other trouble in flush, gfx or other code… How version 8 used?

Well, I stripped everything down, rebuilt the code form the ground up and had no success.

I then tried the same code on another ESP32 dev board and display and had no problems.

I went through and tried it with each pin on the problematic dev board and found that some GPIO pins created some type of disturbance with the display and others didn’t.

I eventually found a pin that worked.

So, there’s some internal issue with this dev board. I’v emailed Waveshare to see their thoughts.

Thank you to everyone who worked through this with me. I really appreciated the help and I’ve learned a lot through the troubleshooting process. Thank you again and take care (until the next problem :wink: )

1 Like