Skip to content

Add headless C3 build with virtual display over HTTP#1

Open
theNailz wants to merge 3 commits intofeature/lovyan-gfxfrom
feature/headless-c3
Open

Add headless C3 build with virtual display over HTTP#1
theNailz wants to merge 3 commits intofeature/lovyan-gfxfrom
feature/headless-c3

Conversation

@theNailz
Copy link
Copy Markdown
Owner

Summary

Adds an esp32c3-headless build environment for debugging display rendering on an ESP32-C3 without a physical screen. All drawing goes into an in-RAM LGFX_Sprite framebuffer, served as a live BMP image via HTTP.

  • New [env:esp32c3-headless] in platformio.ini with -D HEADLESS=1
  • In headless mode, tft points to an LGFX_Sprite instead of hardware — all draw calls captured
  • GET /screenshot streams a 240×280 BMP row-by-row (no large malloc, works on C3's limited SRAM)
  • GET /display serves a minimal HTML page with 2s auto-refresh — live virtual monitor in browser
  • Display function signatures widened from LGFX_Device& to LovyanGFX& (common base) so they accept both hardware and sprite targets
  • Hardware-specific calls (init(), invertDisplay()) use concrete _tft_instance directly
  • All 4 build environments pass: esp32s3, cyd, esp32c3, esp32c3-headless
  • Tested on device: C3 flashed and /display confirmed working in browser

What this is NOT

  • Not a production feature — debug/dev tool only
  • Does not affect any existing board or environment
  • Not related to double-buffering (separate issue)

Test plan

  • Build all 4 envs (esp32s3, cyd, esp32c3, esp32c3-headless)
  • Flash esp32c3-headless to C3 on COM6
  • Verify /screenshot returns valid 201KB BMP (200 OK)
  • Verify /display shows live virtual monitor in browser
  • OTA flash esp32s3 to S3 test device — confirm no regression

New [env:esp32c3-headless] skips physical display init (tft.init,
backlight) and instead allocates a 240x280 LGFX_Sprite as a RAM
framebuffer. The full rendering pipeline runs unchanged against the
sprite.

Adds GET /screenshot (serves sprite as BMP) and GET /display (HTML
page with 2s auto-refresh) so display output can be observed in any
browser without a physical screen attached.

Useful for debugging display logic on a bare C3 module.
- Change tft type from LGFX_Device to LovyanGFX (common base) so it can
  point to either hardware display or LGFX_Sprite in headless mode
- In HEADLESS mode, tft now targets the sprite framebuffer so all draw
  calls are captured for the /screenshot endpoint
- Stream BMP row-by-row instead of malloc'ing the full 197KB image,
  fixing OOM crash on ESP32-C3 (no PSRAM)
- Update all helper functions (gauges, anims, icons) to accept
  LovyanGFX& instead of LGFX_Device&
- Sprite now uses LY_W×LY_H (240×240) instead of hardcoded 240×280,
  saving 19KB of SRAM on the C3
- This frees enough heap for the ~59KB settings page to load at /
- Use LY_W/LY_H in screenshot rowBuf and /display HTML dimensions
- Add layout.h include to web_server.cpp
@theNailz theNailz mentioned this pull request Apr 1, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant