# New Device Skill

> Build or update an M5Stack hardware skill in this marketplace — a Controller (a board that runs firmware, like Core2 or AtomS3), a Unit (a peripheral you drive from a Controller, like a ToF sensor or a relay), or a Chip (an Espressif SoC capability layer, like esp32-c6). Use whenever adding a new board/unit/chip to this repo, correcting an existing skill, deciding whether something warrants its own skill versus a section in an existing one, or figuring out where a piece of content belongs (board wiring vs. chip capability). Covers the research workflow, naming and file-layout conventions, the Controller-to-Chip pointer convention, how to flag unverified sourcing, and the release steps (catalog row, version bump, validation) that make the work actually reach users.

- Skill: `iot-forge/new-device-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add iot-forge/new-device-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/iot-forge/new-device-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: iot-forge (https://skillmd.com/u/iot-forge)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/iot-forge/new-device-skill

---


# Building a device skill for this marketplace

This repo covers M5Stack hardware, one skill per product family, working
toward the full catalog. The conventions below are load-bearing — several
encode failures this project already hit. Read the relevant section before
writing, not after.

## First: which of the three kinds is this?

They're scoped differently on purpose.

**Controller** — a board that runs application firmware (Core2, AtomS3,
Cardputer Adv, Tab5). Its skill needs pin/peripheral specs *and* how to set
up a dev environment, initialize hardware, and debug bring-up across every
framework M5Stack supports for it (usually some mix of Arduino, PlatformIO,
ESP-IDF, UIFlow2/MicroPython). These are the heaviest skills.

**Unit** — a peripheral you talk to *from* a Controller (a Buzzer, a ToF
sensor, a 4Relay board). There's no "which development platform" question —
just wiring, protocol, register/command reference, and example host code.
Much lighter: one `SKILL.md` is usually enough.

**Chip** — the SoC underneath one or more Controllers (ESP32-S3, ESP32-C6).
Not a product anyone buys alone, but substantial content: what the silicon
itself can do, independent of any board's wiring. M5Stack reuses the same
chip across many boards — ESP32-S3 alone covers Cardputer, Cardputer Adv,
AtomS3, AtomS3R, CoreS3, StickS3 — so writing this once and having every
board skill point into it beats maintaining five drifting copies.

## Naming

- Controller/Unit skill name and folder: `m5stack-<slug>`, lowercase
  kebab-case product family name — `m5stack-cardputer-adv`, `m5stack-core2`,
  `m5stack-unit-tof`, `m5stack-unit-4relay`.
- Chip skill name and folder: bare chip slug, **no** `m5stack-` prefix —
  `esp32-s3`, `esp32-c6`. They document Espressif silicon, not an M5Stack
  product, even though this repo maintains them for the M5Stack catalog.
- For a Unit whose generic name would collide (many vendors ship a "Relay"
  unit), keep M5Stack's product name as the slug — `unit-4relay`, not
  `relay-module`. That's what a user will actually type or paste.

**One skill per product family, not per hardware revision.** M5Stack ships
many minor revisions (`Core2`, `Core2 v1.1`, `Core2 v1.3`, `Core2 For
AWS`...). Fold them into one skill with a "Hardware revisions" table.
`m5stack-core2` is the worked example: four revisions across two product
lines in one skill, even though the AWS line adds real extra hardware
(ATECC608 crypto chip, RGB LED ring).

Split into separate skills only when a "revision" is genuinely a different
product with different peripherals — Cardputer vs Cardputer Adv is the
canonical case: same shell, genuinely different keyboard, audio, and IMU.

## File layout

### Controllers

```
plugins/<plugin>/skills/<slug>/
├── SKILL.md              overview, quick specs, platform picker, pointers
└── references/
    ├── pinout.md         required — full pin/peripheral/I2C-address map
    ├── arduino.md        Arduino + PlatformIO, if supported
    └── espidf.md         ESP-IDF, plus a short UIFlow section
