ESP32-S3 + 2.8 吋 TFT LCD 顯示JPG圖檔(LovyanGFX)
發揮 ESP32-S3 N16R8 的硬體優勢,免外接 SD 卡!透過 LittleFS 將 JPG 圖片直接存入 Flash,並利用 8MB PSRAM 快取,搭配 LovyanGFX 在螢幕上快速讀取圖片並輪播。
上一篇教學我們成功讓 ESP32-S3 使用 LovyanGFX 在 2.8 吋螢幕上顯示了繁體中文。許多朋友馬上接著問傑森:「如果我想在畫面上放自己的專案 Logo 或是全彩照片,該怎麼做?」



方法一:將照片轉成 C 語言陣列(寫死在程式碼裡)
這是創客界最常拿來測試、也是最不需要更動硬體的方法。做法是透過轉檔軟體(例如 Image2LCD 或是網頁版轉換器),把 JPG 或 PNG 照片直接翻譯成成千上萬個代表顏色的 16 進位色碼(例如 0xFFFF 代表白色),變成一個 .h 標頭檔。
- 優點:
- 最簡單、免接線: 不需要增加任何額外模組,程式碼燒進去照片就出來了。
- 顯示速度極快: 因為資料已經解碼並直接存在晶片的 Flash 記憶體裡,LovyanGFX 只要用一行
display.pushImage()就能瞬間把畫面刷出來。
- 缺點:
- 極度消耗記憶體: 一張 240x320 解析度的照片,大約會吃掉 150KB 的程式空間。ESP32 的空間通常只有 4MB 到 16MB,放沒幾張高畫質照片,程式庫就爆滿了。
- 更新麻煩: 每次想換照片,都要重新轉檔、重新編譯燒錄程式。
方法二:存放在 ESP32 內部檔案系統 (SPIFFS / LittleFS)
ESP32 的 Flash 記憶體其實可以切出一塊空間,當成「虛擬的隨身碟」來用。你可以把真正的 .jpg 或 .png 檔案,透過 Arduino IDE 的專屬外掛工具,直接上傳到 ESP32 的肚子裡。
- 優點:
- 免接外掛模組: 一樣不需要額外買 SD 卡模組,硬體保持最精簡。
- 管理直覺: 你面對的是真正的圖檔,而不是一堆看不懂的十六進位代碼。LovyanGFX 支援直接讀取檔案,例如使用
display.drawJpgFile()。而且jpg圖檔很省空間,240x320 解析度的照片,一張30-50k,ESP32你可以放一堆圖檔了!
- 缺點:
- 需要安裝上傳工具: Arduino IDE 原本不支援直接上傳檔案到 ESP32 內部,你必須先安裝「ESP32 Sketch Data Upload」或 LittleFS 的外掛。
- 空間依然受限: 通常能切出來當檔案系統的空間還是有限制,適合放幾張介面 UI 圖片或圖示,無法做大型電子相框。但ESP32-S3 N16R8就不同了,有了16M的大容量,可以放不少jpg圖囉!
方法三:讀取外部 SD 卡(外接 MicroSD 模組)
這就是真正的「電子相框」做法。你需要另外準備一個 MicroSD 卡模組,接上 ESP32,然後把電腦裡的照片全部複製到 SD 卡裡面。
- 優點:
- 容量幾乎無限: 隨便一張 8GB 或 16GB 的 SD 卡,可以放幾千、幾萬張照片。
- 更新超方便: 想換照片時,只要把 SD 卡拔下來插進電腦複製貼上就好,完全不用動到 ESP32 的程式碼。
- 缺點:
- 硬體變複雜: 你需要額外買 SD 卡模組,而且它也是走 SPI 通訊,代表你又要多接 4 到 6 根線(CS, MOSI, MISO, SCK)。這意味著它必須跟你的螢幕、觸控晶片一起「三方共用」匯流排,硬體除錯難度會再稍微提升。
- 顯示速度稍慢: ESP32 必須先從 SD 卡把 JPG 檔案讀出來,經過 CPU 運算解碼,再丟給螢幕,所以圖片太大的話,畫面載入會有一點點「由上往下刷」的感覺。
總結來說: 「方法一」是網路最多人用的,但每次要轉檔,傑森是蠻不喜歡的啦!從SD卡也是麻煩,還要自己再焊線,而且讀取慢半拍。所以如果你想做數位照片輪播,或是有其它快速顯示的需求,那絕對要選「方法三」。
所以大家猜到了,這篇進階教學傑森要帶大家徹底發揮 ESP32-S3 N16R8 的強大硬體實力,透過 LittleFS 將圖片存入大容量 Flash,再利用 8MB PSRAM 達成極速讀取與顯示。
核心觀念:為什麼需要 LittleFS 與 PSRAM?
- LittleFS (Flash 儲存): ESP32-S3 斷電後記憶體資料會消失,所以我們必須先用外掛工具,把 JPG 圖片像存進硬碟一樣,燒錄到 ESP32-S3 內建的 Flash 空間中。
- PSRAM (高速快取): 如果每次畫圖都從 Flash 讀取,速度會比較慢。N16R8 版本最棒的就是有 8MB 的超大 PSRAM。我們可以在開機時,把圖片從 Flash 讀出來並「常駐」在 PSRAM 裡,之後要顯示或切換圖片時,速度就會快到飛起來!
準備工作
1. 安裝 LittleFS Data Uploader 工具
這個工具不會內建在 Arduino IDE 中,需要額外安裝。
- Arduino IDE 2.x 版: 請至 GitHub 搜尋
arduino-littlefs-upload下載.vsix檔,並放入磁碟/使用者/(名稱)/.arduinoIDE/plugins資料夾中。安裝後可按Ctrl + Shift + P呼叫指令區使用。 - 如果沒有
plugins資料夾,就自行建立。



