Description
I have two image sources on my EPS32 SPIFFS (both binary files converted with the online tool).
I’d like to start my device with one image displayed and then after a certain event, change the image’s source to display the other image.
I can start the device displaying either image, but when I try to switch to the other in the Arduino main loop, my device crashes.
When\how is the proper time to call lv_img_set_src(img, src)? Can I call this in the main loop?
What MCU/Processor/Board and compiler are you using?
Generic ESP32 Dev module, Arduino on PlatformIO
What LVGL version are you using?
What do you want to achieve?
Change an image’s source during a specific event (at a certain time, button click, etc.)
What have you tried so far?
I’ve tried creating a basic script that loads an image from SPIFFS and displays it at startup. As a test, after 20 I want to change the image to the other one.
Code to reproduce
main.cpp
#include <Arduino.h>
#include <SPIFFS.h>
#include <SPI.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include "touch.h"
#include "FileSystem.h"
#define BUFFER_MULTIPLIER 35
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
static lv_disp_buf_t disp_buf;
static lv_color_t buf_1[LV_HOR_RES_MAX * BUFFER_MULTIPLIER];
static lv_color_t buf_2[LV_HOR_RES_MAX * BUFFER_MULTIPLIER];
lv_obj_t * drinkLabel;
#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char * file, uint32_t line, const char * dsc)
{
Serial.printf("%s@%d->%s\r\n", file, line, dsc);
Serial.flush();
}
#endif
/* 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);
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 slider_event_cb(lv_obj_t * slider, lv_event_t event)
{
// lv_obj_t *scr = lv_scr_act();
// scr.
printEvent("Slider", event);
if(event == LV_EVENT_VALUE_CHANGED) {
static char char_buf[4]; /* max 3 bytes for number plus 1 null terminating byte */
snprintf(char_buf, 4, "%u", lv_slider_get_value(slider));
lv_label_set_text(slider_label, char_buf); /*Refresh the text*/
int16_t value = lv_slider_get_value(slider);
value = map(value, 0, 100, 0, 255);
}
}
void setup()
{
Serial.begin(115200); /* prepare for possible serial debug */
lv_init();
init_file_system_driver();
#if USE_LV_LOG != 0
lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif
// TFT initialization ===========================
tft.begin();
tft.setRotation(0);
// ===============================================
// Touch device initialization =================
if (!touch.begin(150)) {
Serial.println("Couldn't start FT6206 touchscreen controller");
while (1);
} else {
Serial.println("FT6206 touchscreen controller connected!");
}
// ===============================================
lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_HOR_RES_MAX * 10);
/*Initialize the display*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = tft.width();
disp_drv.ver_res = tft.height();
disp_drv.flush_cb = my_disp_flush;
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
drinkLabel = lv_img_create(lv_scr_act(), NULL);
lv_img_set_src(drinkLabel, "S:image01.bin");
lv_obj_align(drinkLabel, NULL, LV_ALIGN_IN_TOP_LEFT, 35, 95);
}
int changed = 0;
void loop()
{
if (millis() > 20000) {
if (changed == 0) {
changed = 1;
lv_img_set_src(drinkLabel, "S:image02.bin");
}
}
lv_task_handler(); /* let the GUI do its work */
delay(1);
}
FileSystem.h
#include <Arduino.h>
#include <SPIFFS.h>
#include <lvgl.h>
/* Create a type to store the required data about your file.*/
typedef File lv_spiffs_file_t;
typedef File lv_spiffs_dir_t;
lv_fs_res_t open_SPIFFS_file(struct _lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
{
(void) drv; /*Unused*/
char filename[32];
snprintf_P(filename, sizeof(filename), PSTR("/%s"), path);
File file = SPIFFS.open(filename, mode == LV_FS_MODE_WR ? FILE_WRITE : FILE_READ);
if(!file) {
return LV_FS_RES_NOT_EX;
} else if(file.isDirectory()) {
return LV_FS_RES_UNKNOWN;
} else {
lv_spiffs_file_t * fp = (lv_spiffs_file_t *)file_p; /*Just avoid the confusing casings*/
if (fp != NULL) (*fp) = file;
return LV_FS_RES_OK;
}
}
lv_fs_res_t read_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
(void) drv; /*Unused*/
lv_spiffs_file_t *fp = (lv_spiffs_file_t *)file_p;
lv_spiffs_file_t file = *fp;
if (!file) {
return LV_FS_RES_NOT_EX;
} else {
*br = (uint32_t)file.readBytes((char *)buf, btr);
return LV_FS_RES_OK;
}
}
lv_fs_res_t close_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p)
{
(void) drv; /*Unused*/
lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;
if (!file) {
return LV_FS_RES_NOT_EX;
} else if(file.isDirectory()) {
return LV_FS_RES_UNKNOWN;
} else {
file.close();
return LV_FS_RES_OK;
}
}
lv_fs_res_t seek_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t pos)
{
(void) drv; /*Unused*/
lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;
if (!file) {
return LV_FS_RES_NOT_EX;
} else {
file.seek(pos, SeekSet);
return LV_FS_RES_OK;
}
}
lv_fs_res_t tell_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p)
{
(void) drv; /*Unused*/
lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;
if (!file) {
return LV_FS_RES_NOT_EX;
} else {
*pos_p = (uint32_t)file.position();
return LV_FS_RES_OK;
}
}
bool init_file_system_driver()
{
lv_fs_drv_t drv;
lv_fs_drv_init(&drv); /*Basic initialization*/
drv.letter = 'S'; /*An uppercase letter to identify the drive */
drv.file_size = sizeof(lv_spiffs_file_t); /*Size required to store a file object*/
drv.rddir_size = sizeof(lv_spiffs_dir_t); /*Size required to store a directory object (used by dir_open/close/read)*/
drv.ready_cb = NULL; //my_ready_cb; /*Callback to tell if the drive is ready to use */
drv.open_cb = open_SPIFFS_file; /*Callback to open a file */
drv.close_cb = close_SPIFFS_file; /*Callback to close a file */
drv.read_cb = read_SPIFFS_file; /*Callback to read a file */
drv.write_cb = NULL; //my_write_cb; /*Callback to write a file */
drv.seek_cb = seek_SPIFFS_file; /*Callback to seek in a file (Move cursor) */
drv.tell_cb = tell_SPIFFS_file; /*Callback to tell the cursor position */
drv.trunc_cb = NULL; //my_trunc_cb; /*Callback to delete a file */
drv.size_cb = NULL; //my_size_cb; /*Callback to tell a file's size */
drv.rename_cb = NULL; //my_rename_cb; /*Callback to rename a file */
drv.dir_open_cb = NULL; //my_dir_open_cb; /*Callback to open directory to read its content */
drv.dir_read_cb = NULL; //my_dir_read_cb; /*Callback to read a directory's content */
drv.dir_close_cb = NULL; //my_dir_close_cb; /*Callback to close a directory */
drv.free_space_cb = NULL; //my_free_space_cb; /*Callback to tell free space on the drive */
// drv.user_data = my_user_data; /*Any custom data if required*/
lv_fs_drv_register(&drv); /*Finally register the drive*/
if (!SPIFFS.begin(true)) {
Serial.println("An Error has occurred while mounting SPIFFS");
return false;
}
return true;
}
touch.h
#include <Adafruit_FT6206.h>
#include <lvgl.h>
int oldTouchX = 0;
int oldTouchY = 0;
static Adafruit_FT6206 touch = Adafruit_FT6206();
unsigned long last_user_input = 0;
bool tft_is_off = false;
bool tft_needs_on = false;
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
uint16_t touchX, touchY;
if (touch.touched())
{
// First wake up the screen if its shut off
if (tft_is_off) {
tft_is_off = false;
tft_needs_on = true;
}
// Retrieve a point
TS_Point p = touch.getPoint();
touchX = p.y;
touchY = p.x;
touchY = 320-touchY;
// rotate coordinate system
// flip it around to match the screen.
uint16_t swapped_touchX = touchY;
touchY = touchX;
touchX = swapped_touchX;
touchX = map(touchX, 0, 320, 320, 0);
// touchY = map(touchY, 0, 320, 320, 0);
// if ((touchX != oldTouchX) || (touchY != oldTouchY))
// {
oldTouchY = touchY;
oldTouchX = touchX;
data->state = LV_INDEV_STATE_PR;
// data->state = touched ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
data->point.x = touchX;
data->point.y = touchY;
last_user_input = millis();
Serial.print("X: ");
Serial.print(touchX);
Serial.print(" Y: ");
Serial.println(touchY);
// }
} else {
data->point.x = oldTouchX;
data->point.y = oldTouchY;
data->state =LV_INDEV_STATE_REL;
}
return 0;
}