This project was designed to be built into an old wooden box to make a magical video player for a 3 year-old child's favourite program, Bagpuss.
The basic version has been working for the last year and is much loved, but simply shows all thirteen episodes in a completely random order. This new project adds touchscreen controls allowing the child to choose which of the episodes to play.
The JC3248W535 is a great little ESP32-S3 powered board that includes a touch screen, audio amplifier, SD card slot and battery management — all in one self-contained unit at a very reasonable price. Here I describe how I managed to create an AVI player with a touch screen UI using the new JC3248W535-Driver and the ArduinoGFX library.
The JC3248W535 uses an AXS15231 chip to control the display and the touchscreen. It is notoriously difficult to get the touch working with the available drivers.
The JC3248W535 is designed to be operated in portrait mode (320 x 480 pixels), but for this project I wanted to show a TV-like display, i.e. landscape mode (480 x 320 pixels). Rotating the AVI 90 degrees during playback is possible but drastically reduces the framerate achievable by the ESP32S3. The solution is to rotate the source file 90 degrees during the conversion process, allowing the processor to display a 320x480 video at full-speed.
The project was inspired by @moononournation's remarkable work on the Arduino_GFX library and his AVI player example, which demonstrated that smooth video playback on an ESP32 is possible. Another project by thelastoutpostworkshop used a different device but provided the inspiration for the player controls. The touch driver is provided by the excellent JC3248W535-Driver repository by me-processware, whose README documentation is itself worth reading.
In the Boards menu, install and select version 3.0.2 of "esp32" by Espressif Systems.
Install version 1.4.9 of "GFX Library for Arduino" by Moon On Our Nation.
The following libraries must be installed manually:
- https://github.com/pschatzmann/arduino-libhelix
- https://github.com/arionik/avilib
- https://github.com/esp-arduino-libs/ESP32_JPEG
- https://github.com/me-processware/JC3248W535-Driver
- https://github.com/olikraus/u8g2
Under Tools, select the following settings:
| Setting | Value |
|---|---|
| Board | ESP32S3 Dev Module |
| Port | Your board's COM port |
| USB CDC On Boot | Enabled |
| Flash Mode | QIO 120 MHz |
| Flash Size | 16MB (128Mb) |
| Partition Scheme | 16M Flash (3MB APP/9.9MB FATFS) |
| PSRAM | OPI PSRAM |
Note: PSRAM is required for the canvas frame buffer. The sketch will print an error on startup if PSRAM is not correctly enabled.
You will need to convert your videos using free software to avi files that will play on this device.
I used a 2-stage video file conversion process.
- Step 1. Use the free HandBrake program to scale your source video file to 480 pixels by 320 pixels AND rotate it 90 degrees clockwise.
- Step 2. Use ffmpeg as described by @moononournation to convert to the Cinepak-compatible AVI file.
Copy your AVI files to a folder called /avi320x480 on the SD card. The player expects files in 320x480 portrait format.
These are the problems encountered during development that cost the most time to resolve. Documenting them here may save others significant debugging effort.
The touch driver included in the ArduinoGFX library does not work with the AXS15231B controller on this board. This was the primary motivation for this project. The solution is the JC3248W535-Driver by me-processware, which works correctly out of the box.
Without this fix, video colours will appear severely distorted — greens, purples and cyans instead of natural colours. If you see distorted colours, change the commented/uncommented state of the Big Endian pixel flag in cinepak.h at line 25, to :
// Change this:
// #define BIG_ENDIAN_PIXEL
// To this:
#define BIG_ENDIAN_PIXEL
or vice versa.This project uses an Arduino_Canvas buffer (required for the QSPI interface), which means:
- All drawing goes to the canvas buffer first
display.flush()must be called to push the canvas to the physical displayoutput_bufmust be obtained viagfx->getFramebuffer()on the canvas, not the display driver- The
avi_draw()function must callflush()to display the contents of the screen buffer.
When centring text on screen, getTextBounds() measures text at the current text size. If setTextSize() is called after getTextBounds(), the measurement will be wrong and the text will appear off-centre. Always set the text size first:
gfx->setTextSize(3); // Set size FIRST
gfx->getTextBounds(text, 0, 0, &x1, &y1, &textW, &textH); // THEN measure
int titleX = (regionW - textW) / 2 - x1;
int titleY = regionY + (regionH - textH) / 2 - y1;The correct formula to centre text both horizontally and vertically within a defined region, accounting for the x1/y1 offsets returned by getTextBounds(), is:
int titleX = (REGION_WIDTH - textW) / 2 - x1;
int titleY = REGION_Y + (REGION_HEIGHT - textH) / 2 - y1;Note: y1 from getTextBounds() is typically a small negative number representing the ascender height above the baseline. Including - y1 gives true visual centring.
This project stands on the shoulders of others' generous open source contributions:
- Volos Projects — YouTube channel that introduced many people (including this author) to ESP32 development
- @moononournation — Author of the Arduino_GFX library and the AVI player example that forms the foundation of this project
- me-processware — Author of the JC3248W535 touch driver that made this project possible, and whose README documentation sets a high standard
- The Last Outpost Workshop - Designed a simple UI operated video player for the JC4827W543 and showed what was possible.
MIT