Cách hiển thị GIF biểu cảm trên ESP32 bằng LVGL
Hướng dẫn ngắn để đưa GIF biểu cảm vào firmware ESP32: chuyển GIF thành asset, bật LV_USE_GIF, chọn lưu trong flash hoặc filesystem và hiển thị bằng lv_gif.
Chia sẻ

Trong bài này, mình sẽ cùng bạn đi từ khâu chuẩn bị GIF đến lúc kiểm tra bộ nhớ và hiển thị animation ổn định trên ESP32.
GIF là một cách nhanh để làm giao diện nhúng có cảm xúc hơn: một khuôn mặt vui, mắt chớp, trạng thái đang nghĩ, hoặc phản hồi nhỏ khi robot nhận lệnh. Với ESP32 và LVGL, phần khó không nằm ở việc gọi widget, mà là chuẩn bị asset đúng cách và không làm firmware thiếu RAM.
Bài này ghi lại luồng làm việc gọn: chuẩn bị GIF, bật decoder, chọn nơi lưu, rồi hiển thị bằng lv_gif.
Luồng tổng quát
Với một GIF biểu cảm, bạn thường có hai cách đưa vào firmware:
- Chuyển GIF thành C array rồi build thẳng vào app.
- Giữ file
.giftrong filesystem, ví dụ SPIFFS, LittleFS hoặc thẻ SD, rồi đưa đường dẫn cho LVGL.
Cách đầu đơn giản khi số lượng animation ít và muốn mọi thứ nằm trong binary. Cách thứ hai hợp hơn khi có nhiều biểu cảm, muốn thay asset mà không build lại toàn bộ firmware, hoặc muốn chia bộ GIF theo pack.
Bật GIF trong LVGL
Trong lv_conf.h, bật GIF widget:
#define LV_USE_GIF 1
Theo tài liệu LVGL, lv_gif dùng GIF decoder để phát GIF động. Sau khi bật cấu hình này, bạn có thể tạo widget bằng lv_gif_create() và đặt nguồn bằng lv_gif_set_src().
Cách 1: nhúng GIF vào flash dưới dạng C array
Nếu muốn GIF đi cùng firmware, hãy chuyển file .gif thành asset C. Với LVGL, hướng phổ biến là dùng script converter của LVGL và để color format ở dạng raw:
python ./scripts/LVGLImage.py --cf RAW --ofmt C -o . --name mochi_smile mochi-smile.gif
Sau đó khai báo và hiển thị:
LV_IMAGE_DECLARE(mochi_smile);
void show_mochi_smile(void)
{
lv_obj_t * gif = lv_gif_create(lv_screen_active());
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
lv_gif_set_src(gif, &mochi_smile);
lv_obj_center(gif);
}
Nếu GIF có nền trong suốt, LV_COLOR_FORMAT_ARGB8888 giữ alpha tốt hơn, nhưng tốn RAM hơn. Với nhiều màn TFT RGB565, dùng LV_COLOR_FORMAT_RGB565 có thể giảm bước chuyển đổi màu và nhẹ hơn cho ESP32. Nên gọi lv_gif_set_color_format() trước lv_gif_set_src() để tránh cấp phát framebuffer mặc định rồi lại đổi sau.
Cách 2: lưu GIF trong filesystem
Khi GIF nằm trong SPIFFS, LittleFS hoặc SD card, bạn truyền đường dẫn file cho lv_gif_set_src():
void show_mochi_eyes(void)
{
lv_obj_t * gif = lv_gif_create(lv_screen_active());
lv_gif_set_color_format(gif, LV_COLOR_FORMAT_RGB565);
lv_gif_set_src(gif, "S:/emoji/mochi-eyes.gif");
lv_obj_center(gif);
}
Điểm quan trọng là LVGL phải có filesystem driver tương ứng. Nếu driver chưa được đăng ký, widget sẽ không mở được file dù đường dẫn nhìn có vẻ đúng. Với ESP-IDF, phần này thường nằm ở lớp mount partition và bridge sang lv_fs_drv.
RAM là phần cần tính trước
GIF động không chỉ là vài byte ảnh trong flash. LVGL cần RAM cho decoder và framebuffer của frame hiện tại. Tài liệu LVGL ghi chú công thức gần đúng là khoảng 25 kB cộng thêm (pixel_size + 1) x width x height byte.
Ví dụ một GIF 128x128:
- RGB565: khoảng
25 kB + 3 x 128 x 128, tức gần 74 kB. - ARGB8888: khoảng
25 kB + 5 x 128 x 128, tức hơn 106 kB.
Con số thực tế còn phụ thuộc cấu hình LVGL, heap, driver màn hình và task khác trong firmware. Nhưng nó đủ để nhắc một điều: với ESP32 nhỏ, đừng bắt đầu bằng GIF quá lớn.
Gợi ý chuẩn bị GIF biểu cảm
Một pipeline đơn giản cho robot face UI:
- Thiết kế hoặc chọn GIF ở kích thước đúng với vùng hiển thị, ví dụ 128x128 hoặc 240x240.
- Giảm số frame nếu animation không cần quá mượt.
- Giảm số màu nếu phong cách hình ảnh cho phép.
- Test từng GIF riêng trước khi load cả bộ biểu cảm.
- Đặt tên file theo trạng thái, ví dụ
idle.gif,happy.gif,thinking.gif,sleepy.gif.
Nếu đang thử giao diện cho Mochi hoặc robot nhỏ, bạn có thể bắt đầu bằng các bộ emoji GIF đang chia sẻ trên Nastrotek như Smile 3D Emoji GIF Pack, Eyes Emoji GIF Pack, Pepe Emoji GIF Pack, hoặc Shiba GIF Pack.
Một ví dụ đổi biểu cảm
Thay vì tạo nhiều widget GIF chồng lên nhau, bạn có thể giữ một widget và đổi source theo trạng thái:
static lv_obj_t * face_gif;
void face_ui_create(void)
{
face_gif = lv_gif_create(lv_screen_active());
lv_gif_set_color_format(face_gif, LV_COLOR_FORMAT_RGB565);
lv_obj_center(face_gif);
}
void face_ui_set_idle(void)
{
lv_gif_set_src(face_gif, "S:/emoji/idle.gif");
}
void face_ui_set_happy(void)
{
lv_gif_set_src(face_gif, "S:/emoji/happy.gif");
}
Với asset C array, ý tưởng tương tự, chỉ khác source là con trỏ tới image descriptor thay vì đường dẫn file.
Lỗi dễ gặp
- Quên bật
LV_USE_GIF. - Dùng file path nhưng chưa đăng ký filesystem driver cho LVGL.
- GIF quá lớn so với RAM còn lại.
- Dùng ARGB8888 cho mọi trường hợp dù màn hình không cần alpha.
- Đổi source liên tục trong nhiều task khác nhau mà không đồng bộ với thread/task LVGL.
- GIF có kích thước lớn hơn giới hạn
LV_GIF_MAX_WIDTHhoặcLV_GIF_MAX_HEIGHTtronglv_conf.h.
Kết luận
Để hiển thị GIF biểu cảm trên ESP32 bằng LVGL, hãy bắt đầu nhỏ: bật LV_USE_GIF, thử một GIF 128x128, chọn giữa C array và filesystem, rồi đo RAM trước khi mở rộng thành nhiều biểu cảm. Khi pipeline đã ổn, các bộ Emoji trên Nastrotek có thể trở thành nguồn asset nhanh để thử nghiệm robot face UI mà không phải tự vẽ mọi thứ từ đầu.
Tham khảo
Chia sẻ
Tiếp tục khám phá
Đọc tiếp
Bài viết liên quan
GIF, PNG, JPG hay ảnh C Array: Định dạng nào phù hợp với màn hình ESP32?
So sánh GIF, PNG, JPG và ảnh C array cho màn hình ESP32 theo flash, RAM, tốc độ giải mã, alpha, animation và chất lượng.
Thiết kế hệ thống trạng thái cảm xúc cho robot Mochi
Xây dựng state machine cảm xúc cho Robot Mochi: idle, listening, talking, happy, sad, thinking, error và animation dễ thay thế.
Embedded UI là gì, và vì sao quan trọng
Embedded UI nghĩa là gì với thiết bị nhỏ, vì sao màn hình nhỏ có ngân sách nhỏ, và vì sao nó xứng đáng được xem như một mảng riêng.