```

Add another reference file only if a framework needs real depth of its own.
`plugins/cardputer/skills/cardputer-adv/` is the reference example.

### Units

```
plugins/<plugin>/skills/<slug>/
└── SKILL.md              what it does, wiring/protocol, example code
```

Add `references/` only if `SKILL.md` would otherwise pass ~300-400 lines.

### Chips

```
plugins/esp32-chips/skills/<chip-slug>/
├── SKILL.md              cross-chip comparison table, quick specs, capability map
└── references/           split by capability domain
```

Domains that have earned their own file across the four built chip skills:
`peripherals.md`, `power-sleep-ulp.md` / `power-sleep-lp.md`, `usb.md`,
`memory-radio.md` / `memory-radio-ai.md` / `memory-concurrency-ai.md`,
`multimedia-vision.md`.

**Not every domain needs its own file.** Skip one when the chip's story
there is a paragraph: `esp32` has no `usb.md` (no USB hardware at all),
`esp32-c6` has none either (fixed-function Serial/JTAG only) and no
task-pinning section (single core). `esp32-p4` went the other way and split
out `multimedia-vision.md` because MIPI-CSI/DSI + ISP + JPEG + H.264 +
PPA/2D-DMA is deep enough to warrant it. Follow the "split by capability
domain" principle, not the literal file list.

**No `pinout.md` in a Chip skill** — pin wiring is a Controller concern.

## The Controller-to-Chip boundary

This is the convention that keeps the catalog from drifting, and the one
most easily gotten wrong.

A Controller's `references/espidf.md` covers **board wiring**: which chip
sits at which I2C address, which GPIO drives what, which library owns the
bus. Generic **chip capability** — RMT, LEDC, I2S, ADC quirks, deep sleep,
ULP/LP coprocessors, PSRAM config, USB controllers, radio coexistence,
AI/DSP extensions — belongs in the Chip skill, and the board file links to
it by name.

See `plugins/cardputer/skills/cardputer-adv/references/espidf.md` for the
pattern, including the inline hint telling readers to
`claude plugin install esp32-chips@m5stack` if that plugin isn't present —
board plugins and the chip plugin install separately, so a board skill can't
assume its chip skill is there.

**When a new Chip skill lands, sweep every Controller skill that uses that
chip.** This project already ate this failure: `core2` sat for a long time
telling readers "no dedicated classic-ESP32 chip skill exists yet" and
sending them to raw Espressif docs, long after `esp32` was built and marked
done in the catalog. Updating the chip's own catalog row is not enough — the
pointers live in the Controller skills.

Only build a Chip skill once a Controller actually needs depth beyond what
fits reasonably in its own `espidf.md`. Don't pre-build all nine ESP32
variants speculatively.

If a Controller pairs a main SoC with a separate radio co-processor (Tab5's
ESP32-P4 + ESP32-C6), each SoC gets its own Chip skill. Don't conflate them.

## Workflow

1. **Pick the target.** Next row from `docs/catalog/controllers.md` or
   `docs/catalog/units.md` — or whatever a user asked for, which jumps the
   queue. Start a `docs/catalog/chips.md` row only when a Controller you're
   building actually needs that depth.

2. **Research.** Fetch the official `docs.m5stack.com` page for the product,
   its schematic, and any library or firmware repo linked from that page.
   For a Chip skill: the chip's datasheet and Espressif's ESP-IDF docs for
   that target. Cross-reference GitHub and community sources for whatever
   the official page doesn't cover — register maps, I2C addresses — and
   **flag those inline as community-sourced**, in the skill body, not just
   in notes. A reader needs to know which claims rest on one forum post.

   Canonical M5Stack sources worth knowing (always fetch fresh — don't
   snapshot; M5Stack updates them as new products ship):

   - `https://docs.m5stack.com/en/product_i2c_addr` — the full I2C address
     table across every M5Stack product (Controllers, Units, Hats, Modules,
     Bases, Faces). Verify a Unit's address here before writing it into a
     skill; this repo treats I2C addresses as load-bearing.
   - `https://docs.m5stack.com/compatible/product_<family>_define.json` —
     machine-readable Grove port + GPIO map per SKU. Known families:
     `core`, `atom`, `stick`, `cardputer`, `stamps3`. Source of truth for
     `PORT.A`/`PORT.B` pin assignments per revision.
   - `https://docs.m5stack.com/compatible/product_<accessory>_compatible.json` —
     which accessories work with which controllers. Known kinds: `unit`,
     `hat`, `module`, `base`, `atomic`, `cap`.
   - `https://docs.m5stack.com/comparison/compare_directory.json` — index
     of the human-readable compare pages (Core/Stick/Atom/Cardputer/
     Station/Paper/Stamp + UnitCAM/TimerCAM/UnitV).
   - The `products_selector/*_compare` pages themselves (e.g.
     `https://docs.m5stack.com/en/products_selector/m5core_compare`) are
     the nicest human overview per family, but they're client-rendered
     Nuxt SPAs — `curl` and `WebFetch` return an empty shell. Open in a
     browser, or use the `_define.json` above for pin data.

3. **Write it** following the layout above.

4. **Record build metadata** in `docs/notes/<slug>.md`:

   ```markdown
   # <slug> — build notes

   Last verified: YYYY-MM-DD
   Sources: <docs URLs, GitHub repos, schematics used>

   ## Confidence / soft spots
   <anything from a third party rather than official docs; anything not
   independently confirmed; anything likely to drift with library versions>

   ## Open questions
   <what's worth checking next time this skill is touched>
   ```

   This never ships to users — it exists so the next session doesn't
   re-derive sourcing and confidence from scratch. The existing notes files
   are unusually candid about what wasn't verified; keep that.

5. **Update the catalog row**: status → `done`, fill in the skill path.
   Cross-link the Controller's `espidf.md` to its Chip skill. While you're
   in a catalog file, skim sibling rows for staleness — `esp32` was fully
   built and sitting in the repo while its catalog row still said
   `not started`.

6. **Bump the plugin version** in `plugins/<name>/.claude-plugin/plugin.json`.
   Without this, users never receive the skill. If the skill goes in a new
   plugin, add it to `.claude-plugin/marketplace.json` too.

7. **Validate and test.**

   ```bash
   python3 scripts/validate.py
   claude plugin marketplace add ./          # from a scratch dir, install, and ask
   ```

   The real test is behavioral: ask a question only the new skill can
   answer, and check the answer is right. Structural validation passing
   means the skill *loads*, not that it *helps*.

## Status values

Used in the `Status` column of every catalog file:

- `not started` — not yet researched or built
- `in progress` — partially drafted, not shipped
- `done` — built, shipped, source in `plugins/`
- `needs update` — was `done`, but the board was revised, a library API
  moved, or someone reported an inaccuracy. Note *why* in the row.

## Which plugin does a new skill go in?

Group by Controller family, chips together in `esp32-chips`. Every *enabled*
skill's name and description sits in the context window of every session, so
a machine that only touches a Cardputer shouldn't carry descriptions for 48
controllers and 120 units.

As families get built, add plugins — `stick`, `atom`, `stamp`, `epaper`,
`units` — rather than growing one mega-plugin. Plugin names are stable
identifiers users have in `enabledPlugins`; adding is free, renaming needs a
`renames` map in `marketplace.json` (see `CONTRIBUTING.md`).

