Cassette-futurism communicator/scanner prop for the CrowPanel Advance ESP32-P4 (hardware V1.0). Custom ESP-IDF firmware structured as a generic prop engine: stable firmware whose behavior is driven live over WiFi, so scenes/LEDs/screen text can be cued and iterated without reflashing.
Forked from example/V1.0/idf-code/Lesson09-LVGL_Lighting_Control.
| Module | Role |
|---|---|
components/bsp_display, bsp_illuminate, bsp_i2c |
Display + touch + backlight bring-up (reused from Lesson09) |
components/bsp_io |
Discrete on/off LEDs + debounced buttons (espressif/button) |
main/prop_engine |
The brain. Scene state machine + 10 Hz animation task; single source of truth, fans state out to observers |
main/prop_net |
WiFi AP+STA via the onboard ESP32-C6 (esp_hosted); STA credentials in NVS |
main/prop_api |
HTTP server: operator web console, REST /cmd, WebSocket /ws, OTA /ota |
main/prop_ui |
LVGL amber-on-black retro readout — a pure view of engine state |
Inputs (physical buttons, remote commands) all funnel through prop_engine, which drives both the LEDs
and the screen, so they never drift out of sync.
IDLE, SCANNING, SIGNAL_ACQUIRED, COMMS, ALERT — each defines status text + an animated LED pattern.
Requires ESP-IDF ≥ 5.4.2 (v6.0.1 recommended).
Windows:
Use the VS Code ESP-IDF extension terminal, or export.ps1 from your IDF install:
idf.py set-target esp32p4
idf.py build
idf.py -p <PORT> flash monitor # first flash over USB (UART0)Or run the helper: pwsh tools/dev.ps1 bf -Port COM7
Linux / Debian:
Source your export.sh script to activate the environment:
. ~/.local/esp/esp-idf/export.sh
idf.py set-target esp32p4
idf.py build
idf.py -p /dev/ttyUSB0 flash monitorOr run the helper: ./tools/dev.sh bf -Port /dev/ttyUSB0
The espressif/button, display, and WiFi components download automatically on first build
(declared in the idf_component.yml files).
Find the device IP from the serial log (or use the AP PROP-COMMS / pass scanner99, then 192.168.4.1).
# REST
curl -X POST http://<ip>/cmd -d '{"cmd":"scene","value":"SCANNING"}'
curl -X POST http://<ip>/cmd -d '{"cmd":"led","name":"alert","on":true}'
curl -X POST http://<ip>/cmd -d '{"cmd":"channel","value":"CH 04 / 147.55 MHz"}'
curl http://<ip>/state
# Operator cue board (scene buttons, live state): open http://<ip>/ in a browser
# WebSocket: ws://<ip>/ws — send the same JSON commands, receive state broadcastsCommand schema: see the top of main/include/prop_api.h.
CLI (recommended) — build + push in one command:
Windows (PowerShell):
pwsh tools/dev.ps1 ota # defaults to comm-unit-7.local
pwsh tools/dev.ps1 ota -DeviceHost 172.17.2.167 # explicit IP fallbackLinux / Debian (Bash):
./tools/dev.sh ota # defaults to comm-unit-7.local
./tools/dev.sh ota -DeviceHost 172.17.2.167 # explicit IP fallbackBrowser — open http://comm-unit-7.local/, scroll to FIRMWARE UPDATE, pick the
.bin (usually build/communicator.bin), click UPLOAD. A progress percentage appears
and the page reports "done — rebooting…" when the device is switching over.
Raw curl (if you prefer):
idf.py build
curl -X POST "http://<ip>/ota?token=prop-ota-2024" --data-binary @build/communicator.binRollback safety: the bootloader is configured with CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE.
A freshly-OTA'd image boots as PENDING_VERIFY; app_main calls
esp_ota_mark_app_valid_cancel_rollback() after the full stack is up. If the new image
crashes before reaching that point the bootloader automatically reverts to the previous
partition on the next reset — no USB reflash required.
Confirm the update landed: GET /state includes a "version" field (from git describe), and the web console state line shows v<version>.
Change PROP_OTA_TOKEN in main/include/prop_api.h and the AP password in
main/include/prop_net.h before any real deployment.
GPIO assignments are placeholders in components/bsp_io/bsp_io.c. Only GPIO48
(the UART1-RX header pin) is confirmed broken-out and free. Verify every other pin against the V1.0
schematic in Eagle_SCH&PCB/1.0/ and the connector silkscreen before wiring. Defaults used:
- LEDs:
power=GPIO48,signal=GPIO47 (UART1-TX),alert=GPIO20 - Buttons (active-low, internal pull-up):
mode=GPIO33 (UART3-IN),action=GPIO11
Buttons: mode cycles scenes, action jumps to SCANNING (edit on_button() in main/main.c).
Builds clean on ESP-IDF v6.0.1 (target esp32p4). The Lesson09/17 examples were written for 5.4.2; this project was migrated to 6.0. Changes required for 6.0 (all already applied):
- cJSON moved out of IDF core → added
espressif/cjsondependency;REQUIRES cjson(notjson). - WiFi stack:
esp_wifi_remotebumped to 1.x (0.x referenced the removedesp_interface.h); paired withesp_hosted2.12.9. drivercomponent split → BSP components now requireesp_driver_i2c/esp_driver_gpio/esp_driver_ledc.- esp_lcd DPI API:
lcd_color_rgb_pixel_format_t→lcd_color_format_t,LCD_COLOR_PIXEL_FORMAT_*→LCD_COLOR_FMT_*,.pixel_format→.in_color_format/.out_color_format,.flags.use_dma2dremoved, RGB666 enum gone. - Stricter warnings-as-errors: fixed a header-guard typo in
bsp_i2c.h. sdkconfig.defaults: 16 MB flash +CONFIG_LV_FONT_MONTSERRAT_24.
Compiles successfully (communicator.bin builds). Not yet flashed/run on hardware — pending the
CH341 USB-serial driver + the board appearing on a COM port. Then work through the verification checklist
in the project plan.