# Cardputer Adv

> Build, flash, and debug firmware for the M5Stack Cardputer-Adv (K132-Adv, Stamp-S3A / ESP32-S3FN8) and the original Cardputer on macOS or Linux with PlatformIO + Arduino + M5Unified/M5Cardputer. Use when a task involves Cardputer hardware or firmware - scaffolding a PlatformIO project for it, fixing a boot loop / black screen / reboot cycle after flashing, choosing DIO vs QIO flash mode, the ESP32-S3 native USB-Serial-JTAG traps (esptool version, entering download mode, a serial port open resetting the chip), driving the 1.14" ST7789 display, the TCA8418 I2C keyboard, the ES8311 audio codec, or the RGB LED and backlight pins.

- Skill: `liukjx/cardputer-adv` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add liukjx/cardputer-adv`
- Raw SKILL.md: https://api.skillmd.com/api/skills/liukjx/cardputer-adv/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: liukjx (https://skillmd.com/u/liukjx)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/liukjx/cardputer-adv

---


# M5Stack Cardputer-Adv firmware development

Cardputer-Adv (SKU K132-Adv) = **Stamp-S3A = ESP32-S3FN8**: dual-core LX7 @240 MHz,
**8 MB flash, no PSRAM**, ESP32-S3 **native USB** (USB-Serial/JTAG, VID:PID `303a:1001`),
1.14" **ST7789** 240×135, **TCA8418** I2C keyboard controller, **ES8311** audio codec.

Everything in this skill was verified on real hardware (2026-09, macOS 15, PlatformIO 6.2,
Arduino core 2.0.16 / espressif32@6.7.0). Timings, pins and failure modes are measured, not copied.

## Follow this workflow

1. Confirm the board enumerates: `ls /dev/cu.usbmodem*` and `ioreg -p IOUSB -w0 | grep -i jtag`.
2. Scaffold the PlatformIO project from [references/toolchain.md](references/toolchain.md).
   The `dio` flash mode line is **not optional** — see rule 1.
3. Let **M5Unified auto-detect the board**. Do not hand-initialise the panel, keyboard or
   codec; `M5Cardputer.begin()` already does all of it for `board_M5CardputerADV`.
4. Do **not** call `Serial.begin()` in the default build (rule 2). Report progress with the
   **RGB LED** instead — it is the only status channel that survives a hang.
5. Build, then flash with [scripts/flash.sh](scripts/flash.sh) (esptool 5.x, not PlatformIO's).
6. Physically power-cycle the board (unplug/replug USB) and read the LED stage before
   concluding anything about your code.
7. Only then debug functionally, with [references/debugging.md](references/debugging.md)
   as the symptom → cause table.

## Non-negotiable rules

Each of these cost a full debugging session; they look like "my code is broken" but are not.

| # | Rule | What it looks like when violated |
|---|------|----------------------------------|
| 1 | `board_build.flash_mode = dio`. **Never `qio`**, even though the stock `esp32-s3-devkitc-1` board definition defaults to it - and note that M5Stack's own PlatformIO snippet in the product PDF omits this line, so the official recipe inherits `qio`. | Endless reboot, black screen. The ROM prints `boot:0x3 (DOWNLOAD(USB/UART0))` — the app never runs at all. With `dio` the same image prints `boot:0xb (SPI_FAST_FLASH_BOOT)` and boots. |
| 2 | Do **not** call `Serial.begin()` unless a host is actively draining the port. With `ARDUINO_USB_CDC_ON_BOOT=1` the CDC write path **blocks once the TX buffer fills**, hanging `setup()` before any M5 init. | App hangs with the RGB LED stuck on the first colour you set. Guard serial output behind a `MIDI_SERIAL_DEBUG`-style macro that defaults to 0. |
| 3 | Do **not** open `/dev/cu.usbmodem*` from a host script while the app is supposed to keep running. Opening the port toggles DTR/RTS, and the ESP32-S3 interprets that as a **reset pulse**. | A perfect fake crash loop: every read attempt yields a ROM banner. This misleads diagnosis badly — see [references/debugging.md](references/debugging.md). |
| 4 | Flash with **esptool 5.x**. PlatformIO's bundled esptool 4.5.1 **cannot sync** with this board. | `A fatal error occurred: No serial data received` while 5.x talks to the same port fine. |
| 5 | Entering download mode needs a **physical** power cycle (unplug USB). esptool's own reset leaves the ROM downloader latched. | After a successful flash the app never starts until you unplug/replug, so "the flash worked but nothing runs". |
| 6 | Charge with the side switch **ON** (M5 documentation); `OFF` is the download-mode position. | Board does not charge / behaves inconsistently when powered from USB. |

**Corollary of rules 2+3+5:** if the running app does **not** own the USB peripheral
(because it never called `Serial.begin()`), then esptool's auto-reset works again and
[scripts/softboot.py](scripts/softboot.py) can restart the app without a physical replug.
That is why rule 2 pays for itself.

## Hardware quick reference

```
Display  ST7789V2 240x135  RST=G33  DC=G34  MOSI=G35  SCK=G36  CS=G37  BL=G38 (PWM)
Audio    ES8311 (I2C addr 0x18, shared bus G8/G9)
         I2S: BCK=G41  WS/LRCK=G43  DATA out=G42  DATA in (mic)=G46
