m5-onboard
End-to-end onboarding for a freshly-plugged-in M5Stack ESP32 device (Cardputer, Cardputer-Adv, Core, CoreS3, Stick) — detect on USB, flash UIFlow 2.0 firmware, and install the Claude Buddy MicroPython app bundle
Install / Use
npx skills add anthropics/claude-plugins-official --skill m5-onboardInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
AutomationSupported Platforms
Our assessment of m5-onboard
m5-onboard scores 98/100 on our quality scale, 41st of 1,111 Automation skills we index (top 4%).
Its SKILL.md is 23 KB long, well organised into 14 sections with 3 code examples: a thorough specification that gives an agent plenty to work with.
With 36,726 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated today, so m5-onboard is actively maintained.
- It is released under the Apache-2.0 license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
Safety scan
ReviewOur scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review judged it risky: The skill instructs the agent to install Homebrew by piping a remote curl script into bash: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`, which is a remote-script-execution pattern flagge.
AI review: risky
- The skill instructs the agent to install Homebrew by piping a remote curl script into bash: `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`, which is a remote-script-execution pattern flagge
- It directs the agent to clone a repository and run hardware-flashing/orchestration scripts (`scripts/onboard.py`, `scripts/flash.py`, `scripts/install_apps.py`) that rewrite device firmware, install a `main.py` bundle, and modify NVS boot o
- While the overall purpose is legitimate device provisioning, the file mixes normal package-manager installs (pip, winget, apt) with the curl|bash Homebrew bootstrap and low-level firmware manipulation.
AI review by kimi-k2.7-code on 2026-09-25. Automated pattern scan on 2026-09-25. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.
m5-onboard compared with similar skills
All 4 of these similar skills score higher than m5-onboard; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| m5-onboard (this skill)by anthropics | 98 | 36.7k | today | SKILL.md |
| Agent-Reachby Panniantong | 100 | 85.4k | 9d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 73.8k | today | CLAUDE.md |
| rufloby ruvnet | 100 | 73.2k | today | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 83.5k | today | MCP Server |
Frequently asked questions
- How do I install m5-onboard?
- Run
npx skills add anthropics/claude-plugins-official --skill m5-onboard. The install tabs above show the steps for each supported agent. - Which AI agents does m5-onboard work with?
- It is written for Claude Code, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is m5-onboard safe to use?
- Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review judged it risky: The skill instructs the agent to install Homebrew by piping a remote curl script into bash:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)", which is a remote-script-execution pattern flagge. It is Apache-2.0-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand. - Is m5-onboard still maintained?
- The repository was last updated today, so m5-onboard is actively maintained.
Skill content
View source on GitHubname: m5-onboard description: End-to-end onboarding for a freshly-plugged-in M5Stack ESP32 device (Cardputer, Cardputer-Adv, Core, CoreS3, Stick) — detect on USB, flash UIFlow 2.0 firmware, and install the Claude Buddy MicroPython app bundle. Use whenever the user plugs in or wants to flash/provision/reset an M5Stack or ESP32 board, or says "m5-onboard go".
M5Stack Onboarding
This skill automates the full cold-start workflow for an M5Stack ESP32 device: detect on USB, identify model, flash UIFlow 2.0, and push a MicroPython app bundle onto /flash/ so the device boots into user software. The apps we ship (Claude Buddy, Snake, Hello) talk over BLE or USB. The workflow runs on macOS, Linux, and Windows; the skill was developed against an M5Stack Basic v2.6 (CH9102 bridge, ESP32-D0WDQ6-V3, 16 MB flash) and generalized to cover the rest of the Core family, with the Cardputer-Adv (ESP32-S3, native USB) as the current default target.
Where the scripts live
This skill ships as part of the cwc-makers plugin for reference, but the executable scripts and the buddy/ app bundle live in a local clone of https://github.com/moremas/build-with-claude (the /maker-setup command creates this clone). Run every scripts/*.py invocation below from inside that clone's onboard/ directory so --apps buddy resolves to the sibling buddy/device/ payload.
When to use
Use this when a user plugs in an M5Stack device and wants it provisioned. The decision tree:
- Fresh/unknown device → run
onboard.py --apps buddyend-to-end (detect → identify → flash → install apps). This is the default path. - Already-flashed device, user just wants apps installed/refreshed → run
install_apps.py --src buddy(or any--src <path>to a directory of.pyfiles). - Flashed device, something feels broken → run
smoke_test.py(I2C + LCD + speaker + button check). - User wants to know what's on the bus / what the device can do →
smoke_test.py.
If multiple devices are plugged in, ask which port to target — don't guess. If the user is provisioning a device they previously worked with (e.g. "same thing as last time" or "another Buddy"), default to --apps buddy unless they say otherwise.
Which variant to assume
The rig this skill lives on provisions Cardputer-Adv boards overwhelmingly, so onboard.py now defaults to --variant cardputer-adv. In practice that means:
- If the user says nothing about the model, go with the default. They're almost certainly holding a Cardputer-Adv.
- If the user says "Cardputer" (no "Adv"), ask — the two models share a form factor but take different firmware images, and flashing the wrong one boot-loops the device.
- If the user names any other board ("Core2", "CoreS3", "Basic", "Fire"), pass the matching
--variantexplicitly — the default won't apply. - The chip is ESP32-S3 either way, and
detect.pywon't be able to tell Cardputer from Cardputer-Adv before UIFlow is flashed (same native USB-JTAG VID, no pre-flash I2C probe). So this is a user-intent question, not a hardware-fingerprint one.
The workflow
The main orchestrator is scripts/onboard.py. It drives the sub-scripts in order and handles the handoffs between them (waiting for reboots, capturing MAC, reporting progress). Prefer calling it directly over stitching the sub-scripts yourself unless the user asks for a partial run.
The default provisioning command (fresh Cardputer-Adv, install the buddy bundle):
python3 scripts/onboard.py --apps buddy
How to invoke this from Claude Code's Bash tool. Do NOT call onboard.py as a foreground Bash command. The Bash tool captures output and does not stream it back to the assistant until the command exits — and this command runs 2–3 minutes. That silence looks identical to a hang, and the assistant will usually give up before the button-dance prompt ever reaches the user. Instead, always run with run_in_background: true, tee to a log file, and then use the Monitor tool (or periodic tail via Read) to surface stage banners, heartbeats, and prompts to the user in real time. 2>&1 is not the fix — all progress already writes to stderr, which a terminal shows fine. The fix is streaming semantics, not redirection. The pattern that works:
# Launch (background, tee log):
python3 scripts/onboard.py --apps buddy 2>&1 | tee /tmp/m5-onboard.log
# Monitor (surfaces key events without drowning in byte-progress spam):
tail -f /tmp/m5-onboard.log | grep -E --line-buffered \
"^====|heartbeat|Heads up|Enter download mode|download mode!|rebooted into UIFlow|Manual reset|DONE|ERROR|Error|Traceback|FAIL|failed|No USB|not detected|Attempt [0-9]|Device already in download|Download mode port|Post-flash port|Waiting for device"
Relaying physical steps to the user (REQUIRED)
The flash stage cannot proceed without a manual button press on native-USB boards — there is no software path. When the monitored log shows Enter download mode (or the script appears to wait at the FLASH stage), you MUST stop and tell the user to do the following on the back of the Cardputer, in your own words, before continuing:
- Press and hold the G0 button
- While still holding G0, briefly press and release the RST button
- Keep holding G0 for about one more second, then release it
- The screen should go fully dark — that means download mode is active
If the device reboots into UIFlow instead of going dark, tell the user G0 was released too early and to try again holding it longer. Do not move on, retry the script, or attempt a software workaround until the user confirms the screen is dark — the flash will not start otherwise. The same applies to any later Manual reset prompt: relay the physical step and wait for the user.
Users running onboard.py directly in their own terminal (not via Claude Code) will see all output live — no changes needed there.
If --port is omitted, detect.py picks the most likely candidate across all three OSes: native-USB ESP32-S3 (/dev/cu.usbmodem* on macOS, /dev/ttyACM* on Linux, COMx on Windows), or a CH9102/CP210x UART bridge on older boards. Bluetooth-serial ports are filtered out. If multiple candidates are present, it asks.
The known apps name buddy resolves to the buddy/device/ directory in this repo (custom launcher + Hello + Claude Buddy BLE client + Snake). Any other --apps value is treated as a filesystem path.
To skip re-flashing and just push (or refresh) the apps onto an already-provisioned device:
python3 scripts/install_apps.py --port <PORT> --src buddy
Where <PORT> is whatever detect.py printed on the last full run — for example /dev/cu.usbmodem1101, /dev/ttyACM0, or COM3.
Stages
- Detect (
detect.py) — enumerate serial ports, filter to USB-UART bridges (CH9102 vendor0x1A86, Silabs CP210x0x10C4, FTDI0x0403) or the ESP32-S3 native USB-JTAG interface (0x303A). Probe with esptool to confirm the chip. Port names differ per OS (/dev/cu.usbmodem*on macOS,/dev/ttyACM*/ttyUSB*on Linux,COMxon Windows) but pyserial abstracts that. - Identify (
detect.py) — alongside port discovery,detect.pyreads the factory-test partition signature and/or scans I2C once UIFlow is on, and cross-referencesreferences/hardware_signatures.mdto suggest the right firmware variant (Basic-16MB, Core2, CoreS3, Cardputer-Adv, etc.). User-facing variant choice happens viaonboard.py --variant; there is no separatedetect.py --identifyflag. - Fetch firmware (
fetch_firmware.py) — query the M5Burner manifest API and download the appropriate UIFlow 2.0 binary into the system temp dir. Cached between runs — safe to clear the cache anytime, it just re-downloads. - Flash (
flash.py) —esptool write_flash 0x0 <image>at 460800 baud for UART bridges,--no-stubat 115200 baud for native-USB S3 devices. 921600 fails intermittently on the CH9102 bridge — do not increase it. Native-USB flash can intermittently throwLost connection, retryingmid-erase; esptool recovers. The post-flashwatchdog-resetteardown step can fail even when the flash itself succeeded —flash.pyparses esptool's stdout, treats that specific failure pattern as non-fatal whenHash of data verifiedappeared, andonboard.pyfalls back toflash.native_reset()and then manual-RESET coaching if needed. - Install apps (optional,
install_apps.py) — paste-mode REPL upload of every.pyfrom a source directory into/flash/, then reboot viarepl_reset(DTR/RTS is a no-op on native USB — don't reach for it). Source layout: root*.py→/flash/,apps/*.py→/flash/apps/(UIFlow's stock launcher scans that). When the bundle ships a rootmain.py,install_apps.pyalso sets NVSboot_option=2so UIFlow's own launcher doesn't run and ourmain.pytakes over the boot flow — critical for BLE-using apps on ESP32-S3 (see gotchas below). - Smoke test (optional,
smoke_test.py) — I2C scan, LCD test pattern, speaker beep, button read.
Critical gotchas (baked into the scripts — do not second-guess)
These are things the scripts already handle correctly but which you should not override if the user asks you to "just run esptool manually" or similar:
- Native-USB ESP32-S3 boards (Cardputer, Cardputer-Adv, CoreS3) require a physical BtnG0+BtnRST dance to enter download mode. There is no software path. The chip has no DTR/RTS bridge, so nothing esptool or pyserial can do will put it into the ROM bootloader — the user has to hold GPIO0 low across a reset pulse with the hardware buttons. On Cardputer-Adv specifically both buttons (BtnG0 and BtnRST) are on the back of the device — small, flush-mounted, often easiest to press with a fingernail.
onboard.py:_wait_for_download_portprompts for this at runtime during FLASH: press and HOLD BtnG0, briefly press BtnRST, release BtnRST first, keep holding BtnG0 for ~1 more second, release BtnG0, screen should be fully dark. If the device reboots back into UIFlow instead, BtnG0 was released too early — the coaching retries and tells the user to hold it longer. Do NOT try to automate this withesptool --before default_resetor pyserial's DTR/RTS; both are no-ops on native USB (the pins aren't wired to EN), and adding them just hides the real prompt. - Do not unplug the device during FLASH. Especially on native USB. A mid-flash disconnect leaves the internal flash in an inconsistent state. Mask ROM is usually reachable afterwards (press BtnG0 alone on the back, or do the full BtnG0+BtnRST dance), so the recovery is just to re-run
m5-onboard go— it's idempotent and will re-enter download mode, re-flash, re-push apps. Don't panic and don't start opening the case; the mask ROM is in silicon and survives a corrupted flash as long as the USB PHY is intact. - Baud rate is 460800 on UART bridges, 115200 with
--no-stubon native USB. Not 921600 on either. The CH9102 bridge loses sync onerase_flashat 921600 (not theoretical — it fails). Native USB's stub-baud-bump path produces "Lost connection" mid-flash; 115200 no-stub is counterintuitively faster end-to-end because it never fails. - NVS writes must use
set_str, notset_blob(relevant toinstall_apps.py'sboot_optionsetter). UIFlow's startup callsnvs.get_str()and ESP-IDF tags blob and string entries separately. A blob-tagged key returnsESP_ERR_NVS_NOT_FOUNDtoget_str, and the device boot-loops. If a prior attempt wrote a blob, callnvs.erase_key(name)beforeset_str. - REPL multi-line blocks need paste mode. Sending
try:/except:line-by-line makes the REPL accumulate indentation forever. Use Ctrl-E to enter paste mode, send the block, Ctrl-D to execute.mpy_repl.pywraps this. - Hard reset is DTR=False, RTS=True, 100ms, RTS=False — but only on UART-bridge devices. On native-USB ESP32-S3 boards the DTR/RTS lines aren't wired to EN/GPIO0, so that pulse
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
85.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
73.8kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ruflo
73.2k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
Scrapling
83.5k🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
