Building a board
Describe a board's pins and the MicroPython firmware it runs.
A board package describes hardware: which pins the board has and what each one can do, how the studio talks to it, and which MicroPython firmware it runs. It has no code — the runtime is the platform's, and every action comes from a component.
my-board/
├── package.json
└── workspace/
├── manifest.yaml what the board is, its firmware
└── board.schema.yaml its pins and connectionOnly YAML at the top
Henglos IO reads package.json and the .yaml files at the package root or directly in
workspace/ — keep the manifest and the schema there.
The manifest
kind: board
name: ESP32
schemaVersion: 1
dependences:
platform: ^1.0.0
ui:
schema: ./board.schema.yaml
capabilities: [wifi, bluetooth]
firmware:
micropython: 1.29.0
image: https://micropython.org/resources/firmware/ESP32_GENERIC-20260824-v1.29.0.bin
flash:
chip: esp32
offset: "0x1000"| Field | What it is |
|---|---|
kind | Always board. |
name | The board's name in the studio. |
dependences.platform | The platform versions it works with (a semver range). The studio only offers this board for workflows on a matching platform. |
ui.schema | The board schema file, relative to the manifest. |
capabilities | What the board offers besides its pins: wifi, bluetooth, ethernet, can, usb-host. Components that need one only run on boards that list it. Absent: nothing is checked. |
firmware.micropython | The MicroPython version the image runs. The build compiles the runtime and the components for exactly this version — a .mpy only imports on the version it was compiled for. |
firmware.image | URL of the MicroPython firmware image — usually the port's official build from micropython.org. |
firmware.flash.chip | The chip, as esptool --chip names it (esp32, esp32s3, esp32c3…). |
firmware.flash.offset | Where the image is written: 0x1000 on the classic ESP32, 0x0 on the S3/C3. |
Quote the offset
Write it as a string — "0x1000". Unquoted, YAML reads 0x1000 as the number 4096.
Change micropython and image together: the image must run the version the build compiles for.
The board schema
name: ESP32
description: ESP32 DevKit (ESP-WROOM-32)
pins:
- gpio: 2
type: [digital_in, digital_out, analog_in, pwm, touch]
displayName: GPIO2
- gpio: 4
type: [digital_in, digital_out, analog_in, pwm, touch, uart]
displayName: GPIO4
- gpio: 34
type: [digital_in, analog_in]
displayName: GPIO34
connection:
serial:
baudrates: [115200, 9600, 230400, 460800, 921600]Pins
Every pin the board exposes, at least one:
| Field | What it is |
|---|---|
gpio | The GPIO number — what machine.Pin(gpio) takes. |
type | What the pin can do: digital_in, digital_out, analog_in, analog_out, pwm, touch, i2c, spi, uart. |
displayName | How the studio shows it — the label printed on the board is a good choice. |
type decides which pins a field offers. A component's pin configuration lists the capabilities it
needs (filterBy in its manifest); the studio shows only the pins that have all of them. GPIO
Read asks for digital_in, so a pin without it never shows up there.
List only what's safe
Leave out pins that are wired to flash, used by the USB-serial chip or that break boot — a pin the schema doesn't list can't be picked.
Connection
How the studio reaches the board — at least one of:
serial.baudrates— the baud rates the studio offers for this board, from the standard list (9600 … 921600 and up). The first one is the default. Keep115200first: it's what MicroPython's REPL runs at, and the driver talks to the board through it.wifi—defaultSsid,defaultPassword,defaultHostanddefaultPort, for boards reached over the network.
Board configurations
configurations (optional) are settings that belong to the board rather than to an action. They use
the same types as a component's configurations — see
Building a component.
Flashing the firmware
When a board doesn't run MicroPython yet, or runs another version, the studio's Flash writes
firmware.image to it. The platform's build container runs esptool against the board's port
(tunneled from the driver): it erases the flash, then writes the image at firmware.flash.offset
for firmware.flash.chip.
ESP chips today
Flashing goes through esptool, so it works for the Espressif family (ESP32, S2, S3, C3…). A
board on another chip can still be used if MicroPython is already on it — only Flash is
unavailable.
Publish it
{
"name": "@you/board-esp32",
"version": "1.0.0",
"description": "ESP32 DevKit board for Henglos IO",
"type": "module",
"files": ["workspace/manifest.yaml", "workspace/board.schema.yaml"],
"exports": {
"./manifest.yaml": "./workspace/manifest.yaml",
"./package.json": "./package.json"
}
}npm publish # the public registry
npm publish --registry http://localhost:4873 # a local VerdaccioInstall it from the studio's store by its package name. It then shows up in the board list of New workflow — and in a workflow's Settings in the studio (or Edit on the Hub), to move an existing workflow onto it. That's how Henglos IO supports many boards: each one is a package like this, and every workflow picks its own.
Versions are immutable
Henglos IO stores each package version once. To change pins or firmware, publish a new version — workflows keep the version they were made with until you pick the new one.