[solved] Beginner Q: how to read sensor value without display RESET

Sorry, I’m a 60 year old trying to enjoy the new possibilities using LVGL.
I have made some great and useful projects but LVGL is very different.
So bare with me please… :wink:

Using Arduino IDE 2.3.3 / LVGL 9.1 with TFT_eSPI

What do you want to achieve?

Read sensor data without the screen resetting itself: turning black and reset. If I selected for example tab 4, it wil go black and go to tab 1

What have you tried so far?

spent many hours/days searching for a solution… I’m learning by studying code from others but I cloud not find anything which would help me out.

Code to reproduce

This is only the LOOP() code but I thing it’s enough to make the point. If not please let me know.

If I implement it this way, every 5 seconds, the display will reset
For the sensor, an Adafruit BME280, I’m using the Adafruit libraries

void loop() {
  lv_tick_inc(millis() - lastTick);  //Update the tick timer. Tick is new for LVGL 9
  lastTick = millis();
  lv_timer_handler();  //Update the UI
  delay(5);

  if (millis() - timerTemp > 5000) {
    timerTemp = millis();
    // Get the latest temperature reading in Celsius or Fahrenheit
    float bme_temperature = bme.readTemperature();
  }
}

Welcome to LVGL world :slight_smile:

There isn’t quite enough information here to correctly diagnose, but the way you describe it, it sounds the same as if the call to bme.readTemperature is actually crashing the device, causing it to reset and start over?

Can you add some diagnostic prints in your code, print to serial in your setup() function, print to serial before and after the call to bme.readTemperature, etc. and then monitor the console output? If you see the print in setup() after the screen goes black, or don’t see the print after the call to bme.readTemperature, that’s indicative of that failing.

Remember to add Serial.flush() after your console prints, to ensure the in-flight data is sent before moving on - many a message is lost in flight due to hard crashes, making it appear that it wasn’t sent, when it was, thus confusing the diagnostics.

Also, your lv_tick_inc / lv_timer_handler calls aren’t safe as setup, but that probably isn’t the cause of your issue here. See:

Clarification about lv_timer_handler documentation

1 Like

Hi,

I must admit I have multiple displays around the house reading from various temp sensors and never had an issue like that.

However I am only on 8.x as I’ve not been brave enough to start migrating my code to v9.

In all of my code bases I’ve always called the temp update from within a LVGL task:

  void tsk_temps(lv_timer_t * task)
  {
    if(config.debuglevel > 6 && debug)
    {
        Serial.print(" Temps Task Start");
        Serial.println();
    }
    temp_sensor.requestTemperatures();
    lastLocaltemp = temp_sensor.getTempCByIndex(0);
    //publishNewTemp = true;

    if(config.debuglevel > 6 && debug)
    {
      Serial.println("Checking Local Temps");
    }

    if(lastLocaltemp != -127.00 && lastLocaltemp != 127.00 && lastLocaltemp != 85.00)
    {
      char buff[6];
      dtostrf(lastLocaltemp, 5, 2, buff);
      String tempString = buff;

      if(config.debuglevel > 6 && debug)
      {
        Serial.print("tempString ");
        Serial.println(tempString);
      }

      char strColour[7];
      if(config.debuglevel > 6 && debug)
      {
        Serial.println("Get Colour For Temp");
      }
      getColourForTemp(strColour, (double)lastLocaltemp);

      if(config.debuglevel > 6 && debug)
      {
        Serial.print("strColour ");
        Serial.println(strColour);
        Serial.println("Set Local Temp Label");
      }
      lv_label_set_text_fmt(footer.lbl_temp, "#%s %s°c#", strColour, tempString.c_str());

      publishNewTemp = true;
    }
    if(config.debuglevel > 6 && debug)
    {
        Serial.print(" Temps Task End");
        Serial.println();
    }
  }

Then initialise it in your setup:

    Serial.println("Create Temp Timer");
    lv_timer_t * task_tempCheck = lv_timer_create(tsk_temps, 60 * 1000, NULL);
    Serial.println("Execute Local Temp Timer");
    lv_timer_ready(task_tempCheck);
    Serial.println("Local Temp Timer Complete");

