Can I change an image source in the main loop?

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

That should work. Do you know where it’s crashing specifically?

Its happening in this line in the main loop:

lv_img_set_src(drinkLabel, "S:image02.bin");

But I’m pretty sure the problem is not changing the source, but the fact that I’m opening a second file using the File System that I implemented. I’m going take this topic back to my other post from yesterday and talk about it there.

I’ve done a few tests including changing around which images are opened first and second, when they’re opened (in setup or in loop). After trying to create two images in the setup, it crashed there as well, on the second image, which is why I think its the file system.

OK, so I might take back my last comment.
In an attempt to put together some content for a post on the other thread, I created smaller images for a test and now the image is swapping between the two as expected.

So now maybe I’m running into a memory issue caused by loading the second image? Here is the error message from the crash:

Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x40108922  PS      : 0x00060930  A0      : 0x800d1b5e  A1      : 0x3ffb1e40
A2      : 0xcccccccc  A3      : 0x3ffcb1d8  A4      : 0x3ffb1e7c  A5      : 0x3f405a1b
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x80108a80  A9      : 0x3ffb1e20
A10     : 0x00000000  A11     : 0x00000000  A12     : 0x00000000  A13     : 0x00000000
A14     : 0x3ffb1e7c  A15     : 0x00000001  SAR     : 0x00000008  EXCCAUSE: 0x0000001c
EXCVADDR: 0xccccccd0  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0xffffffff

Backtrace: 0x40108922:0x3ffb1e40 0x400d1b5b:0x3ffb1e60 0x400df0f1:0x3ffb1ec0 0x400dd437:0x3ffb1ef0 0x400ddc0f:0x3ffb1f20 0x400e4ddf:0x3ffb1f40 0x400d1a8e:0x3ffb1f90 0x400e8825:0x3ffb1fb0 0x40088e39:0x3ffb1fd0        

Rebooting...

Is there any way to see how much memory is being used at any one time? Or maybe how much memory a single image would take up once loaded?

I’m going to try to use the same images, only smaller and see if that helps.

I removed one of my lvgl display buffers (I was using a double buffer) and shrank the image sizes of the two that were giving me trouble. After doing so, I was able to get the two images to swap as expected, but once a third swap happened, it crashed the same way.

I assume I’m running out of memory and every time I load a new image off of the SPIFFS, its using more memory even if its the same image as what I loaded the first time. So every file load using lv_img_set_src(label1, “S:image01.bin”); needs its own memory.

At this point, I’ll probably need to rework my implementation so that I’m only ever taking up the memory for one image. If that means somehow releasing the memory from the old image and temporarily displaying a string message “Please Wait…” where the first image was displayed while the second image loads into memory, then that is fine by me.

I’ll need an implementation that supports:

  • Only one image will ever be displayed at a time.
  • I’ll need to swap the image at times, but they will never be the same two images. Each image could be one of many downloaded from a service I’ll be creating to support the product.
  • The images will always be the same width & height in pixels.

I’ll update any progress when I make some. But in the meantime, any suggestions or ideas is appreciated.

Running out of memory should fail gracefully, not cause a crash. It sounds like something is configured incorrectly.

Here is my sketch, in case somebody can spot an implementation problem:

Question: When I call lv_img_set_src() does it remove the previous image data from memory? Could this be where its failing there is no memory left?

#include <Arduino.h>
#include <SPIFFS.h>
#include <SPI.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <Adafruit_FT6206.h>


#define BUFFER_MULTIPLIER 20

lv_obj_t *label1;
lv_obj_t *label2;

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];

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






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






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





void setup()
{

    Serial.begin(115200); /* prepare for possible serial debug */

    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);

    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, NULL, 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);

    // The drink label
    label1 = lv_img_create(lv_scr_act(), NULL);
    lv_obj_align(label1, NULL, LV_ALIGN_IN_TOP_LEFT, 35, 95);
    lv_img_set_src(label1, "S:image01.bin"); 

    // label2 = lv_img_create(lv_scr_act(), NULL);
    // lv_obj_align(label2, NULL, LV_ALIGN_IN_TOP_LEFT, 50, 125);;
    // lv_img_set_src(label2, "S:image02.bin");
}

int changed = 0;
unsigned long last_swap = 0;
int swap_time = 5000;

void loop()
{
    if (millis() - last_swap > swap_time) {
        last_swap = millis();

        if (changed == 0) {
            lv_img_set_src(label1, "S:image02.bin"); 
            changed = 1;
        } else if (changed == 1) {
            lv_img_set_src(label1, "S:image01.bin"); 
            changed = 0;
        }
    }

    lv_task_handler(); /* let the GUI do its work */
    delay(1);
}

Not sure if this helps, but have you tried to set #define LV_IMG_CACHE_DEF_SIZE 1 to a higher value?

That could work, though it would cause images to stay in memory instead of being removed, which seems contrary to what @jc78 wants.

You could also try 0 (which would turn off caching entirely).