tamagotchi

Flashing the firmware

Install the toolchain, build the sketch, upload to the ESP32-C3 SuperMini.

The firmware is plain C++ on the Arduino core, built and uploaded entirely from the terminal with arduino-cli. There's no Arduino IDE involved.

If you haven't installed arduino-cli and the ESP32 core yet, do that on the Setup page first. This page covers the day-to-day build/flash workflow once your toolchain is in place.

Before the daily workflow, make sure the board is visible:

make ports

You should see something like /dev/cu.usbmodem14101 (macOS), /dev/ttyACM0 (Linux), or COM5 (Windows) tagged with esp32:esp32:esp32c3. The trailing digits vary by machine and USB port — match on the usbmodem / ttyACM / COM prefix and the esp32c3 tag, not the exact number. If nothing shows up, see Troubleshooting.

Daily workflow

The repo's Makefile wraps every common operation. The port is auto-detected; you only need PORT=... if you have multiple boards plugged in.

CommandWhat it does
make buildCompile the sketch
make flashCompile + upload
make monitorOpen the serial monitor (115200, Ctrl-C exits)
make flash-monitorFlash, then immediately attach the monitor
make dbRegenerate compile_commands.json for clangd
make formatAuto-format all sources
make portsList connected ESP32 boards
make cleanDelete build artifacts
make eraseWipe the entire flash (factory reset)

The first build takes ~30 seconds. Subsequent rebuilds are 2–5 seconds (arduino-cli caches aggressively).

When you run make monitor you'll land on a line like:

Connecting to /dev/cu.usbmodem101. Press CTRL-C to exit.

That's it — you're connected. The monitor stays attached and streams the board's serial output; hit Ctrl-C when you want to detach.

Wiping the board

make clean only deletes the host-side build folder — it doesn't touch the chip. To wipe the board itself (the app, plus saved NVS data like wifi credentials), use make erase:

make erase

It finds esptool on your PATH — or falls back to the copy bundled with the ESP32 core, so there's nothing extra to install — frees the serial port if the gochi daemon is holding it, and chip-erases the flash. The board is blank afterwards; run make flash to put the firmware back.

If it hangs at "Connecting…", force download mode (hold BOOT, tap RESET, release BOOT) and re-run. Pass PORT=... if you have more than one board plugged in.

Use make flash-monitor 90% of the time. It builds, uploads, and drops you into the serial console so you can immediately interact with the pet over the serial protocol.

OLED I²C address

The SSD1306 modules in the kit speak to address 0x3C. A small number of clones use 0x3D instead. If the screen wakes up black after a successful flash, run the I²C scanner sketch (one terminal command):

make flash SKETCH=examples/i2c_scan

Then make monitor — you'll see the address printed. If it's 0x3D, edit firmware/src/config.h and change OLED_I2C_ADDR to match.

Force download mode

The ESP32-C3 SuperMini occasionally won't enter the bootloader on its own. Symptoms:

  • make flash hangs at "Connecting...___"
  • make ports shows the board, but uploads time out

Fix:

Hold the BOOT button.
Tap RESET while still holding BOOT.
Release BOOT.
Re-run make flash.

You only have to do this once per power-cycle; after a successful upload the board reboots into your code and accepts future flashes normally.

Flashing on Linux / WSL2

Flashing from a native Linux host is straightforward. WSL2 is a lightweight VM with its own kernel, so USB serial devices need extra plumbing that a native host doesn't. Pick your environment:

On a real Linux box it usually just works once the board is plugged in — the Makefile's autodetect already probes the Linux device names (/dev/ttyACM*, /dev/ttyUSB*):

make ports          # confirm the board shows up, e.g. /dev/ttyACM0
make flash          # autodetects PORT; pass PORT=/dev/ttyACM0 if you have several boards

If you hit Permission denied: /dev/ttyACM0, add yourself to the dialout group (then log out and back in so it takes effect):

sudo usermod -aG dialout $USER

Need make format? Install clang-format at user level — no root, lands in ~/.local/bin:

pip install --user clang-format   # apt alternative: sudo apt install clang-format