I find this method a lot easier than having to manage the timings based on stuff like caching the last timer and using millis…

If that doesnt work then knowing what MCU might help…

Regards

1 Like

Many thanks you for your replies!

To be clear, there is no crashing of the ESP32. I personally think that while the temperature sensor is read, the LVGL “tick” is interrupted and the display goes blank?

Could that be the case? because I read in the provided URL:

Therefore lv_tick_inc needs to be called periodically no matter what.

The UI code was generated by EEZ studio and the callback functions have been manually created.

I also tried to use a lv_timer but that gave the same problem: while reading the sensor value, the screen goes blank and revives after reading:

// Create an LVGL timer to periodically update the temperature label
    lv_timer_create(updateTemperatureLabel, 1000, NULL);  // Call every 1000 ms (1 second)

This is the code but without the other .h files EEZ generated.

#include <lvgl.h>
#include <ui.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>

// Install Adafruit Unified Sensor and Adafruit BME280 Library
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define I2C_SDA 21
#define I2C_SCL 22
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

// Touchscreen pins >> FireBeetle ESP32
#define XPT2046_CLK 14   // T_CLK
#define XPT2046_CS 25    // T_CS
#define XPT2046_MOSI 26  // T_DIN
#define XPT2046_MISO 27  // T_OUT
#define XPT2046_IRQ 9    // T_IRQ

#define LED_RED 4
#define LED_GREEN 16
#define LED_BLUE 17

/*Set to your screen resolution*/
#define TFT_HOR_RES 320
#define TFT_VER_RES 240

unsigned long timerTemp = millis();
float bme_temperature = 0.0;

SPIClass touchscreenSpi = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);

/*LVGL draw into this buffer, 1/10 screen size usually works well. The size is in bytes*/
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))

#if LV_USE_LOG != 0
void my_print(lv_log_level_t level, const char *buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}
#endif


void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
  lv_disp_flush_ready(disp);
}
void my_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data) {
  if (touchscreen.tirqTouched() && touchscreen.touched()) {
    TS_Point p = touchscreen.getPoint();
    data->point.x = map(p.x, 350, 3950, 1, TFT_HOR_RES);
    data->point.y = map(p.y, 240, 3850, 1, TFT_VER_RES);
    data->state = LV_INDEV_STATE_PRESSED;
  } else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

lv_indev_t *indev;      //Touchscreen input device
uint8_t *draw_buf;      //draw_buffer , allocated on heap otherwise the static area is too big on ESP32 at compile
uint32_t lastTick = 0;  //Used to track the tick timer

void setup() {

  //Some basic info on the Serial console
  String LVGL_Arduino = "LVGL demo ";
  LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.begin(115200);
  Serial.println(LVGL_Arduino);

  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);

  //Initialise the touchscreen
  touchscreenSpi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); /* Start second SPI bus for touchscreen */
  touchscreen.begin(touchscreenSpi);                                         /* Touchscreen init */
  touchscreen.setRotation(1);                                                /* Inverted landscape orientation to match screen */

  //Initialise LVGL GUI
  lv_init();

  draw_buf = new uint8_t[DRAW_BUF_SIZE];
  lv_display_t *disp;
  disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, DRAW_BUF_SIZE);

  //Initialize the XPT2046 input device driver
  indev = lv_indev_create();
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  lv_indev_set_read_cb(indev, my_touchpad_read);

  //Done
  Serial.println("LVGL Setup done");

  //Integrate EEZ Studio GUI
  ui_init();

  lv_obj_add_event_cb(objects.text_name, text_name_event_cb, LV_EVENT_ALL, NULL);
  lv_obj_add_event_cb(objects.text_password, text_password_event_cb, LV_EVENT_ALL, NULL);
  lv_obj_add_event_cb(objects.arc_threshold, arc_threshold_event_cb, LV_EVENT_VALUE_CHANGED, objects.arc_threshold_label);
}

