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));
}