🤖 explore · 🛠️ build · 🚀 deploy
// objective: understand the rig before touching it
RISC-V chip with built-in WiFi + Bluetooth. The brain of your robot.
The friendly C++ dialect with setup(), loop(), digitalWrite().
The build tool. Downloads libs, compiles, flashes, monitors. Driven from launch.sh.
Servos · NeoPixel LEDs · buzzer · OLED · ultrasonic · BT phone control
./launch.sh → option 0 → grab a snack. The launcher does everything.
// objective: know which file does what
NN_ prefix is reading order only. Compiler ignores it. Top-down = config → data → features → demos.
// objective: stop being mystified by .ino files
An .ino file is C++ with two cheats:
Auto-injects #include <Arduino.h> at the top — gives you pinMode, Serial, etc. for free.
Auto-generates forward declarations — call any function before defining it.
// WHAT YOU WRITE (.ino)
void setup() {
pinMode(LED, OUTPUT);
blink(); // OK in .ino
}
void loop() { delay(1000); }
void blink() {
digitalWrite(LED, HIGH);
}
// WHAT COMPILER SEES
#include <Arduino.h> // injected
void blink(); // injected
void setup() {
pinMode(LED, OUTPUT);
blink();
}
void loop() { delay(1000); }
void blink() {
digitalWrite(LED, HIGH);
}
setup() (runs once) and loop() (runs forever). The framework calls them — main() is hidden inside Arduino.h.
// objective: read the build orders
[env:esp32-c3-devkitm-1]
platform = espressif32 @ 6.7.0 ; PINNED — ESP-IDF 5.1, fixes RMT bug
board = esp32-c3-devkitm-1 ; pin map + flash size
framework = arduino ; activates Arduino API
monitor_speed = 115200 ; serial baud rate
build_flags =
-D FASTLED_RMT_MAX_CHANNELS=1 ; C3 has only 2 RMT slots
-D FASTLED_RMT_BUILTIN_DRIVER=1 ; use IDF's tested RMT driver
lib_deps =
RemoteXY ; phone control via BLE
fastled/FastLED @ ^3.7.0 ; NeoPixel (version pinned)
madhephaestus/ESP32Servo ; servo library
adafruit/Adafruit GFX Library ; OLED graphics
adafruit/Adafruit SSD1306 ; OLED driver
@ 6.7.0 = exactly this version. @ ^3.7.0 = 3.x but ≥ 3.7. Without pins, today's build differs from tomorrow's — silent regressions sneak in. Pinning = reproducibility.
platform = espressif32 shipped ESP-IDF 4.x with a buggy RMT ISR. NeoPixels caused random Guru Meditation crashes. Pinning to 6.7.0 (ESP-IDF 5.1) fixed it. See BUG-001.
// objective: stop typing commands. Press numbers.
Accordion menu: press a letter (s, b, w, g) to expand a section, or just type the number directly. Numbers always work whether expanded or not.
./launch.sh flash — compile + upload + monitor in one command.
./launch.sh compile./launch.sh upload./launch.sh monitor
PORT=COM7 ./launch.sh upload
forces a specific port
// objective: install everything, transparently
Pick option 0. The launcher runs four automatic levels:
If missing, runs winget install Python.Python.3.13 automatically. No clicks.
pip install -U platformio · ~30 MB download.
The big one. Arduino-ESP32 core + toolchain + libs · ~150 MB. Grab a snack.
Optional. Adds PlatformIO IDE in the editor sidebar.
pacman / python.org instruction.
// objective: see what happens when you press "compile"
Option 10 = full flash = compile + upload + monitor in one shot.
// objective: know what gets written where
The 3 binaries are written at fixed offsets in the chip's 4 MB flash:
| Segment | What it does |
|---|---|
| bootloader.bin | The first code that runs on power-up. Loads the partition table, then jumps to firmware. |
| partitions.bin | A tiny table saying "firmware lives here, OTA backup there, NVS data over there." |
| firmware.bin | YOUR sketch — every .cpp in 01_src/ compiled together. |
7 wipes ALL flash. Use after flashing weird firmware or when the chip acts haunted.
// objective: force the chip into bootloader mode manually
// the chip enters bootloader mode and accepts the next flash operation
// objective: let anyone flash via Chrome — no terminal required
./launch.sh package-web — builds + copies bins to 02_web/firmware/
./launch.sh serve — starts http://localhost:8000
Plug board → flash.html → click Connect & install. Done.
// objective: diagnose like a pro
/dev/cu.usbserial-*, not /dev/tty.*.sudo usermod -aG dialout $USER then log out/in.
/usr/bin/python3 is broken. Either install python.org Python (launcher auto-detects it) or:
pacman -Syu
pacman -S mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-python-pip
3 (install board files) or 16 (update libs).
python -m platformio. To fix permanently, paste the suggested export PATH=… into your ~/.bashrc.
// objective: turn a wall of hex into a function name
When the ESP32-C3 panics, you get a "Guru Meditation" register dump. Those 0x4038xxxx
addresses look like noise — but they're real instructions in your firmware.
The addr2line tool maps them back to filename.cpp:line_number.
This is the move that solved BUG-001.
Three registers to read first:
MEPC — where the CPU was when it died. 0x4038xxxx = inside flash code (decode!). 0x3fc8xxxx = inside RAM = corrupted return address.RA — where it was returning to. Decode this to find the caller.MCAUSE — why it died. 0x01 = invalid code jump. 0x05/0x07 = bad memory access. 0x02 = illegal instruction.# 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 0x40389094
# Output:
_global_interrupt_handler at .../riscv/interrupt.c:66
ESP32RMTController::interruptHandler at .../idf4_rmt_impl.cpp:827
vPortEnterCritical at .../freertos/port/riscv/port.c:246
-f = show function name · -i = inline expansion · -p = pretty print · -e = ELF file path
That's exactly what BUG-001 looked like: ESP32RMTController::interruptHandler repeating
9× with 192-byte frames = the ISR re-entering itself before iret, overflowing the stack in milliseconds.
addr2line still works — but decode against the same firmware.elf you flashed. A stale ELF gives wrong line numbers.
Run ./launch.sh monitor, reproduce the crash, copy the full register dump.
Feed MEPC, RA, and a few stack pointers through addr2line.
Look for repeating frames, decrementing counters, RAM-pointed MEPC.
// objective: skip the menu when you know what you're doing
# compile
python -m platformio run
# upload
python -m platformio run -t upload --upload-port COM3
# erase chip
python -m esptool --chip esp32c3 --port COM3 erase_flash
# serial monitor
python -m platformio device monitor -b 115200 --port COM3
# clean
python -m platformio run -t clean
# update everything
python -m platformio pkg update
If pio is on PATH, drop the python -m prefix.
// objective: build the same code without PlatformIO
The source in 01_src/ is plain Arduino C++ — Arduino IDE 2.x can compile it too.
You'll redo the config by hand (no platformio.ini in IDE land).
File → Open → pick 01_src/01_src.ino. Folder name must match the .ino name — Arduino IDE requirement.
Boards Manager → search esp32 → install esp32 by Espressif Systems
Library Manager: FastLED, ESP32Servo, Adafruit GFX Library, Adafruit SSD1306, RemoteXY
Tools → Board → ESP32 Arduino → ESP32C3 Dev Module
USB CDC On Boot = Enabled
Core Debug Level = Info
Upload Speed = 921600
Partition Scheme = Huge APP
Plug board → pick port → click ✅ Verify, then ➜ Upload. If it fails, try the BOOT/RESET trick.
| PlatformIO feature | Arduino IDE equivalent |
|---|---|
pinned lib_deps versions | Library Manager — no version pinning |
build_flags = -DXYZ=… | only what's in the Tools menu |
[env:debug] / [env:release] | none — change settings each time |
esp32_exception_decoder filter | none — crash dumps stay raw hex |
./launch.sh flash one-shot | Verify + Upload separately |
01_src/. Different toolchains, identical firmware.