Keyboard TCA8418RTWR on the same I2C bus (4x14 = 56 keys), INT = G11
IMU      BMI270 (I2C, ADV only - absent on the original Cardputer)
IR       emitter = G44
Battery  ADC = G10
microSD  CS=G12  MOSI=G14  CLK=G40  MISO=G39  (SPI bus shared with the EXT port)
Grove    HY2.0-4P: G1 / G2  (+5V, GND)   EXT 14P: RESET=G3 INT=G4 BUSY=G6 UART_TX=G13 UART_RX=G15
RGB LED  data = G21, and G38 must be driven HIGH first (it also drives the backlight)
Boot     G0 = download-select button; side switch OFF = download position
```

Full official pin table, EXT/Grove bus, and Cardputer vs Cardputer-Adv differences:
[references/hardware.md](references/hardware.md).
Verified official documentation URLs, the exact official procedures (download mode, charging,
RGB LED rail), and how to read the SPA-hosted docs with a plain fetch:
[references/official-docs.md](references/official-docs.md).

## Toolchain recipe (PlatformIO, working baseline)

```ini
[env:cardputer-adv]
platform = espressif32@6.7.0
board = esp32-s3-devkitc-1
framework = arduino
board_build.flash_size = 8MB
board_build.flash_mode = dio          ; <- rule 1
board_build.partitions = default_8MB.csv
board_upload.flash_size = 8MB
upload_speed = 460800                 ; 1500000 fails on this USB bridge
build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 -DCORE_DEBUG_LEVEL=1 -Wall
lib_deps =
    https://github.com/m5stack/M5Unified.git
    https://github.com/m5stack/M5GFX.git
    https://github.com/m5stack/M5Cardputer.git
```

Pin the three libraries by commit when reproducibility matters. Details and the
flash/boot procedures: [references/toolchain.md](references/toolchain.md).

## Library-level notes worth knowing up front

- **`M5Unified` knows this board**: `board_t::board_M5CardputerADV` is auto-detected by M5GFX
  via GPIO/I2C probing. Never hard-code pins for the panel, keyboard or codec.
- **`M5Cardputer` is the convenience wrapper**: `M5Cardputer.begin(cfg, true)` = `M5.begin()` +
  `Keyboard.begin()`; `M5Cardputer.update()` pumps both. It exposes `Display`, `Speaker`,
  `Mic`, `Keyboard`, `BtnA`.
- **The default tone waveform is a 16-point sine**, which a 1 W speaker cannot radiate below
  roughly 300 Hz — low notes are effectively silent. Pass a **sawtooth** table to
  `Speaker.tone(freq, dur, ch, stop, raw, len)` instead.
- `Speaker_Class` gives 8 virtual channels; `stop_current_sound=true` stops **only the target
  channel**, so per-note voice allocation gives clean polyphony.

API details, keyboard `KeysState` fields and the waveform tables:
[references/m5-library-notes.md](references/m5-library-notes.md).

## Reusable resources

- [scripts/flash.sh](scripts/flash.sh) — flash a PlatformIO build with esptool 5.x (correct offsets).
- [scripts/softboot.py](scripts/softboot.py) — restart the app over DTR/RTS instead of replugging.
- [scripts/serial_capture.py](scripts/serial_capture.py) — one-shot capture that documents the
  reset-on-open trap instead of walking into it.
- [references/official-docs.md](references/official-docs.md) — verified M5Stack doc URLs, the
  PDF fallback for the JavaScript docs site, verbatim official procedures, and the two places
  where M5Stack's own published recipe differs from a configuration that actually boots.
- [examples/midi-keyboard/](examples/midi-keyboard/) — a complete, hardware-verified app:
  8-voice polyphonic synth, TCA8418 keyboard mapping, chord recognition, custom waveforms,
  LED stage reporting, and an M5GFX UI. Use it as the reference implementation.

