You've already read start-here.html — the narrative tour.
This page is the reference manual: every launch.sh option, every platformio.ini directive,
every "wait, what's actually happening here?" — explained with the underlying commands, failure modes, and cross-links to the audit log.
launch.shThe launch.sh menu has 20 numbered options grouped into sections. Numbers always work — letters (s, b, w, g) just expand the visual section.
1, 2, 3, then opening VS Code.act_bootstrap() — calls act_check, act_install_pio, act_install_deps in order.winget install Python.Python.3.13 auto-magically. On macOS/Linux, you need to install Python 3.10+ manually first.pacman -S mingw-w64-ucrt-x86_64-python.act_check() — wraps have python, have pio, version probes.pio available as a CLI command.pip install -U platformiopio may install to a directory not on your PATH. The launcher works around this by always invoking via python -m platformio. To get bare pio working, add the suggested export PATH=... line to your ~/.bashrc.platformio.ini. Pinned: platform 6.7.0 (ESP-IDF 5.1).pio pkg install~/.platformio/packages/)..pio/build/esp32-c3-devkitm-1/.pio runbootloader.bin (offset 0x0) · partitions.bin (0x8000) · firmware.bin (0x10000) + an .elf with debug symbols.board_build.partitions..pio/build/ directory. Forces a from-scratch recompile next time.pio run -t cleanbuild_flags or lib_deps — incremental builds may not pick up the change.pio run -t upload --upload-port COM8 (Windows) — port detected automatically or via option 9.sudo usermod -aG dialout $USER.esptool --chip esp32c3 --port COM8 erase_flashSerial.println() messages, crash dumps.pio device monitor -b 115200 --port COM8$PORT.pio device list + interactive prompt.PORT=COM7 ./launch.sh uploadact_compile && act_upload && act_monitor — chained with && so failure stops the chain../launch.sh flash — same thing, no menu.code . + optional code --install-extension platformio.platformio-idecode command on PATH. Install via VS Code's command palette → "Install 'code' command in PATH".pio home → opens http://localhost:8008/02_web/ on localhost:8000
WEB
02_web/ and opens the browser. Lets you use flash.html + monitor.html (Web Serial requires HTTPS or localhost).cd 02_web && python -m http.server 8000http://<your-ip>:8000/ from their phones / tablets / chromebooks.02_web/firmware/ so flash.html can install them via Web Serial.pio run + cp .pio/build/esp32-c3-devkitm-1/{bootloader,partitions,firmware}.bin 02_web/firmware/03_logs/ (past command outputs). Each ./launch.sh action with with_timer wrapping logs to a timestamped file.lib_deps entry and the platform itself, respecting your version pins. Optionally upgrades PlatformIO Core (with confirmation).pio pkg update + optional pio upgradefastled/FastLED @ ^3.7.0 only allow patch upgrades. Updating ESP-IDF was the cause of BUG-001 in the first place — pinning helps, but always test after updating.git statusgit log --oneline --decorate -20less if available.git diff HEAD./launch.sh compile · ./launch.sh upload · ./launch.sh monitor · ./launch.sh flash · ./launch.sh serve · ./launch.sh package-web. Skip the menu when you know what you want.
platformio.ini decoded — every directiveAnnotated walk-through of every line in this project's platformio.ini, with explanation and gotchas.
| Directive | Value | What it does |
|---|---|---|
src_dir |
01_src |
Where source files live. Default is src/; we override to 01_src/ so Arduino IDE folder-name rule still works. |
| Directive | Value | What it does |
|---|---|---|
platform |
espressif32 @ 6.7.0 |
Pinned — version 6.7.0 ships ESP-IDF 5.1 with the rewritten RMT driver. Earlier versions had the ISR re-entry race that crashed FastLED on C3 (BUG-001). The @ X.Y.Z syntax means EXACT version. Without the pin, you'd get whatever's latest at install time → silent regressions. |
board |
esp32-c3-devkitm-1 |
Selects the board profile from PlatformIO's database. Determines: pin map, flash size (4 MB), partition layout, upload speed, default baud. The seeed_xiao_esp32c3 alternative (commented out) has a different pin map. |
framework |
arduino |
Activates the Arduino-ESP32 API: setup()/loop(), pinMode(), Serial.println(), etc. Built on top of ESP-IDF — you can mix and match. The espidf alternative (commented) gives raw IDF with no Arduino layer. |
monitor_speed |
115200 |
Baud rate the serial monitor uses. Must match what your firmware calls in Serial.begin(). Wrong baud → garbage characters. |
| build_flags | ||
FASTLED_RMT_MAX_CHANNELS |
1 |
FastLED defaults to 8 RMT channels (ESP32 classic has 8). The C3 has only 2. Capping to 1 prevents FastLED from trying to claim non-existent channels. Safety: belt-and-suspenders even with the IDF 5.1 fix. |
FASTLED_RMT_BUILTIN_DRIVER |
1 |
Tells FastLED to use IDF's tested RMT driver instead of FastLED's bundled custom one. The custom driver was the cracked egg in BUG-001; this flag opts out of it. |
| lib_deps | ||
RemoteXY |
(unconstrained) | Phone-app library for Bluetooth control. NOT pinned because pinning didn't fix BUG-004 (PRO check is enforced server-side, not in the lib). |
fastled/FastLED |
@ ^3.7.0 |
NeoPixel driver. ^3.7.0 means "3.x but ≥ 3.7" — patch updates allowed, major upgrades blocked. |
madhephaestus/ESP32Servo |
(unconstrained) | Servo PWM driver. Author prefix prevents PIO from grabbing a different lib of the same name. |
adafruit/Adafruit GFX Library |
(unconstrained) | Generic 2D drawing primitives — the OLED driver depends on it. |
adafruit/Adafruit SSD1306 |
(unconstrained) | Concrete OLED driver for the SSD1306 chip on our display. |
| Directive | What it would do |
|---|---|
upload_speed | Default for the C3 is auto-negotiated at 921600. Override only if you have flaky USB cables. |
upload_protocol | esp-builtin uses USB-JTAG instead of the CDC reset dance. Could fix BUG-005. |
monitor_filters | esp32_exception_decoder auto-runs addr2line on crashes. Highly recommended for debugging. |
board_build.partitions | Custom partition table CSV. Default is a balanced 1.2 MB app. huge_app.csv gives 3 MB if you don't need OTA. |
board_build.usb_cdc | Toggles USB CDC console (Serial-over-USB). Already enabled by default for C3 boards with native USB. |
extra_scripts | Run Python scripts at compile time — e.g. inject git hash into firmware version string. |
@ 6.7.0): platform changes mean toolchain + IDF changes — high blast radius.@ ^X.Y.Z): patch fixes welcome, breaking changes blocked.Hardware peripheral for generating precise pulse trains without CPU involvement. Originally for IR remotes, but FastLED uses it to drive WS2812 NeoPixels at 800 kHz with sub-150-ns timing — impossible to bit-bang reliably.
| Chip | RMT channels | Notes |
|---|---|---|
| ESP32 classic | 8 | Lots of headroom, FastLED has no issues. |
| ESP32-S2 / S3 | 4 | Comfortable. |
| ESP32-C3 (this board) | 2 | Tight. Any IR receiver or second LED strip directly competes. |
| ESP32-C6 | 2 | Same as C3. |
The full forensic — including the IDF 4.x ISR re-entry bug that triggered BUG-001 — is in the audit page.
Flash memory is divided into named regions. The bootloader reads partitions.bin at offset 0x8000 to know where everything else lives.
# Default Arduino-ESP32 layout (4 MB flash):
nvs data 0x9000 0x5000 # 20 KB — saved settings
otadata data 0xe000 0x2000 # 8 KB — OTA pointer
app0 app 0x10000 0x140000 # 1.25 MB — your firmware
app1 app 0x150000 0x140000 # 1.25 MB — OTA backup
spiffs data 0x290000 0x170000 # 1.4 MB — file system
If you don't need OTA, switch to huge_app.csv (3 MB single app slot). For audio assets, switch to large_spiffs.csv.
The ESP32-C3 Super Mini has native USB — the chip itself implements USB-CDC over its built-in USB JTAG/Serial peripheral. There is NO external UART chip (no CP210x, no CH340).
| Aspect | External UART (e.g. CP210x) | Native USB CDC (this board) |
|---|---|---|
| Speed | Up to 921 600 baud | ~12 Mbps theoretical |
| Drivers (Windows) | Manual install required | Built into Win 10/11 |
| Reset behavior | External chip toggles RTS/DTR | Chip resets itself · COM port disappears + re-enumerates |
| Failure mode | Driver mismatch | Re-enumeration race (BUG-005) |
Some GPIOs have special meaning at boot. Using them for sensors / LEDs without thinking can prevent the chip from booting.
| GPIO | Boot role | Audit ref |
|---|---|---|
2 | Strapping — VDD_SPI voltage select. Pulled at boot. | — |
8 | Strapping + onboard LED on the Super Mini (active LOW, blue). Conflicts with I²C SDA without a pull-up. | H1 |
9 | Strapping + BOOT button. Pulled LOW at power-on enters ROM bootloader. | H2 |
The exact dev-board this project uses. Reference page: randomnerdtutorials.com / Getting Started with ESP32-C3 Super Mini. Things that bite you on this specific board:
| Quirk | What & why | Mitigation |
|---|---|---|
| Onboard LED is active LOW | The blue LED on GPIO 8 is wired so that digitalWrite(8, LOW) turns it on, HIGH turns it off. Standard blink sketch looks inverted. |
Invert your logic, or move status indicator to another GPIO. |
| Antenna defect (some batches) | Wi-Fi range is poor on certain production runs. Cause: the cap labelled C3 sits ~1.5 mm from the antenna trace instead of the design spec ~3.3 mm — detunes the matching network. |
Inspect under magnification before assembly. If it's a defective batch, the only real fix is switching boards. |
| Breadboard kills Wi-Fi | Metal contacts under the antenna detune it. Range drops to a few metres. | Raise the board 15 + mm above the breadboard with stacking headers, or mount it on the edge so the antenna overhangs. |
| Power LED drains the battery | The red "power on" LED steals ~500 µA in deep sleep — about 10× the chip's own minimum. | Desolder the LED (or its current-limit resistor) for battery-powered builds. Brings sleep current to the datasheet's ~43 µA. |
| USB CDC On Boot must be enabled | The board has no external UART chip — only the chip's native USB-Serial/JTAG. Without the right Arduino IDE setting, Serial.print() goes nowhere. |
Arduino IDE → Tools → USB CDC On Boot → Enabled. PlatformIO sets this automatically for the esp32-c3-devkitm-1 board. |
| Deep-sleep upload trap | If your firmware enters deep sleep within seconds of boot, esptool can't sync — the chip is asleep before the auto-reset finishes. | BOOT/RESET trick (hold BOOT, tap RST, release BOOT) before retrying upload. Or build a delay-on-boot guard into firmware before the sleep call. |
| UART defaults: GPIO 20 (RX) / 21 (TX) | If you use Serial1 or expose a hardware UART, these are the default pins on the Arduino-ESP32 core. |
Or remap with Serial1.begin(baud, mode, RX, TX) to any free GPIO. |
When the ESP32-C3 panics, you get a "Guru Meditation" register dump in the serial monitor. Here's how to turn the hex into source-line info.
Guru Meditation Error: Core 0 panic'ed (Instruction access fault)
Core 0 register dump:
MEPC : 0x3fc9027c # where it died
RA : 0x4038c53c # return address (where it would have gone)
SP : 0x3fc8f8d0 # stack pointer at crash
MCAUSE : 0x00000001 # why it died
MTVAL : 0x3fc9027c # offending address (load/store cause)
| MCAUSE | Name | Means |
|---|---|---|
0x01 | Instruction access fault | Tried to fetch code from non-executable memory. Classic stack-overflow signature (RA pointed into RAM). |
0x02 | Illegal instruction | CPU saw bytes that don't decode as RISC-V. Usually corruption — bad function pointer. |
0x05 | Load access fault | Tried to read from invalid memory. Look at MTVAL. |
0x07 | Store access fault | Tried to write to invalid memory. Buffer overrun on a global, or null-pointer write. |
0x0B | Environment call | Software ecall — usually intentional in ISR. |
| Range | Meaning | Action |
|---|---|---|
0x4038xxxx – 0x403F | IROM — flash code | Decode with addr2line. |
0x4080xxxx | IRAM — code in RAM (interrupt handlers) | Decode with addr2line. |
0x3FC0xxxx – 0x3FCF | DRAM — data + stack | If MEPC points here = stack overflow / corrupted return address. |
0x3C0xxxxx | DROM — read-only data in flash | If MEPC points here = jumped into a string constant. Bad function pointer. |
# Linux / macOS / Git Bash:
~/.platformio/packages/toolchain-riscv32-esp/bin/riscv32-esp-elf-addr2line \
-fipe .pio/build/esp32-c3-devkitm-1/firmware.elf \
0x4038c53c 0x403801ee 0x4038907e
# Output:
ESP32RMTController::interruptHandler at .../FastLED/.../idf4_rmt_impl.cpp:827
vPortEnterCritical at .../freertos/port/riscv/port.c:246
xPortEnterCriticalTimeout at .../freertos/port/riscv/port.c:198
| Flag | What |
|---|---|
-f | show function name |
-i | show inlined-function chain |
-p | pretty-print (one line per address) |
-e <path> | ELF file path |
-C | demangle C++ symbols (try if functions look mangled) |
0x3FCxxxxx) + repeating stack frames at fixed offsets + decrementing counter. Exact pattern of BUG-001. Fix: reduce recursion depth, increase stack size, or fix the runaway loop.
0x3FC8xxxx) but the object should still exist. Use-after-free or buffer overrun. Try CONFIG_HEAP_POISONING_COMPREHENSIVE in sdkconfig.
monitor_filters = esp32_exception_decoder to platformio.ini and the serial monitor will auto-run addr2line on every crash. Saves the manual step.
The Web Serial API (W3C) lets a webpage talk directly to USB-serial devices — no plugins, no native install. flash.html and monitor.html use it.
| Browser | Support |
|---|---|
| Chrome 89+ | ✅ Yes |
| Edge 89+ | ✅ Yes |
| Opera 76+ | ✅ Yes |
| Firefox | ❌ No (philosophical objection — Mozilla considers Web Serial too dangerous) |
| Safari | ❌ No |
// 1 — request a port (must be in a click handler)
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
// 2 — read a stream of bytes
const decoder = new TextDecoderStream();
port.readable.pipeTo(decoder.writable);
const reader = decoder.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(value); // chunk of UTF-8 text
}
// 3 — write
const writer = port.writable.getWriter();
await writer.write(new TextEncoder().encode("hello\n"));
writer.releaseLock();
// 4 — close
await port.close();
Got a topic to add? Open an issue: github.com/abourdim/robot_1/issues · Other guides: user · build · instructor · start-here