Version 1.4.2 custom firmware for the JC4880P443C_I_W / ESP32-P4 Function EV Board profile.
This project keeps the Espressif phone-style launcher experience, then extends it with a broader native app set, emulator support, better SD-card behavior, persistent Wi-Fi settings, timezone control, online firmware discovery, a local factory reset flow, and an external ESP32-C6 coprocessor firmware path for BLE and ZigBee features.
- LoRa Mesh restores reliable E22-400T22S UART communication with the in-tree runtime/config path again, replacing the failed library-swap direction with corrected mode-pin handling, live config sync, and startup validation on hardware.
- Hosted Wi-Fi and the shared audio path are brought back to the validated ESP32-C6 over SDIO configuration, fixing the recent regression where wireless startup drifted and audio behavior regressed with it.
- The hosted Wi-Fi compatibility layer for ESP-IDF 5.5.4 now packages cleanly again for release builds, so the published firmware, OTA version, and GitHub release assets stay aligned.
Screen module views:
Battery and speaker case v1:
Printable STL files for the enclosure and related 3D assets are stored in 3D/, including the original V1 files, the updated V2 and V3 case variants, separate lid / ring / fix parts, and editable Max or 3MF source files for the enclosure work.
An attachable joystick accessory for the device is currently in progress. The repo already includes the current hardware view and the early wiring reference used for that add-on work.
Joystick view:
Joystick circuit draft:
Compared with the stock Espressif-based firmware stack used for this hardware profile, this build adds or changes the following:
- Files app for browsing both
/sdcardand SPIFFS directly on the device. - E-Reader app for reading text-oriented files from the SD card.
- MQTT app with launch-page broker, credentials, client ID, and topic settings for quick connection testing.
- Internet Radio app with station discovery by popularity, country, language, and category.
- Native Recorder app for AAC recording on the built-in microphone, with SD-card saving under
/sdcard/record, live spectrum visualization, in-app playback, and PSRAM-first runtime buffers. - Native LoRa Mesh app with common and peer chat flows, persisted radio-module selection, GPIO remapping for SPI and UART modules, startup/send-path hardening, E22-400T22S UART config-sync fallback for modules that ignore register commands, corrected UART default healing for stale saved pin maps, active UART self-test probing that can swap and persist TX/RX automatically when a recovered pair succeeds, SD-card chat transcript restore and clear support, optimistic send bubbles with async delivery state, and chat send/receive event sounds.
- Native RS-485 HMI app for field-service scanning, raw terminal traffic, Modbus RTU master access, saved profiles, dashboard polling, and communication logs.
- Native Labyrinth app with 100 IMU-driven levels, progressively shorter timers, score tracking, a last-10-attempt results chart, and app-local X/Y/Z tilt remapping for ball movement.
- Native SEGA app with Master System, Game Gear, SG-1000, and Genesis / Mega Drive ROM support.
- SEGA browser now includes an optional FPS overlay toggle, and the in-game control surface is tuned for the rotated handheld presentation.
- Shared launcher icon set sized to fit the OTA partition budget.
- Persistent Wi-Fi credentials and reconnect behavior backed by NVS.
- Wi-Fi Settings now also includes device AP mode setup with saved hotspot enable, SSID, and password controls.
- Added a native Web Server launcher app with quick-access curtain control, local mDNS discovery, captive-portal friendly AP behavior, SD-card
/webhosting with SPIFFS fallback, and an embedded recovery uploader when site files are missing. - IMU support now includes runtime autodetection, live telemetry, a live indicator view, app-facing motion data shared through the firmware IMU service, and calibration persistence that survives restart.
- Display autorotation now uses live IMU readings and a user-selectable X/Y/Z control axis instead of the old placeholder sensor-pin dropdowns.
- BMI160 is now the default IMU wiring profile for the handheld build.
- Display timezone dropdown in GMT format with saved preference storage.
- Auto timezone detection from the internet after Wi-Fi connects.
- Firmware screen factory reset button with confirmation and settings wipe.
- Firmware releases now publish OTA-detectable
.binassets directly instead of ZIP-only packages. - GitHub OTA updates now follow release-asset redirects correctly, keep visible status during checks and flashes, preserve failure messages, and keep the final OTA verification worker on an internal stack so the update no longer panics at the end of flashing.
- OTA update awareness now runs in the background, supports default-enabled automatic update start from the Firmware OTA page, keeps a passive update-available icon in the top bar, can reschedule or cancel from the OTA modal, and can roll back automatically if a freshly updated image crashes before it is marked healthy.
- System audio notifications now include distinct generated tones for boot, OTA availability, OTA success, reboot flows, chat send, chat receive, and error recovery events.
- Power management now enables tickless idle and runtime light-sleep configuration on the main ESP32-P4 firmware so idle CPU utilization can drop instead of staying artificially high.
- Safer SD-card boot behavior so video playback is only enabled when MJPEG content is actually present.
- SPIFFS cleanup that removes bundled demo media and frees flash for larger OTA-safe application images.
- Additional low-risk PSRAM placement for radio preview workers, background service stacks, and emulator lookup / ROM buffers to preserve internal SRAM for time-sensitive work.
- Additional PSRAM-first placement now covers more subsystem memory, including NVS cache, mDNS allocations, NimBLE heap usage, the audio echo test buffer, and more background worker stacks.
- Internet radio buffering and MP3 recovery are more tolerant of malformed or slow streams, which reduces playback stalls.
- Firmware settings can browse GitHub releases directly and offer OTA updates from attached
.binassets. - Dead launcher apps and unreachable video-player sources were removed to reduce maintenance surface and keep OTA builds within budget.
- Camera and 2048 were removed from the installed launcher set to reduce boot-time memory pressure and maintenance surface.
- MP3 probing and decode fallback behavior are more tolerant of malformed frames and stream sync loss.
- BLE and ZigBee features are now enabled through a matching ESP32-C6 coprocessor firmware release.
- The standalone ESP32-C6 release now includes the fixed ZigBee storage partition layout required for stable bring-up.
- Wi-Fi status in the top bar now follows real connection state and signal strength updates more reliably.
- The Wi-Fi settings page now uses an explicit full-width
Scanaction instead of background auto-scan, which avoids dropdown races and makes scan timing predictable. - Saved networks now stay connectable without live availability gating, and failed attempts can fall back to the previously connected network instead of leaving the device disconnected.
- Intentional disconnect flows no longer trigger automatic reconnect recovery, so a manual disconnect can keep Wi-Fi offline until the user asks for another connection.
- BLE startup, teardown, and disconnect recovery on the ESP-Hosted path were hardened to avoid stuck startup states and disconnect-time crashes.
- BLE game controller support now runs through the ESP32-C6 coprocessor with Bluepad32-based report forwarding, analog sticks and triggers, and broader controller button coverage.
- The BLE controller settings page now includes a live controller visualizer, persistent per-controller calibration storage, and calibration profile reuse across reconnects.
- Joypad layout editing now uses a dedicated local configurator that reads and regenerates the firmware layout header directly, supports BLE and Local controller targets, and lets controller artwork be refreshed alongside the generated LVGL asset.
- The BLE controller preview now follows the generated Joypad layout live on-device, including layout-driven buttons, triggers, and calibration-centered stick movement instead of the old split preview path.
- The Local Controller path now supports analog X/Y inputs plus MCP23017-backed buttons with a live on-device preview, serial input diagnostics, and the additional
Keyaction on the default MCPB4mapping. - The Local Controller settings page now includes integrated WS2812 / Neopixel controls, configurable haptic GPIO and strength, and live test feedback for motor changes without blocking the rest of the UI.
- Settings > Display now exposes saved 0/90/180/270 orientation control that applies live at runtime, including corrected touch remapping after rotation.
- Display rotation changes now use a live 30-second confirmation flow that reverts automatically if the user does not confirm the new orientation.
- Battery sampling now detaches from ADC2 while the Local Controller is active so analog local inputs can run without the previous ADC2 ownership conflict.
- The settings UI now includes compact Bluetooth and ZigBee status icons used by the latest wireless status flow.
- Settings now exposes separate Media and System Sounds volume controls on the shared audio output path.
- The top-right quick-access curtain now also exposes a persistent microphone gain slider from
x1tox10, and the recorder input path uses that shared gain setting. - Launcher home-screen page swipes now use animated transitions instead of instant page swaps when moving between launcher pages.
- UI tap sounds now reuse the shared audio policy more effectively, so idle taps no longer sound noticeably weaker than taps triggered while media is already playing.
- Hardware Monitor now keeps one hour of background history in PSRAM for CPU load, SRAM, PSRAM, Wi-Fi, battery, and CPU temperature instead of sampling only while the page is open.
- Hardware Monitor SD-card status now follows the app-wide storage mount state, so it no longer disagrees with File Manager when the card is mounted and browsable.
- The Hardware Monitor page now relies on the common system back gesture instead of its own dedicated back button, and the duplicate non-functional temperature card was removed.
- Files, Music, and Radio startup paths were hardened to avoid preallocating unnecessary runtime memory before launch.
- Internal SRAM usage was reduced substantially by moving large SEGA emulator permanent buffers and tables into PSRAM-backed BSS.
- Additional SEGA emulator permanent RAM, preview buffers, and SMS / Genesis scratch state now live in PSRAM-backed BSS, which cuts fresh-boot internal SRAM pressure before the emulator is launched.
- Additional boot-time SRAM pressure was removed by deferring heavy Internet Radio, Image Display, and SEGA UI/runtime setup until first launch.
- Image Viewer now reads images only from
/sdcard/image, lazy-loads thumbnails, maintains a dynamic/sdcard/sys/thumbscache, prefers PSRAM-backed decode buffers, falls back across multiple JPEG decode paths, and opens fullscreen/slideshow transitions through the safe LVGL-thread path. - The unused camera and deep-learning component stack was removed from the resolved build graph to reduce flash footprint and memory pressure.
- Settings shutdown and modal-close flows were hardened against stale LVGL object updates that could previously trigger a panic during screen teardown.
- Music Player runtime metadata, library indexes, and long-lived worker stacks now prefer PSRAM, which reduces launch-time and background SRAM pressure.
- Music Player playlist confirmation dialogs now use safe LVGL async-close handling to avoid the panic that could occur when deleting or cancelling from the modal.
- Quick-access controls now stay available while apps are open, with a full-width power-and-audio curtain, smaller launcher row icons, and richer live Music Player detail in the strip.
- The quick-access
Close Allaction now uses a taller button with a larger touch hit area so it can be triggered more reliably on-device. - Internet Radio now mirrors Music Player-style top-bar quick actions for country station lists, including previous / next station controls and live buffering detail while the quick-access sheet is open.
- Internet Radio now exposes a live buffered-stream meter, larger playback modal layout, deeper PSRAM-first buffering, and delayed startup until the stream cache reaches a safer prefill level.
- Internet Radio steady-state downloading now refills in the background instead of blocking the audio playback path, which reduces refill-time audio dropouts on slow stations.
- Radio station catalogs, stream working buffers, and the shared audio playback task now prefer PSRAM more aggressively to preserve internal SRAM during radio use.
- Internet-backed Radio flows now fail fast when Wi-Fi has no usable IP or DNS, which avoids long timeout paths when the device is connected to Wi-Fi without real internet access.
- Radio app close/reopen handling now clears stale LVGL screen pointers on teardown, preventing the main-screen artifacts and close-time panic that could occur after failed online lookups.
- Attachable joystick hardware support is now being prepared in parallel with the main firmware and enclosure work, with the current mechanical and circuit references tracked in this repo.
- The repo now also includes newer attachable joystick enclosure assets and fresh 3D reference exports for the ongoing accessory fit work.
- Build-time modular configuration now exposes top-level menuconfig switches for major launcher apps and feature domains, so firmware variants can be trimmed without hand-editing code.
- Panic behavior now reboots automatically, and the next boot shows a short recovery popup that distinguishes crash, watchdog, brownout, and CPU lockup resets.
- Flash-backed core dump capture is now enabled, and the next boot persists a readable crash report under SPIFFS for later developer analysis.
- The reboot recovery popup can now show a manual
Reportaction that tries to submit the saved crash report when Wi-Fi is connected, otherwise it reports that the device is offline or the private relay is not configured. - Additional enclosure revisions and raw CAD exports are included under
3D/for the updated hardware fit iterations. - SEGA manual saves now use separate
SAVEandLOADactions instead of auto-resume behavior, so launching a ROM always starts from a clean boot unless a valid save is chosen explicitly. - The SEGA load flow now pauses emulation and shows a rotated save-slot picker with up to five recent preview thumbnails pulled from SD-card save folders, with load-button state and preview orientation kept in sync with the active handheld rotation.
- Genesis save-state loading now restores the full 68K register set and rejects older incompatible save files instead of crashing after load.
- The SEGA browser now keeps the top bar visible on entry and return-from-game while gameplay itself stays fullscreen.
- The SEGA browser header and save-slot picker scrolling were cleaned up so the title no longer overlaps and the horizontal save list no longer recenters itself while browsing.
- Genesis runtime timing, audio pacing, and framebuffer rotation were tuned further to match the handheld layout and reduce slow-background-music behavior.
- On-screen keyboards now share the same shift and password-toggle behavior across Settings, File Manager, Music Player, and other text-entry flows.
- Settings now includes a dedicated GPIO Control page for ESP32-P4 header testing, with input/output/PWM/wave/timer/alarm modes, centralized multi-pin output actions, and on-screen keyboard entry for the editable control fields.
- GPIO timer and alarm flows now run asynchronously, can hold the selected output state until stopped, rescan
/sdcard/wavplus other supported audio formats for alert playback, and avoid overlapping timer melodies by keeping a single active alert owner.
- Phone-style launcher UI based on ESP-Brookesia and LVGL.
- Settings, Calculator, Files, E-Reader, MQTT, Music Player, Internet Radio, LoRa Mesh, Recorder, RS-485 HMI, Image Display, Labyrinth, and SEGA Emulator.
- SEGA Emulator app integrated into the launcher instead of living as a separate upstream project.
- Files app can inspect both onboard SPIFFS and the SD card.
- E-Reader reads supported files from the SD card.
- Recorder captures AAC files to
/sdcard/record, updates the saved-recordings list while recording, and supports playback directly from the recorder app. - RS-485 HMI stores exported communication logs under
/sdcard/rs485and keeps saved transport/device state in persistent settings storage. - Image Viewer now looks only in
/sdcard/image, lazy-loads gallery thumbnails, keeps a dynamic/sdcard/sys/thumbscache in sync with the image folder, and uses safer fullscreen/slideshow transitions. - Music and image sample payloads were removed from SPIFFS to save flash.
- The firmware updater can scan
/sdcard/firmwarefor local.binimages or check GitHub releases for OTA-ready.binassets.
- Saved Wi-Fi credentials persist across reboots and reconnect automatically.
- Wi-Fi scanning is now manual from Settings with a dedicated
Scanbutton placed below the saved-network card. - Pressing
Scanwhile Wi-Fi is off turns Wi-Fi on first, then launches the scan through the normal init path. - Saved networks remain connectable even if they are not in the latest scan result set.
- Failed switches to unavailable saved networks can restore the previously connected network automatically.
- Intentional disconnects no longer trigger unwanted reconnect attempts.
- Wi-Fi Settings can also run the device as an AP with editable SSID and password fields from the same screen.
- Signal strength and scan results are exposed in Settings.
- System time is sourced from SNTP and converted with the configured local timezone.
- Manual timezone selection is available in GMT offsets.
- Auto timezone mode can update the offset from online geolocation when internet access is available.
- Display orientation can be changed live between 0, 90, 180, and 270 degrees from Settings > Display, and the selected rotation is restored on the next boot.
- Rotation changes now show a 30-second confirmation dialog and only persist after the user presses
OK; otherwise the previous orientation is restored automatically. - Settings > IMU now exposes live detection and live motion telemetry for the installed IMU, plus saved zero/calibration state across reboot.
- Settings > Display autorotate now follows IMU motion at runtime and lets the user choose the rotation axis (
X,Y, orZ). - Quick-access audio controls include separate Media, System, and Mic sliders, with the Mic control mapped to the recorder input gain.
- Factory Reset in Settings > Firmware clears the app preferences namespace and reapplies defaults immediately.
- Default IMU profile: BMI160
BMI160 VCC -> JP1 VCC3V3BMI160 GND -> JP1 GNDBMI160 SDA -> GPIO31BMI160 SCL -> GPIO30BMI160 INT1 -> GPIO50(optional)BMI160 INT2 -> GPIO51(optional)
- Panic and watchdog resets now reboot back into the launcher instead of halting on a dead screen.
- The next boot shows a short recovery popup with the general reset cause so failures are visible without opening a serial monitor.
- Core dumps are stored in a dedicated flash partition and summarized into a text crash report on boot.
- Crash reports are saved locally in SPIFFS and can be manually submitted from the recovery popup when Wi-Fi is available.
- BLE and ZigBee runtime support depends on the external ESP32-C6 coprocessor firmware published in the matching GitHub release.
- If the C6 is not flashed with the firmware from the same release, BLE and ZigBee features on the P4 side are not expected to work correctly.
- The C6 firmware is built from
coprocessor_c6/and is released alongside the main P4 firmware.
- BLE controller support is tested with the TOBO BSP-D9 game controller.
- Controller manual: TOBO BSP-D9 manual.
- To pair the TOBO BSP-D9 with the unit, turn on
BLE Controllerin Settings, then pressHOME+Xon the controller. - Reflash the ESP32-C6 first with the coprocessor binary from the latest release, otherwise controller pairing on the P4 side is not expected to work correctly.
- Local Controller mode also supports a directly wired handheld setup with analog
Y Axis/X Axisinputs on the P4 and MCP23017 button expansion over I2C. - The default MCP23017 Local Controller dropdowns are
Y Axis GPIO = 50,X Axis GPIO = 51,MCP SDA GPIO = 30,MCP SCL GPIO = 31,Start = B5,Exit = B3,Save = B7,Load = B6,A = B2,B = B1,C = B0, andKey = B4. - The Local Controller preview is live on-device and reflects analog movement plus MCP-backed face and action buttons, including
Save,Load,Exit, andKey. - Local Controller settings also expose non-blocking Neopixel and haptic feedback controls, including effect selection, brightness, GPIO routing, and haptic strength test pulses.
- The SEGA app scans
/sdcard/sega_gamesfor.sms,.gg,.sg,.md,.gen,.bin, and.smdROMs. - SMS and Game Gear battery saves are written next to the ROM as
.savsidecars. - Genesis / Mega Drive support is integrated through the adapted Gwenesis path.
- Manual save states are stored under
/sdcard/saved_games/<game>_<hash>/with up to five recent slots and thumbnail previews. - The in-game
LOADaction opens a rotated preview picker and pauses the emulator until a slot is chosen or cancelled.
- ESP32-P4 Function EV Board based target.
- 7-inch 1024x600 MIPI-DSI display using EK79007-compatible support.
- USB-C for power, flashing, and serial monitoring.
- Optional SD card for media, firmware packages, and emulator ROMs.
- Optional BMI160 IMU on the default wiring above for motion input, live telemetry, and display autorotation.
- Flash size is configured for 16 MB.
- Partition table provides two balanced OTA app slots of
0x7D0000each. - A dedicated
0x020000flash coredump partition is reserved for post-crash diagnostics. - SPIFFS storage is
0x020000to prioritize OTA update headroom while preserving the remaining onboard filesystem features. - Version 1.4.2 validates at
0x77E610(7,857,680bytes), leaving0x0519F0(334,320bytes) free in either0x7D0000OTA app slot. - Browser and YouTube launcher leftovers are removed from the app tree; codec support is limited to the active playback paths: MP3 radio streams plus MP3/AAC/M4A/MP4/FLAC/WAV local music.
/sdcard/musicfor music content./sdcard/recordfor AAC voice recordings captured by the Recorder app./sdcard/rs485for exported RS-485 communication logs./sdcard/imagefor image content./sdcard/sys/thumbsfor cached Image Viewer thumbnails./sdcard/sega_gamesfor SEGA ROMs./sdcard/saved_gamesfor SEGA manual save-state folders and preview thumbnails./sdcard/firmwarefor local.binfirmware packages.
This project targets ESP-IDF 5.5.x and esp32p4.
The main ESP-IDF project and generated P4 firmware image are now named ESP32P4_Remote.
The ESP-IDF equivalent of a project configuration.ini in this repo is menuconfig / sdkconfig.
This firmware now exposes a top-level JC4880 Modular Configuration menu where a programmer can enable or disable major launcher apps and major feature domains before building.
Current modular switches include:
- Connectivity domains: Wi-Fi, Bluetooth, BLE, ZigBee, internet time/date sync.
- System domains: hardware info, battery, display, audio, security, OTA, about-device.
- Launcher apps: Settings, Calculator, SEGA Emulator, Image Viewer, File Manager, Music Player, Internet Radio, Recorder, E-Reader, MQTT.
Within the Settings app, these feature flags now also gate the actual Settings sections and their supporting runtime work. For example, disabling BLE removes the Bluetooth entry from Settings and skips its startup path, and disabling OTA removes the firmware updater entry and screen.
Current limitation:
CONFIG_JC4880_FEATURE_BATTERYis reserved for a future dedicated battery screen/card. The existing hardware monitor is currently gated by the broader hardware domain.
Run menuconfig from the project root:
C:\Users\Elik\Documents\Arduino\JC4880P443C_I_W_Remote
If you are using the ESP-IDF extension in VS Code, the simplest path is to open an ESP-IDF Terminal and run:
idf.py menuconfigIf you are in a normal PowerShell terminal and accidentally activated a Python virtual environment such as .venv or .venv-1, leave that environment first. idf.py must use the ESP-IDF-managed Python environment, not a project venv.
PowerShell example:
deactivate
& "C:\Espressif\frameworks\esp-idf-v5.5.4\export.ps1"
idf.py menuconfigIf the ESP-IDF extension is configured correctly, you can also use its SDK Configuration Editor instead of typing the command manually.
Open the config UI with:
idf.py menuconfigThen go to:
JC4880 Modular Configuration
Inside that menu:
- Open
Feature Domainsto control top-level platform capabilities such as Wi-Fi, Bluetooth, BLE, ZigBee, OTA, audio, display, and security. - Open
Launcher Applicationsto include or exclude whole apps from the launcher build. - Save before exiting so the selected values are written into
sdkconfig.
The generated file updated by menuconfig is sdkconfig. The option definitions themselves live in components/apps/Kconfig.projbuild.
Example sdkconfig values:
CONFIG_JC4880_APP_SETTINGS=y
# CONFIG_JC4880_APP_INTERNET_RADIO is not set
CONFIG_JC4880_FEATURE_WIFI=y
That means:
- Settings app is enabled.
- Internet Radio app is disabled.
- Wi-Fi feature domain is enabled.
If you prefer editing by hand instead of using the UI, you can modify the CONFIG_JC4880_* lines in sdkconfig directly, then rebuild. The UI is still the safer path because it preserves dependencies.
Open the configuration editor:
idf.py menuconfigRebuild after changing switches:
idf.py buildBuild and flash to the board:
idf.py -p COM10 flashBuild, flash, and open the serial monitor:
idf.py -p COM10 flash monitorAfter changing the switches, rebuild and flash as usual:
idf.py set-target esp32p4
idf.py buildTo flash and open the serial monitor:
idf.py -p PORT flash monitorThe firmware also exposes a built-in USB Serial JTAG command console from main/main.cpp.
Open it with the normal monitor command:
idf.py -p COM10 monitorThen type help to list the available commands. The LoRa Mesh app now exposes its own lora.* debug and control commands through that same console.
Important notes:
- Use
lora.openfirst to put the LoRa Mesh app on screen before sending view-changing commands. - View commands update the on-device UI so you can drive the app from serial while also watching the screen.
- The app now emits LoRa lifecycle, navigation, startup, and self-test progress messages to the serial log.
LoRa Mesh serial commands:
lora.status: print the current LoRa app state, including active view, startup state, radio readiness, self-test state, and selected chat.lora.open: open the LoRa Mesh app visibly on the device.lora.targets: switch the visible app view to the target list.lora.chat.common: switch the visible app view to the common mesh chat.lora.chat.peer <device_id>: switch the visible app view to a private chat for the given peer device ID.lora.peers: list the known peers with device ID, display name, presence, RSSI, and SNR.lora.settings: switch the visible app view to settings.lora.selftest.start: start the visible LoRa self-test flow.lora.selftest.stop: request that the current self-test stop cleanly.lora.log [count]: dump the last log lines captured by the LoRa app. Default is20lines.lora.close: close the visible LoRa Mesh app.
Typical serial-driven LoRa debug flow:
lora.open
lora.status
lora.settings
lora.selftest.start
lora.log 40
lora.selftest.stop
lora.targets
lora.peers
The repo now includes three ready-made modular profiles:
sdkconfig.defaults.fullsdkconfig.defaults.mediasdkconfig.defaults.minimal
To build one of those profiles without replacing your current checked-in sdkconfig.defaults, layer it on top of the base defaults:
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.media" reconfigure buildSwap sdkconfig.defaults.media for sdkconfig.defaults.full or sdkconfig.defaults.minimal as needed.
Examples:
Build the full profile:
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.full" reconfigure buildBuild the media profile:
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.media" reconfigure buildBuild the minimal profile:
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.minimal" reconfigure buildThese preset commands are useful when you want repeatable build variants without manually flipping many switches in menuconfig each time.
If idf.py menuconfig fails, the problem is usually the shell environment rather than the project.
No module named 'click'
- Cause:
idf.pywas launched from a normal Python virtual environment such as.venvor.venv-1instead of the ESP-IDF Python environment. - Fix: leave the Python venv, load ESP-IDF, then run
idf.pyagain.
deactivate
& "C:\Espressif\frameworks\esp-idf-v5.5.4\export.ps1"
idf.py menuconfigidf.py works in the ESP-IDF extension but not in your terminal
- Cause: the VS Code extension has the right ESP-IDF environment loaded, but your normal terminal does not.
- Fix: use an
ESP-IDF Terminalfrom the extension, or run theexport.ps1command above in your shell first.
Wrong directory
- Cause:
idf.pywas run frommain/,components/, or another subfolder instead of the project root. - Fix: run it from:
C:\Users\Elik\Documents\Arduino\JC4880P443C_I_W_Remote
Wrong serial port during flash
- Cause: the board COM port changed or the command used the wrong port.
- Fix: use the correct port explicitly, for example:
idf.py -p COM10 flash monitorIf flash fails, re-check Device Manager or the ESP-IDF port selector in VS Code.
Build does not reflect changed config
- Cause: the project needs a rebuild or reconfigure after
sdkconfigchanges. - Fix: run:
idf.py reconfigure buildIf you changed preset defaults with SDKCONFIG_DEFAULTS, keep using the same command line for that profile so the build graph stays consistent.
The BLE controller overlay layout editor lives under tools/joypad_layout/ and runs as a small local web app inside VS Code.
Run the VS Code task:
JC4880: Joypad Layout Configurator
That task starts this command in a terminal:
& "C:\Users\Elik\.espressif\python_env\idf5.5_py3.12_env\Scripts\python.exe" "${workspaceFolder}\tools\joypad_layout\server.py"The server listens on:
http://127.0.0.1:8765/
To open it in a VS Code tab like the integrated browser tab already used during development, open that URL inside VS Code after the task is running.
The configurator is only a local localhost server. To shut it down, stop the terminal or task that is running server.py.
Common ways in VS Code:
- Press
Ctrl+Cin the terminal that is runningJC4880: Joypad Layout Configurator. - Use
Tasks: Terminate Taskand selectJC4880: Joypad Layout Configurator. - Kill the terminal tab that is hosting the task.
Read: reloads both BLE and Local controller layouts fromcomponents/apps/setting/joypad/SettingJoypadLayout.hpp, then refreshestools/joypad_layout/joypad_layout.jsonfrom that generated source of truth.Apply: writes the current configurator state back intotools/joypad_layout/joypad_layout.jsonand regeneratescomponents/apps/setting/joypad/SettingJoypadLayout.hpp.
The generated header is the authoritative layout source used by firmware builds. Apply only updates code artifacts, so build and flash remain manual steps.
The configurator also lets you replace the default controller background PNG.
When you choose a PNG and press Replace Background PNG, the tool updates:
icons/joypad/controller.pngfor the web preview.components/apps/setting/ui/images/ui_img_controller_png.cfor the firmware image asset.
That means the next firmware build and flash will use the same background artwork that the configurator preview is showing.
Typical follow-up flow:
1. Run JC4880: Joypad Layout Configurator
2. Adjust layout or replace the background PNG
3. Press Apply if you changed layout geometry or visuals
4. Build with JC4880: Build P4 or flash with JC4880: Build and Flash P4
If you want one simple switch in VS Code to choose whether to flash the main P4 firmware or the external C6 firmware, use the local workspace settings and task created in .vscode/settings.json and .vscode/tasks.json.
Important:
- This repo keeps the root ESP-IDF target as
esp32p4. - Do not use the ESP-IDF status bar target picker to switch the root project to
esp32c6just to flash the coprocessor. - Instead, switch the local flash selector and run the matching task.
In .vscode/settings.json, change:
Valid values are:
p4to flash the main ESP32-P4 firmware.c6to flash the ESP32-C6 coprocessor firmware.
The local COM ports are configured with:
"jc4880.ports.p4": "COM10",
"jc4880.ports.c6": "COM12"After changing the selector, run the VS Code task:
JC4880: Flash Selected Target
That task flashes the latest built image for the selected target.
BLE and ZigBee features require the ESP32-C6 coprocessor to be flashed with the matching firmware from the same GitHub release as the P4 firmware.
Use the release assets for both devices together:
- Flash the P4 with the P4 firmware from the release.
- Flash the C6 with the C6 firmware from the same release.
- Do not mix older C6 firmware with a newer P4 release if you expect BLE or ZigBee to work.
To flash the C6 from an external UART bridge, connect the bridge to the board header like this:
RX -> TXTX -> RXGND -> GND5V -> 5V
To put the C6 into boot mode:
- Pull
C6_IO9toGND. - Connect USB to the PC.
- Put the P4 side into boot mode as well so it does not interfere with the C6: press
BOOTandRST, then releaseRSTso the screen stays in boot mode. - Release
C6_IO9fromGND. - Flash the C6 firmware.
This sequence keeps the P4 out of the way while the external UART bridge talks directly to the C6.
main/boot flow and app installation.components/apps/native applications and emulator integration.common_components/board-specific and locally adapted support code.managed_components/ESP Component Manager dependencies.third_party/imported upstream code adapted into this firmware.spiffs/bundled non-media filesystem assets.
The firmware in this repository adapts upstream vendor and emulator code. These are the primary sources that should be credited when redistributing or reviewing changes:
- Espressif ESP-Brookesia: https://github.com/espressif/esp-brookesia
- Espressif ESP-WiFi-Remote component: https://components.espressif.com/components/espressif/esp_wifi_remote
- Espressif WiFi Remote over EPPP component: https://components.espressif.com/components/espressif/wifi_remote_over_eppp
- Espressif ESP32-P4 Function EV Board BSP: https://components.espressif.com/components/espressif/esp32_p4_function_ev_board
- Retro-Go upstream: https://github.com/ducalex/retro-go
- SMS Plus GX upstream: https://github.com/ekeeke/smsplus-gx
The local emulator integration under components/apps/sega_emulator/ and the vendor-facing launcher / board adaptations in this repository were modified to fit the JC4880P443C_I_W firmware, storage layout, UI flow, and OTA constraints.
GitHub repository:
https://github.com/elik745i/JC4880P443C_I_W_Remote
External write-up covering the device bring-up, hardware experiments, ESP-IDF migration, case work, and overall project direction:










