Board support: Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 (esp32p4 target, MIPI-DSI display, touch, speaker) - #308
Draft
bitcoin3us wants to merge 8 commits into
Draft
bitcoin3us wants to merge 8 commits into
bitcoin3us wants to merge 8 commits into
Conversation
ESP32_GENERIC_P4 with MicroPython's C6_WIFI variant (esp_hosted + esp_wifi_remote over SDIO, pulled in by the port's component manifest), 32 MB flash, 4 MiB OTA app partitions (boards ship 32 MB, no reason to inherit the 3.5 MiB legacy size), freezefs native code as rv32imc. Opt-in: nothing changes for the existing targets. c_mpos/src/quirc_decode.c: the uxTaskGetStackHighWaterMark stub for desktop/web builds was keyed on __xtensa__, which made the RISC-V ESP32-P4 build fail with a conflicting prototype; key on INC_FREERTOS_H (py/runtime.h already includes FreeRTOS on the ESP32 port) instead. First target: Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 (MicroPythonOS#307). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ESP32-P4 boards get their own probe branch in detect_board(): the xtensa boards' probe pins map to unrelated peripherals on the P4 (the SDIO link to the ESP32-C6 Wi-Fi co-processor among them) and an I2C probe on them hangs the chip before the REPL comes up. The P4 branch probes the GT911 touch controller on the board's I2C bus (SDA 7, SCL 8, address 0x5D or 0x14). main.py now also stops cleanly when a board file registers no LVGL display: init_rootscreen() would otherwise dereference a NULL display and panic the chip into a reboot loop with no REPL to debug from. The board file is a stage-1 placeholder (no display driver yet); the MIPI-DSI display, touch and audio follow in later commits. Verified on the board: clean stop at the REPL, DeviceInfo hardware id set, Wi-Fi scan through the C6 (14 networks), I2C scan finds 0x5D (GT911), 0x18 (ES8311) and 0x40 (ES7210). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The board file brings up the 4.3" 480x800 panel over 2-lane MIPI-DSI and the GT911 touch controller, replacing the stage-1 placeholder: - drivers/display/st7701_dsi: an ST7701 driver for lvgl_micropython's lcd_bus.DSIBus (video mode). The bus owns two screen-sized PSRAM frame buffers that LVGL renders into directly and the P4's DPI DMA scans out; the driver's job is the hardware reset and the panel's DCS register setup (Waveshare's vendor table, preceded by the bank-0 MADCTL/COLMOD writes Espressif's esp_lcd_st7701 sends), sent before the bus starts the video stream on the first flush. Rotation is not supported (no row/column exchange on the video interface); the board runs the panel in its native portrait orientation. - Backlight: LEDC PWM on GPIO26, active low (Waveshare's BSP drives its LEDC channel inverted), kept off until the first frame is out. - Touch: GT911 polled at whichever I2C address its strap selected (0x5D or 0x14); neither its reset nor its interrupt line is wired to the P4 here, so drivers/indev/gt911.py no longer tries to build a Pin from interrupt_pin=None. - build_mpos.sh (esp32p4): PSRAM at 200 MHz and the 256 KB L2 cache with 128-byte lines, as in Waveshare's and Espressif's P4 display examples. The panel is scanned out of PSRAM continuously (46 MB/s at 60 Hz) and MicroPython's default 20 MHz PSRAM clock cannot feed it. Needs lvgl_micropython with DSIBus (feat/dsi-bus); the submodule pointer is bumped once that is merged. Verified on the board: DCS reads answer (RDDPM 0x9C, RDDMADCTL 0x00, RDDCOLMOD 0x50), LVGL renders the launcher into the panel buffers and full-screen refreshes complete in one frame period (14-17 ms), GT911 reports product 911 / firmware 0x1060 / 480x800, no DMA underruns. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ES8311 codec (I2C 0x18 on the shared bus) driving the board's power amplifier: I2S MCLK=13, BCLK=12, LRCK=10, data=9, amplifier enable GPIO53 (active high), switched on around playback only and sequenced with the DAC soft-mute like the other ES8311 boards. Starts from the 3.5" board's 76% DAC volume; this amplifier still needs an ear on it. The microphones sit behind a separate ES7210 ADC (I2C 0x40) that MicroPythonOS has no driver for yet, so only playback is wired up. Verified on the board: a 1 s WAV plays to completion with the amplifier enabled 69 ms and the DAC unmuted 254 ms after start (needs the frozen native code alignment fix in lvgl_micropython's builder, without it the first native helper call panics on the P4). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The UI runs landscape (800x480) like the other MicroPythonOS boards. The ST7701 cannot rotate on the video interface, so the P4's PPA does it: the DSI bus is created with rotation=270 and the ST7701_DSI driver sets the same rotation on the LVGL display, which then renders landscape into its own buffers while the bus rotates every finished update into the panel's portrait frame buffers. LCD_ROTATION in the board file picks which way is up (270 is upright in the enclosure, 90 is upside down); 0 gives the native portrait. The GT911 keeps reporting panel coordinates, LVGL maps them to the rotated UI itself. Needs lvgl_micropython with DSIBus rotation (feat/dsi-bus). Verified on the board: LVGL at 800x480 with rotation 270, upright in the enclosure, forced full-screen refreshes ~29 ms (render + PPA + vsync), no DMA underruns. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Boards whose radio is a separate ESP-Hosted co-processor (the ESP32-P4
with an ESP32-C6 over SDIO) need to know which co-processor firmware
they talk to, and to be able to update it: the host warns at boot
when the major.minor versions differ ("upgrade co-proc to avoid RPC
timeouts"), but that log is lost while USB re-enumerates.
hosted.host_version() and hosted.coprocessor_version() return
(major, minor, patch); hosted.ota_begin/ota_write/ota_end/ota_activate
wrap esp_hosted's co-processor update calls. Compiled only where
CONFIG_ESP_HOSTED_ENABLED is set; other targets get an empty file.
Used on the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 to find its C6 on
firmware older than esp_hosted 1.4.7 (it does not answer the version
request) against a 2.7.0 host, and to update it to 2.7.0 from an
image placed in the P4's spare app partition.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-4.3 Now that WAVStream supports an opt-in warm output (MicroPythonOS#301), the P4 board's ES8311 speaker uses it like the ESP32-S3-Touch-LCD-3.5: the I2S clocks keep running and the codec stays unmuted for 30 s after a clip, so back-to-back clips do not click on every MCLK/I2S restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bitcoin3us
force-pushed
the
feat/esp32p4-target
branch
from
September 29, 2026 13:01
aea6fca to
8098602
Compare
This was referenced Sep 29, 2026
On this board the ESP32-C6 co-processor joins slowly or not at all on 2.4 GHz channels 6-9 (waveshareteam/ESP32-P4-WIFI6-Touch-LCD-4.3#7). Against a test AP on channel 6, 1 m away, it joined in 6 of 9 attempts, after 14-30 s; channels 1 and 11 take ~3 s. That is longer than WifiService's 13 s wait, so the board file raises WifiService.CONNECT_TIMEOUT_S (added in MicroPythonOS#311) to 30 s. Until MicroPythonOS#311 is merged nothing reads the attribute and the wait stays 13 s. PSRAM stays at 200 MHz: an 80 MHz build (PSRAM clock source at 320 instead of 400 MHz) made no consistent difference to channels 6-9 in a same-day comparison and slowed a full-screen refresh from ~30 ms to ~53 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
First ESP32-P4 board for MicroPythonOS, following the plan in #307. Eight commits, on top of current
main:build: add an esp32p4 target—./scripts/build_mpos.sh esp32p4:ESP32_GENERIC_P4/C6_WIFIvariant (Wi-Fi 6 and BLE through the ESP32-C6 co-processor over SDIO, esp_hosted), 32 MB flash with 4 MiB app partitions, RISC-V frozen builds (rv32imc), PSRAM at 200 MHz and the 256 KB L2 cache with 128-byte lines (the MIPI-DSI panel is scanned out of PSRAM continuously). Also fixesc_mpos/src/quirc_decode.c's FreeRTOS stub guard, which keyed on__xtensa__.boards: detect the Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3— the P4 gets its own probe branch indetect_board()(GT911 at 0x5D/0x14 on SDA 7 / SCL 8): the xtensa boards' probe pins map to unrelated peripherals on the P4 and hang the chip.main.pyalso stops cleanly when a board file registers no LVGL display instead of panicking ininit_rootscreen().boards: ... display and touch—drivers/display/st7701_dsi: ST7701 overlcd_bus.DSIBus(video mode; hardware reset, Waveshare's vendor register table over DCS, no window addressing) and the board file: 2-lane DSI at 500 Mbps, DPI 30 MHz, RGB565, two PSRAM frame buffers, backlight on GPIO26 (PWM, active low), GT911 polled (its RST/INT are not wired),drivers/indev/gt911.pyfixed forinterrupt_pin=None.boards: ... speaker output— ES8311 codec + the board amplifier (I2S MCLK 13 / BCLK 12 / LRCK 10 / data 9, PA enable GPIO53 active high), sequenced with the DAC soft-mute like the other ES8311 boards. The microphones sit behind a separate ES7210 ADC that has no driver yet.boards: ... in landscape— the UI runs 800x480 like the other boards. The panel is natively portrait and the ST7701 cannot rotate on the video interface, so the P4's PPA does it (DSIBus(rotation=270), same rotation on the LVGL display).LCD_ROTATIONin the board file picks which way is up; 270 is upright in the enclosure.c_mpos: hosted module—hosted.host_version()/hosted.coprocessor_version()report the esp_hosted versions on both sides of the SDIO link, andhosted.ota_begin/ota_write/ota_end/ota_activatewrap esp_hosted's co-processor update calls. Compiled only whereCONFIG_ESP_HOSTED_ENABLEDis set (an empty file elsewhere; desktop builds don't include it). See "Wi-Fi co-processor firmware" below for why it matters on this board.boards: warm speaker output— now that WAVStream: opt-in warm output so back-to-back clips don't click (ES8311) #301 is in, the speaker useswarm_ms=30000like the 3.5" board, so back-to-back clips don't click on every I2S restart.boards: ... Wi-Fi 30 s— the board file raisesWifiService.CONNECT_TIMEOUT_S(added in WifiService: cancel a pending reconnect (#309), let a board lengthen the connection wait #311) to 30 s, because this board's C6 can need up to 30 s to join on channel 6 (see below). Until WifiService: cancel a pending reconnect (#309), let a board lengthen the connection wait #311 is merged nothing reads the attribute and the wait stays 13 s.Depends on
@micropython.nativecall panics on the P4). Thelvgl_micropythonsubmodule pointer will be bumped here once that is merged, so this stays a draft until then.CONNECT_TIMEOUT_Ssetting that commit 8 uses.Verified on the board
mainwith WAVStream: opt-in warm output so back-to-back clips don't click (ES8311) #301, Waveshare ESP32-S3-Touch-LCD-3.5: LCD SPI clock 80 MHz #303, Re-pin micropython-camera-API to db42861 (restores sccb_i2c_port; fixes "No camera found" on the 3.5) #304, c_mpos: jpegdec, a native JPEG decoder module (esp_new_jpeg) for video playback #305 (jpegdec and the size trims also apply to the P4 build) and Waveshare ESP32-S3-Touch-LCD-3.5: reliable panel init on cold boot #306: boots cleanly, jpegdec imports, 617 KB of app-partition headroom.WifiService.CONNECT_TIMEOUT_Sreads 30 after boot, the board joins its saved network by itself, and a connection attempt to a channel-6 AP waits the full 30 s instead of 13 s. Image 616 KB below the partition size.Wi-Fi co-processor firmware
The board's ESP32-C6 ships with esp_hosted firmware older than 1.4.7 (it does not answer the co-processor version request, which 1.4.7 already supports), while this build's host side is esp_hosted 2.7.0. esp_hosted warns about that mismatch at boot ("upgrade co-proc to avoid RPC timeouts"), but the message is lost while USB re-enumerates, hence the
hostedmodule. On the test board the C6 was updated to 2.7.0 over SDIO: esp_hosted'sslaveproject built for esp32c6, the image written into the P4's unusedota_1with esptool, then streamed to the C6 withhosted.ota_begin/ota_write/ota_end(under 10 s; the old firmware switches and restarts atota_end, 2.x needsota_activate). A documented update path for users is a follow-up.Separately, this board's C6 joins slowly or not at all on 2.4 GHz channels 6–9. Against a test AP 1 m away:
It reproduces with Waveshare's own
04_wifistationexample and with plain ESP-IDF. No clear improvement came from changing the co-processor firmware, the SDIO clock or stopping the display, nor from moving the PSRAM clock source from 400 to 320 MHz (CONFIG_SPIRAM_SPEED_80M, which also slows a full-screen refresh to ~53 ms, so the build stays at 200 MHz). Reported as waveshareteam/ESP32-P4-WIFI6-Touch-LCD-4.3#7. Commit 8 gives the board 30 s to connect, so the slow channel-6 joins succeed when they happen.Not yet
ES7210 microphones, the TF card slot (slot 0 needs the P4's on-chip LDO channel 4 switched on, which
machine.SDCardcannot do), the MIPI-CSI camera.Flashing
USB-Serial-JTAG, so plain
esptool --chip esp32p4 --before default_resetworks (no TinyUSB caveats): full image at 0x0, app-only at 0x20000. Factory partition layout differs (the factory image is worth backing up first).🤖 Generated with Claude Code
With thanks to the scientists and engineers who did the hard, unglamorous work that got us here.