Can't show a jpg file in sd card while fatfs works fine

Description

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

esp32s3 with arduino

What LVGL version are you using?

v8.3

What do you want to achieve?

Show a jpg or sjpg img saved on sd card

What have you tried so far?

I have tired the code below to check lvgl can read the file inside my sd card, result is output “Open OK”

void lv_fs_test(void)
{
	lv_fs_file_t f;
	lv_fs_res_t res;
	res = lv_fs_open(&f, "S:readme.txt", LV_FS_MODE_RD);
	if(res != LV_FS_RES_OK) 
	{
		Serial.println("Open fail");
	}
	else
	{
		Serial.println("Open OK");
	}
}

In lv_conf.h:

/* JPG + split JPG decoder library.
 * Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 1

/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
#define LV_USE_FS_FATFS  1
#if LV_USE_FS_FATFS
    #define LV_FS_FATFS_LETTER 'S'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
    #define LV_FS_FATFS_CACHE_SIZE 0   /*>0 to cache this number of bytes in lv_fs_read()*/
#endif

Code to reproduce

void lvglSetup(){
  // Init Display
  gfx->begin();
                   
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);

  gfx->fillScreen(RED);
  delay(100);
  gfx->fillScreen(GREEN);
  delay(100);
  gfx->fillScreen(BLUE);
  delay(100);
  gfx->fillScreen(BLACK);
  delay(50);
  
  lv_init();
  delay(10);

  screenWidth = gfx->width();
  screenHeight = gfx->height();
  
  disp_draw_buf1 = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 8, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  disp_draw_buf2 = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 8, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, disp_draw_buf2, screenWidth * screenHeight / 8);

  /* 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;
  lv_disp_drv_register(&disp_drv);

  lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x000000),LV_STATE_DEFAULT );
  lv_obj_set_style_bg_opa(lv_scr_act(), 255, LV_STATE_DEFAULT);

  Serial.println("Setup done");
}

After the initialization of display, lvgl works cause the CPU usage and FPS count are shown.
In setup (SD card initialization omitted due to insignificance.):

void setup() {
  lvglSetup();
  lv_fs_fatfs_init();
  lv_fs_test();
  lv_obj_t *img= lv_img_create(lv_scr_act());
  lv_split_jpeg_init();
  lv_img_set_src(img, "S:icons/bilibili.sjpg");
  lv_obj_set_style_bg_opa(img, 255, LV_STATE_DEFAULT);
  lv_obj_align(img, LV_ALIGN_CENTER, 0, 0); 
  Serial.println("Icon showed");
}

Result till now

CPU usage and FPS count are shown → lvgl runs success
lv_fs_test() fuction give positive result → lvgl can read file inside my sd card

I don’t know what to do next? No idea where i missed, cause LV_SYMBOL can be shown

lv_img_set_src(img, LV_SYMBOL_OK);

A few days ago, I was able to open and display JPG format images from an SD card on my screen using LVGL 8.3. The images were directly output from Photoshop without any special processing, and their size matched the resolution of my screen.
I used the following code to set the image source and invalidate the object at the appropriate time:

···
lv_img_set_src(img, “S:/images/wallpaper/0.jpg”);
lv_obj_invalidate(img);
···

I remember making some adjustments to the cache configuration in lv_conf.h, but I have made too many adjustments recently and can’t remember all of them.

Now that I have upgraded LVGL to version 9.0, I am unable to open the JPG images anymore, which is frustrating.

Found where’s the problem, the test img i used is jpeg at the beginning. I saved it as jpg in windows img browser then output a sjpg without a warning. So i guess there is something wrong with jpg_to_sjpg.py

Good luck to you, my problem is solved

Dear James1976,
Could you please share the code based on LVGL 8.3 please. I am trying to get the done for my Arduino Giga R1 but without luck so far. And just to make sure : you are reading the raw JPG file into your code, or do you also perform some kind of postprocessing?
really interested, Regards

My friend, I achieved it step by step.

  1. Make sure your SD card is working correctly.
  2. Ensure that fs, LV_USE_PNG, and LV_USE_SJPG configurations are enabled in lv_conf.h.
  3. Check if fs can read files correctly. (Confirm that the fs system is functioning properly)
  4. Check if lvgl can display image arrays. (Confirm that your screen and lvgl are working fine)
  5. Try displaying a smaller PNG image from the SD card. I used a size of 32x32.
  6. Attempt to open and display a JPG image.

I have switched to LVGL9 and encountered many problems as well. Below is my code snippet for testing. It can display PNG images from the SD card, but not JPG images.
My problem.


#include <Arduino.h>
#include <SPI.h>
#include <lvgl.h>
#include <TFT_eSPI.h>
#include "SD.h"
#include "FS.h"

static TFT_eSPI tft;
static lv_display_t *disp_1;
static uint8_t draw_buf_1[320 * 240 / 10];
static lv_obj_t *ui = nullptr;
static ulong lvgl_tick_millis = millis();

void display_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
    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((uint16_t *)px_map, w * h, true);
    tft.endWrite();
    lv_display_flush_ready(disp);
}

void setup()
{
    delay(3000);
    Serial.begin(115200);
    delay(100);
    Serial.println("Initializing...");

    SPI.begin(12, 13, 11);
    if (!SD.begin(10, SPI, 27000000))
    {
        Serial.println("SD card failed!");
    }
    else
    {
        Serial.println("SD card success!");
    }
    ledcSetup(4, 5000, 8);
    ledcAttachPin(3, 4);
    ledcWrite(4, 20);

    tft.init();

    tft.setRotation(3);
    tft.fillScreen(TFT_BLACK);

    lv_init();
    lv_fs_fatfs_init();

    disp_1 = lv_display_create(320, 240);
    lv_display_set_flush_cb(disp_1, display_flush);
    lv_display_set_buffers(disp_1, draw_buf_1, NULL, sizeof(draw_buf_1), LV_DISPLAY_RENDER_MODE_PARTIAL);

    ui = lv_obj_create(NULL);
    lv_obj_set_size(ui, 320, 240);
    lv_obj_set_style_bg_opa(ui, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(ui, lv_color_hex(0x0e0e0e), LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_screen_load(ui);

    Serial.println("lv_image_create img_1");
    lv_obj_t *img_1 = lv_image_create(ui);
    lv_obj_set_size(img_1, 32, 32);
    lv_obj_set_pos(img_1, 130, 70);
    lv_image_set_src(img_1, "S:/images/clock-min.png");

    Serial.println("lv_image_create img_2");
    lv_obj_t *img_2 = lv_image_create(ui);
    lv_obj_set_size(img_2, 105, 33);
    lv_obj_set_pos(img_2, 0, 0);
    lv_image_set_src(img_2, "S:/images/wallpaper/img_lvgl_logo.jpg");

    delay(1000);
    Serial.println("Refresh the screen.");
    lv_obj_invalidate(lv_screen_active());

}

void loop()
{
    lv_timer_handler();
    unsigned long tick_millis = millis() - lvgl_tick_millis;
    lvgl_tick_millis = millis();
    lv_tick_inc(tick_millis);
    yield();
    delay(5);
}

Thank you so much. The interesting thing is that you mention that with LVGL 8.3 you were able to display JPG images. That is exactly what I want to do. I can not upgrade to LVGL 9.0 because apparently Arduino R1 and its Giga display does not support that “upgrade”. According to the 8.3 documentation [Images — LVGL documentation] its says : LVGL doesn’t directly support, however, generic image formats like PNG or JPG.
But in your post you dont seem to mention that you have postprcessed the JPG file. You appear to have read it in directly from he SD card). Which is exactly what I need. Could you please elaborate on that a bit more. Thanks

I am very certain that with LVGL 8.3, I was able to display JPG images from an SD card through the fs module. In the LVGL 8.3 documentation, it is mentioned that the source of the image can be a file, like in this case.

Store images
You can store images in two places
as a variable in internal memory (RAM or ROM)
as a file

I remember trying smaller images first to see if they could be opened (provided you are already able to display PNG images from the SD card). Then I adjusted a certain cache configuration in lv_conf.h, and I was able to open JPG images that matched my screen resolution.

Once I solve the issue with displaying JPG images in LVGL 9.0, if you haven’t figured it out by then, I’ll try to reproduce it again using LVGL 8.3. :smile:

Hi,
I am still trying to get this to work with version 8.3 on my Arduino G1. I went back to the original demo LVGLDemo which contains this part of the code

  LV_IMG_DECLARE(img_arduinologo);
  
  lv_obj_t * img1 = lv_img_create(obj);
  lv_obj_set_size(img1, 32, 32);
    lv_obj_set_pos(img1, 130, 70);
  //lv_img_set_src(img1, &img_arduinologo); //This one displays perfectly on the screen
  lv_img_set_src(img1, "S:/LOGO.PNG"); // tried different calls S:LOGO.PNG S:/LOGO.PNG  etc

The Arduino logo ( a binary array) is drawn perfectly. Now I changed that to reading from my SD card. I have checked the file, it is a PNG file (checking the binary contain) and it is very small 365 bytes. I have tried different calls for S:/LOGO.PNG. All to no avail. My settings in the config file are these `
/*File system interfaces for common APIs */

/API for fopen, fread, etc/
#define LV_USE_FS_STDIO 1
#if LV_USE_FS_STDIO
#define LV_FS_STDIO_LETTER ‘S’ /Set an upper cased letter on which the drive will accessible (e.g. ‘A’)/
#define LV_FS_STDIO_PATH “” /Set the working directory. File/directory paths will be appended to it./
#define LV_FS_STDIO_CACHE_SIZE 512 />0 to cache this number of bytes in lv_fs_read()/
#endif

/API for open, read, etc/
#define LV_USE_FS_POSIX 1
#if LV_USE_FS_POSIX
#define LV_FS_POSIX_LETTER ‘S’ /Set an upper cased letter on which the drive will accessible (e.g. ‘A’)/
#define LV_FS_POSIX_PATH “” /Set the working directory. File/directory paths will be appended to it./
#define LV_FS_POSIX_CACHE_SIZE 512 />0 to cache this number of bytes in lv_fs_read()/
#endif

/API for CreateFile, ReadFile, etc/
#define LV_USE_FS_WIN32 1
#if LV_USE_FS_WIN32
#define LV_FS_WIN32_LETTER ‘S’ /Set an upper cased letter on which the drive will accessible (e.g. ‘A’)/
#define LV_FS_WIN32_PATH “” /Set the working directory. File/directory paths will be appended to it./
#define LV_FS_WIN32_CACHE_SIZE 512 />0 to cache this number of bytes in lv_fs_read()/
#endif

/API for FATFS (needs to be added separately). Uses f_open, f_read, etc/
#define LV_USE_FS_FATFS 1
#if LV_USE_FS_FATFS
#define LV_FS_FATFS_LETTER ‘S’ /Set an upper cased letter on which the drive will accessible (e.g. ‘A’)/
#define LV_FS_FATFS_CACHE_SIZE 512 />0 to cache this number of bytes in lv_fs_read()/
#endif

/API for LittleFS (library needs to be added separately). Uses lfs_file_open, lfs_file_read, etc/
#define LV_USE_FS_LITTLEFS 1
#if LV_USE_FS_LITTLEFS
#define LV_FS_LITTLEFS_LETTER ‘S’ /Set an upper cased letter on which the drive will accessible (e.g. ‘A’)/
#define LV_FS_LITTLEFS_CACHE_SIZE 512 />0 to cache this number of bytes in lv_fs_read()/
#endif

/PNG decoder library/
#define LV_USE_PNG 1
`

I am really running out of options here for something that should not be complicated. I hope to get some help.