WSL2 works, but the ESP32-C3's USB re-enumeration on reset makes it finicky.

If WSL keeps fighting you, flashing from native Windows arduino-cli is the more reliable path — it skips the usbipd hop entirely. Everything below is for when you specifically want to flash from inside WSL.

Forward the USB device into WSL with usbipd-win. WSL2 can't see Windows USB devices by default.

# Windows PowerShell (Admin) — one time:
winget install usbipd

# Each session: find the board, bind once, then attach.
usbipd list                                  # note the ESP32's BUSID, e.g. 2-4
usbipd bind   --busid 2-4                     # one-time per device
usbipd attach --wsl --busid 2-4 --auto-attach # --auto-attach survives re-enumeration

--auto-attach matters: the ESP32-C3's native USB re-enumerates often, and without it the attach drops and won't come back on its own.

Load the CDC-ACM serial driver in WSL. It ships with the WSL kernel but isn't loaded by default, so no /dev/ttyACM* node is ever created even after a successful attach:

sudo modprobe cdc_acm            # once per boot

Verify with lsmod | grep cdc_acm. An attached board then shows up as /dev/ttyACM0 (confirm in dmesg: cdc_acm 1-1:1.0: ttyACM0: USB ACM device).

If the board reset-loops, force download mode. If dmesg shows the device number climbing rapidly (re-enumerating every ~0.6s, each lasting under a second), the firmware is in a crash / watchdog boot-loop and usbipd can never hold the attach. Force download mode — hold BOOT, tap RESET, release BOOT — so the stable ROM bootloader runs instead. The usbipd flapping goes quiet and /dev/ttyACM0 persists.

Fix serial-port permissions. usbipd-attached devices come up owned root:root mode 600 — the udev rule that normally hands them to the dialout group doesn't fire under WSL, so even a dialout user can't open the port:

sudo chmod 666 /dev/ttyACM0
make flash

The chmod resets every time the device re-attaches (it's a new node). For a permanent fix, add a udev rule so every ttyACM* comes up world-accessible:

echo 'KERNEL=="ttyACM[0-9]*", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ttyacm.rules
sudo udevadm control --reload-rules

(udev rules only fire if udev/systemd is actually running in your WSL distro.)

Need make format? Install clang-format at user level — no root, lands in ~/.local/bin: pip install --user clang-format (apt alternative: sudo apt install clang-format).

Cold-start cheat sheet

# Windows (Admin):
usbipd attach --wsl --busid 2-4 --auto-attach
# WSL:
sudo modprobe cdc_acm           # once per boot
# Hold BOOT, tap RESET, release BOOT  (puts board in download mode)
ls /dev/ttyACM*                 # confirm /dev/ttyACM0 is present & stable
sudo chmod 666 /dev/ttyACM0
make flash

Editor setup (optional but nice)

Any editor with clangd support gets full code intelligence — completion, go-to-definition, inline errors — via the compile_commands.json generated by make db.

VS Code users can install the clangd extension and it works out of the box. Other editors with clangd support work similarly — point them at compile_commands.json and treat .ino files as C++.

Don't install Microsoft's "C/C++" extension alongside clangd — they compete and you'll get duplicate squiggles.

Troubleshooting

SymptomTry this
make: command not foundmacOS: xcode-select --install. Linux: sudo apt install make
arduino-cli: command not foundThe binary isn't on your PATH. Run which arduino-cli
No board in make portsTry a different USB cable (many are power-only). Reboot the host.
Upload hangs at Connecting...Force download mode
Permission denied: /dev/ttyACM0 (Linux)sudo usermod -aG dialout $USER, then log out + back in
Permission denied: /dev/ttyACM0 (WSL2)dialout doesn't apply — sudo chmod 666 /dev/ttyACM0. WSL setup
No /dev/ttyACM* in WSL2 after attachingLoad the driver: sudo modprobe cdc_acm. WSL setup
usbipd attach keeps dropping (WSL2)Board is reset-looping — force download mode, use --auto-attach
Screen flickers after uploadLoose SDA/SCL joint — reflow the OLED end
Pet works for 5s then diesBrown-out: try a higher-quality USB cable or power source