- name
- flash
- description
- Build, flash, and monitor the eink-screen firmware on the LilyGo T5 ePaper S3 PRO, with recovery for the recurring USB-port-drop and wrong-port-auto-detect gotchas and a serial-capture recipe. TRIGGER when the user wants to build/compile, upload/flash, or watch serial for this firmware ("flash it", "upload", "build and run", "check the serial log", "why won't it flash"). DO NOT trigger for editing the display layout (that's HelmLog / `/preview`) or for pure code questions.
# /flash — build, flash, monitor
The build/flash commands and the display/flashing gotchas are in `AGENTS.md`;
this skill is the **operational runbook** for getting firmware onto the board
when the native USB-CDC port misbehaves.
## Normal path
```bash
pio run # compile
pio run -t upload --upload-port /dev/cu.usbmodem2101 # flash (ALWAYS pass the port)
```
Always pass `--upload-port`. esptool auto-detect regularly grabs the wrong
`/dev/cu.*` (e.g. Bluetooth headphones) and fails with "No serial data
received".
## When the port is missing / upload can't open it
`ls /dev/cu.usbmodem*` shows nothing, or upload says "port doesn't exist / busy".
1. If a serial monitor is open, close it (it holds the port).
2. Ask the user to **re-plug the USB-C cable**, or enter **download mode**:
hold **BOOT**, tap **RST**, release **BOOT**.
3. Re-check `ls /dev/cu.usbmodem*`, then retry the upload with `--upload-port`.
The port dropping right after a `Hard resetting via RTS pin` is normal for this
board — it re-enumerates. Don't over-debug it; just recover the port.
## Reading serial reliably
`pio device monitor` needs a TTY and fails when run headless/scripted. Capture
with a reconnecting pyserial reader instead (firmware `Serial` is on USB CDC):
```python
import serial, time
deadline = time.time() + 30
while time.time() < deadline:
try: s = serial.Serial('/dev/cu.usbmodem2101', 115200, timeout=0.3)
except Exception: time.sleep(0.2); continue
try:
while time.time() < deadline:
line = s.readline().decode('utf-8','replace').rstrip()
if line: print(line)
except Exception: pass
finally:
try: s.close()
except Exception: pass
```
Run it with PlatformIO's Python so pyserial is available:
`/Users/dweatbrook/.local/share/uv/tools/platformio/bin/python`.
Do **not** toggle DTR/RTS to "reset" — the wrong pattern drops the board into
download mode (`waiting for download`) instead of running the firmware. To catch
a clean boot, start the reader and have the user tap **RST** (only RST).
## What a healthy boot looks like
```
[HelmLog e-paper display]
E epdiy: cache line size 32 (<64B) … Reducing pixel clock … 5 MHz <- harmless
[epd] 960x540 framebuffer=0x...
[wifi] trying <ssid> ... joined ... probing <host>:3002
[wifi] <ssid>: HelmLog reachable -> using it
[http] frame ok; refresh=Ns full=Ns
```
If you see `i2c driver install error`, something called `Wire.begin()` — the
panel won't power on. Remove it (see `AGENTS.md`).
## First-build environment notes
PlatformIO here runs under a uv-installed Python; if `tool-esptoolpy` install
fails for lack of `pip`, bootstrap it once:
`/Users/dweatbrook/.local/share/uv/tools/platformio/bin/python -m ensurepip --upgrade`.
The `usc1.contabostorage.com` package CDN sometimes TLS-blocks but resolves via
mirror fallback — retry rather than abort.
عرض على GitHub