Games on the M5Stack Tab5 (ESP32-P4),
built with ESPP. Sibling of
esp-box-emu, with the same layout:
a launcher GUI, a Cart per game / core, and a thin hardware abstraction
layer around the espp board support package.
The first (and main) target is Star Wars: Jedi Knight – Dark Forces II, via OpenJKDF2 (our fork: esp-cpp/OpenJKDF2). See docs/PLAN.md for the port plan and status.
Status: bring-up. The launcher, pause menu, HAL, PPA video path and the full OpenJKDF2 engine (software rasterizer, ESP32 platform layer) compile and link for the ESP32-P4; nothing has run on hardware yet. Turn
CONFIG_JK_ENGINEoff in menuconfig to make the "Jedi Knight" cart show a test pattern instead, for checking the video / input / audio paths.
| SoC | ESP32-P4 (dual RISC-V @ 360 MHz, FPU, PPA 2D accelerator, 32 MB PSRAM) |
| Display | 5" 1280x720 MIPI-DSI (portrait-native, rotated to landscape) |
| Touch | GT911 capacitive |
| Audio | ES8388 codec + NS4150B amp, dual mics |
| Storage | microSD (SDMMC 4-bit) |
| Input | touch (virtual gamepad), BOOT button (pause menu), USB keyboard / mouse / gamepad on the USB-A port (hubs supported) |
| USB-C | serial console, or the SD card as a USB drive (settings > "USB drive") |
main/ app_main, Cart base class, per-game carts
components/
tab5-emu/ HAL (Tab5Emu): display / PPA video path, audio, SD, input
gui/ LVGL launcher (rom list + boxart + settings)
menu/ LVGL in-game pause menu (resume/reset/save/load/quit)
rom_info/ metadata.csv parsing
statistics/ frame timing
jk/ OpenJKDF2 port: engine submodule + ESP32 platform layer + glue
docs/PLAN.md the plan
/sdcard/
metadata.csv # rom_path, boxart_path, name
boxart/jk.jpg # baseline JPEG, ~400x400
jk/ # your Jedi Knight install (copy from CD / GOG)
jk.cd # (any marker file; metadata points here)
episode/JK1.gob JK1CTF.gob JK1MP.gob
resource/Res1hi.gob Res2.gob jk_.cd
resource/video/*.SMK # optional, cutscenes
MUSIC/Track12.wav ... # optional, the soundtrack: run
# tools/convert_music.py <game dir> on the GOG / Steam
# MUSIC folder (IMA ADPCM; the .ogg originals also
# play, but Vorbis decoding costs ~75% of a core)
player/ # saves / settings (created)
saves/ # tab5-emu save-slot screenshots
Example metadata.csv (one line per game: rom path, boxart, name; the
platform comes from the extension: .md/.gen Genesis, .sfc/.smc SNES,
.gba Game Boy Advance, jk.cd Jedi Knight):
jk/jk.cd, boxart/jk.jpg, Star Wars: Jedi Knight - Dark Forces II
genesis/sonic.md, boxart/sonic.jpg, Sonic the Hedgehog
snes/smw.sfc, boxart/smw.jpg, Super Mario World
gba/mk.gba, boxart/mk.jpg, Mario Kart: Super Circuit
A real gba_bios.bin next to a GBA ROM is used when present; otherwise the
open-source BIOS built into gpSP is used.
Requires ESP-IDF v6.x.
git clone --recurse-submodules=components/jk/OpenJKDF2 https://github.com/esp-cpp/tab5-emu
cd tab5-emu
idf.py set-target esp32p4
idf.py build flash monitor
(Do not recurse into the engine's own submodules; they are desktop-only dependencies.)
Touch (virtual gamepad):
- Left third: d-pad (relative to the zone center)
- Right third: A (lower right), B (lower left), X (upper right), Y (upper left)
- Bottom strip of the middle third: SELECT (left) / START (right)
- Top-right corner, or the BOOT button: pause menu
USB keyboard / mouse / gamepad on the USB-A port (directly or through a
hub; the host runs at full speed so hubs work). Devices are decoded from
their HID report descriptors (espp hid-rp report map), so any standard HID
gamepad works: face buttons by position (A = east, B = south, X = north,
Y = west), shoulders / triggers = L / R, select / start (also the Consumer
"back" / "menu" controls of mobile pads), d-pad / hat / left stick = d-pad,
select+start or the home button = pause menu. Known layouts: Xbox-style and
DirectInput / DualShock numbering, Backbone Pro quirks. Keyboards: arrows,
Z/X/A/S, Enter, Shift/Backspace, Q/W map to the gamepad for the emulators.
Jedi Knight gets the full keyboard and mouse look; on a gamepad the left
stick strafes, the right stick looks, R = fire, L = secondary fire, A =
activate, B = jump, X = crouch, Y = use item.
New controllers: the console prints each device's decoded buttons on change
(debug level in usb_hid.cpp); a wrong mapping is a quirk-table entry in
espp's hid-rp-report-map.hpp.
Core dumps go to flash (coredump partition) and are printed base64 on the
console at the next boot, so a crash while the console is unavailable (the
USB drive hand-over) can still be read. Decode with:
base64 -d < dump.b64 > core.bin
python -m esp_coredump --chip esp32p4 info_corefile -t raw -c core.bin build/tab5-emu.elf
Note that a terminal which opens the console port with DTR low and RTS high
resets the chip (rst:0x17 CHIP_USB_UART_RESET); keep both asserted, or
expect a reboot whenever the console re-enumerates (after the USB drive).
Settings > "USB drive" exposes the SD card as a USB mass storage device on the USB-C port, so ROMs, boxart and game data can be copied without pulling the card. While the drive is on the serial console is off (the Tab5's USB-C carries the P4's full-speed USB PHY, which the console and the USB device controller share) and the launcher cannot start games; switch it off (eject first) to get the card, the console and the ROM list back.
MIT (this project). OpenJKDF2 is under its own permissive license; the game data is not included and must come from your own copy of the game.