//Requires LVGL 9.0+
void loop() {
  lv_tick_inc(millis() - lastTick);  //Update the tick timer. Tick is new for LVGL 9
  lastTick = millis();
  lv_timer_handler();  //Update the UI
  delay(5);

  if (millis() - timerTemp > 5000) {
    timerTemp = millis();
    // Get the latest temperature reading in Celsius or Fahrenheit
    float bme_temperature = bme.readTemperature();
  }
}


//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
static void text_name_event_cb(lv_event_t *e) {
  lv_event_code_t code = lv_event_get_code(e);

  // This line gets the target object of the event, which in this case should be the text_name object.
  lv_obj_t *text_area = (lv_obj_t *)lv_event_get_target(e);

  // Set the LED_RED pin to high and LED_BLUE pin to low
  digitalWrite(LED_RED, 1);
  digitalWrite(LED_BLUE, 0);

  // This conditional block checks if the event is either a click or focus event.
  if (code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) {
    // If a keyboard object exists, this line sets the focus of the keyboard to the text_name text area, allowing the user to enter text.
    if (objects.keyboard != NULL) lv_keyboard_set_textarea(objects.keyboard, objects.text_name);

    // This conditional block checks if the event is a ready event, which typically indicates that the text area has finished being updated.
  } else if (code == LV_EVENT_READY) {
    // This line logs the current text content of the text_name text area to the console using the LV_LOG_USER macro.
    // Button 'Carridge Return' or 'OK' button is clicked
    LV_LOG_USER("NAME: %s", lv_textarea_get_text(objects.text_name));
  }
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
static void text_password_event_cb(lv_event_t *e) {
  lv_event_code_t code = lv_event_get_code(e);
  lv_obj_t *text_area = (lv_obj_t *)lv_event_get_target(e);

  digitalWrite(LED_RED, 0);
  digitalWrite(LED_BLUE, 1);

  if (code == LV_EVENT_CLICKED || code == LV_EVENT_FOCUSED) {
    // Focus on the clicked text area
    if (objects.keyboard != NULL) lv_keyboard_set_textarea(objects.keyboard, objects.text_password);

  } else if (code == LV_EVENT_READY) {

    //char buf[64];
    LV_LOG_USER("PASSWORD: %s", lv_textarea_get_text(objects.text_password));
  }
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
static void arc_threshold_event_cb(lv_event_t *e) {
  lv_obj_t *arcThreshold = (lv_obj_t *)lv_event_get_target(e);
  lv_obj_t *arcLabel = (lv_obj_t *)lv_event_get_user_data(e);

  int arcThresholdValue = lv_arc_get_value(arcThreshold);

  if (arcThresholdValue < 40) {
    lv_obj_set_style_text_color((lv_obj_t *)arcLabel, lv_palette_main(LV_PALETTE_GREEN), 0);
  } else if (arcThresholdValue < 60) {
    lv_obj_set_style_text_color((lv_obj_t *)arcLabel, lv_palette_main(LV_PALETTE_DEEP_ORANGE), 0);
  } else if (arcThresholdValue < 80) {
    lv_obj_set_style_text_color((lv_obj_t *)arcLabel, lv_palette_main(LV_PALETTE_BLUE), 0);
  } else if (arcThresholdValue < 100) {
    lv_obj_set_style_text_color((lv_obj_t *)arcLabel, lv_palette_main(LV_PALETTE_RED), 0);
  }

  /* Explanation:
  lv_label_set_text_fmt:    This function is used to set the text content of an LVGL label object using a formatted string.
  arcLabel:                 This is a pointer to the label object whose text content you want to modify.
  "%d%%":                   This is a format string that specifies how the value should be formatted. 
                            The %d placeholder will be replaced with the value of arcThresholdValue, 
                            and the %% will be replaced with a literal % character.
  arcThresholdValue:        This is the value that will be inserted into the format string */
  lv_label_set_text_fmt(arcLabel, "%d%%", arcThresholdValue);

  // char buffer[8];

  // lv_snprintf(buffer, sizeof(buffer), "%d%%", (int)lv_arc_get_value(arcThreshold));
  // lv_label_set_text(arc_threshold_label, buffer);

  LV_LOG_USER("Slider changed to %d%%", (int)lv_arc_get_value(arcThreshold));
}

What does your TFT_eSPI config/pins look like?

Yes I tried that also! But same result…

I will make a video to show what is happening…

Link to video: https://www.youtube.com/watch?v=7hJ7LKfWZ9I

/* 
  

  FireBeetle ESP32 met IL9241 - Erik



  
Rui Santos & Sara Santos - Random Nerd Tutorials

  Install the "TFT_eSPI" lbirary by Bodmer to interface with the TFT Display - https://github.com/Bodmer/TFT_eSPI
  *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials ***
  *** YOU MUST USE THIS User_Setup.h FILE, CHECK FOR THE LATEST VERSION IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS ***
  https://RandomNerdTutorials.com/cyd/
  https://RandomNerdTutorials.com/esp32-tft/
  FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://RandomNerdTutorials.com/cyd/ or https://RandomNerdTutorials.com/esp32-tft/
*/

//                            USER DEFINED SETTINGS
//   Set driver type, fonts to be loaded, pins used and SPI control method etc
//
//   See the User_Setup_Select.h file if you wish to be able to define multiple
//   setups and then easily select which setup file is used by the compiler.
//
//   If this file is edited correctly then all the library example sketches should
//   run without the need to make any more changes for a particular hardware setup!
//   Note that some sketches are designed for a particular TFT pixel width/height

// User defined information reported by "Read_User_Setup" test & diagnostics example
#define USER_SETUP_INFO "User_Setup"
#define ILI9341_2_DRIVER     // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
#define TFT_WIDTH  240 // ST7789 240 x 240 and 240 x 320
#define TFT_HEIGHT 320 // ST7789 240 x 320

#define TFT_BL   10            // LED back-light control pin
#define TFT_BACKLIGHT_ON HIGH  // Level to turn ON back-light (HIGH or LOW)

// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins

#define TFT_MISO 19 //SDO
#define TFT_MOSI 23 //SDI
#define TFT_SCLK 18 
#define TFT_CS   15  // Chip select control pin
#define TFT_DC    2  // Data Command control pin
//#define TFT_RST   4  // Reset pin (could connect to RST pin)
#define TFT_RST  -1  // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST

#define TOUCH_CS 34 //was 33     // Chip select pin (T_CS) of touch screen

#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT

#define SPI_FREQUENCY  55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// Optional reduced SPI frequency for reading TFT
#define SPI_READ_FREQUENCY  20000000
// The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here:
#define SPI_TOUCH_FREQUENCY  2500000
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
#define USE_HSPI_PORT

That really, really looks like the ESP32 is crashing.

What does your serial output look like?

Did you put any serial debug statements around the call to get temp?

1 Like

YOU ARE RIGHT!

the ESP32 is crashing…

19:27:35.886 -> ets Jun  8 2016 00:22:57
19:27:35.918 -> 
19:27:35.918 -> rst:0xc (SW_CPU_RESET),boot:0x1b (SPI_FAST_FLASH_BOOT)
19:27:35.918 -> configsip: 0, SPIWP:0xee
19:27:35.918 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
19:27:35.918 -> mode:DIO, clock div:1
19:27:35.918 -> load:0x3fff0030,len:4604
19:27:35.918 -> ho 0 tail 12 room 4
19:27:35.918 -> load:0x40078000,len:15488
19:27:35.918 -> load:0x40080400,len:4
19:27:35.918 -> load:0x40080404,len:3180
19:27:35.918 -> entry 0x400805b8
19:27:36.175 -> LVGL demo V9.1.0
19:27:36.207 -> E (16) gpio: GPIO can only be used as input mode
19:27:36.465 -> LVGL Setup done
19:27:41.157 -> Read sensor: Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
19:27:41.189 -> 
19:27:41.189 -> Core  1 register dump:
19:27:41.189 -> PC      : 0x400fcdff  PS      : 0x00060030  A0      : 0x800fce39  A1      : 0x3ffb21a0  
19:27:41.189 -> A2      : 0x00000000  A3      : 0x00000000  A4      : 0x3ffb8368  A5      : 0x00000000  
19:27:41.189 -> A6      : 0x00000000  A7      : 0x00000000  A8      : 0x80088c92  A9      : 0x3ffb2170  
19:27:41.189 -> A10     : 0x3ffc26b0  A11     : 0x00000000  A12     : 0x00000000  A13     : 0x00000001  
19:27:41.223 -> A14     : 0xb33fffff  A15     : 0xb33fffff  SAR     : 0x0000000a  EXCCAUSE: 0x0000001c  
19:27:41.223 -> EXCVADDR: 0x00000000  LBEG    : 0x40086951  LEND    : 0x40086961  LCOUNT  : 0xfffffffc  
19:27:41.223 -> 
19:27:41.223 -> 
19:27:41.223 -> Backtrace: 0x400fcdfc:0x3ffb21a0 0x400fce36:0x3ffb21c0 0x400fce5d:0x3ffb21e0 0x400fcb2d:0x3ffb2200 0x400fcb3d:0x3ffb2230 0x400d24f3:0x3ffb2250 0x40101584:0x3ffb2270 0x4008aa36:0x3ffb2290
19:27:41.223 -> 
19:27:41.223 -> 
19:27:41.223 -> 

Yes I did put serial statements around the temp reading and then I discovered the crashing.

I’ll have to look at the BME280 library…

Thanks for all your replies, my apologies for your lost time due to my stupidity. :woozy_face:

Dont worry about it. We all go awry sometimes…

If you want to understand where exactly it’s crashing you can try this:

It should allow you to view the actual code that caused the crash.

If I were you I would strip out all of the LVGL/TFT_eSPI code and just get the temp sensor to output to the terminal (serial).

Once you get that going successfully then incorporate those changes into your project. Sorry if it sounds like I’m trying to teach you how to suck eggs but this is how I track down issues/implement new sensors, IE going from ds18b20 to the bme range etc.

Hope you have some luck. Keep us updated!

1 Like

Why did you mark elgerg as the solution when he just repeated what I said in the very first response to your post?!

Additionally, LVGL is not actually driving the display to stay illuminated and showing an image, the displays own controller does that, from whatever has been put into the physical displays internal buffer. If you stopped calling lv_tick_inc, the display would just appear with a ‘frozen’ picture with whatever you last send it. The LVGL tick is required so LVGL can internally process the changes to the LVGL components and push out updated data to the physical display to update the image in the displays own frame buffer.

1 Like

sorry, corrected that!
Thank you for explaining more about LVGL, learned a lot by this mess I created. That is how I learn most. Too old to learn programming the right way due to brain damage. I’m not the one I used to be 40 years ago. But still enjoying my hobby.

Again, many thanks to all who took the effort to help me out!

Thank you for this useful option!

But I was blinded, forgot to check the bme280 part completely. It had worked with another sketch so I copied it to this sketch but forgot one line: bme.begin(); :face_with_spiral_eyes:
My head is ‘foggy’ and tired continually so this stupid mistake persisted, could not discover it myself.

As I said in another reply here, I learned a lot!

It’s always one little thing isn’t it?!?

Well, glad you got it sorted!

Regards

1 Like

Greetings all,

If it’s any consolation to wwJd, I just wanted to thank you for not finding the solution to your problem of forgetting to initialize the sensor earlier, because the various posts that followed helped me solve a somewhat different problem. My display was also crashing randomly while reading temp sensor data but I could not find a reliable way to keep the read routine out of the loop. I knew the issue was there.
In Post 3,elgerg suggested the creation of the lv_timers to handle the reading of the sensor data. That approach was just what I needed and it works perfectly. I have literally been struggling with this for weeks and my eyes are now open to this straightforward and simple technique.
My heartfelt thanks to everyone who contributed to this post.