- 準備圖片: 在你的 Arduino 專案資料夾下,建立一個名為
data的資料夾。把準備好的圖片(例如image1.jpg、image2.jpg,建議尺寸 240x320)放進去。

2. 最關鍵的一步:自訂分區表 (Partition Scheme)
ESP32-S3 N16R8 有高達 16MB 的空間,但預設的分割方式可能沒有留空間給 LittleFS,直接上傳會遇到 Partition entry not found in csv file! 的錯誤。
傑森的終極解法: 在專案資料夾中(與 .ino 檔同層),建立一個名為 partitions.csv 的純文字檔,貼上以下內容:
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
app1, app, ota_1, 0x310000,0x300000,
spiffs, data, spiffs, 0x610000,0x9F0000,
(這段設定保留了 OTA 更新功能,並給了 app 3MB,剩下的近 10MB 全部分配給 spiffs 也就是你的 LittleFS。如果你要做其它的分區,簡單,叫Gemini幫你寫新的語法就行了^^)

接著回到 Arduino IDE:
工具 (Tools)->Partition Scheme-> 選擇 Custom 的選項(IDE 會優先讀取你剛建立的 csv 檔)。- 然後編譯(或是直接上傳)一次,讓設定生效。
- 執行 LittleFS Data Uploader,按住組合鍵 Ctrl+Shift+p,在輸入框內打upload,就能找到LittleFS Data Uploader,點它就能成功把圖片上傳到開發板了!


範例一:載入單張 JPG 到 PSRAM 並顯示
圖片上傳成功後,我們來寫程式。別忘了在 IDE 的 工具 -> PSRAM 中選擇 OPI PSRAM!
這個範例會示範如何配置 PSRAM 記憶體,並把圖片畫在螢幕上。
#include <LovyanGFX.hpp>
#include <LittleFS.h>
// ===== 這裡請貼上與上一篇完全相同的 class LGFX 腳位設定 =====
// (包含 SPI2_HOST, SCK=8, MOSI=18, CS=15 等設定,為了版面簡潔此處省略)
// ==========================================================
LGFX display;
// 宣告指標與大小,用來存放 PSRAM 的位址與圖片大小
uint8_t* img_buffer = NULL;
size_t img_size = 0;
void setup() {
Serial.begin(115200);
delay(500);
display.init();
display.setRotation(1); // 橫向顯示
display.fillScreen(TFT_BLACK);
// 1. 初始化 LittleFS
if(!LittleFS.begin(true)){
Serial.println("LittleFS 掛載失敗");
return;
}
// 2. 檢查 PSRAM
if (!psramFound()) {
Serial.println("警告:未找到 PSRAM!");
return;
}
// 3. 開啟圖片檔案
File file = LittleFS.open("/image1.jpg", "r");
if(!file){
Serial.println("開啟圖片失敗");
return;
}
img_size = file.size();
// 4. 在 PSRAM 中要一塊記憶體空間
img_buffer = (uint8_t*)ps_malloc(img_size);
if (img_buffer != NULL) {
// 5. 將圖片資料讀取進 PSRAM
file.read(img_buffer, img_size);
Serial.println("圖片成功載入 PSRAM!");
}
file.close();
// 6. 畫出 PSRAM 中的圖片 (指標, 大小, X, Y)
if (img_buffer != NULL) {
display.drawJpg(img_buffer, img_size, 0, 0);
}
}
void loop() { }

範例二:3 張圖片極速輪播 (Carousel)
如果你要做一個數位相框或 UI 介面,我們可以在開機時把 3 張圖片全部塞進大容量的 PSRAM 裡,之後在 loop() 中就能無延遲地切換!
#include <LovyanGFX.hpp>
#include <LittleFS.h>
// ===== 請貼上完整的 class LGFX 腳位設定 =====
// 警告:如果你複製程式碼時漏掉腳位設定,螢幕會變成全白哦!
LGFX display;
const int IMG_COUNT = 3;
uint8_t* img_buffers[IMG_COUNT];
size_t img_sizes[IMG_COUNT];
String file_names[IMG_COUNT] = {"/image1.jpg", "/image2.jpg", "/image3.jpg"};
void setup() {
Serial.begin(115200);
display.init();
display.setRotation(1);
display.fillScreen(TFT_BLACK);
LittleFS.begin(true);
// 用迴圈將 3 張圖片全部載入 PSRAM
for (int i = 0; i < IMG_COUNT; i++) {
File file = LittleFS.open(file_names[i], "r");
if(file){
img_sizes[i] = file.size();
img_buffers[i] = (uint8_t*)ps_malloc(img_sizes[i]);
if (img_buffers[i] != NULL) {
file.read(img_buffers[i], img_sizes[i]);
Serial.printf("成功載入 %s 至 PSRAM\n", file_names[i].c_str());
}
file.close();
}
}
}
int current_img = 0;
void loop() {
if (img_buffers[current_img] != NULL) {
display.drawJpg(img_buffers[current_img], img_sizes[current_img], 0, 0);
}
current_img++;
if (current_img >= IMG_COUNT) {
current_img = 0;
}
delay(3000); // 每 3 秒切換一張
}
學會了顯示圖片,你的 ESP32-S3 專案視覺效果就能大幅提升啦!如果有任何問題,歡迎到我們粉絲團留言,我們下篇教學見!


