Henglos IO
Creating packages

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 connection

Only 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

workspace/manifest.yaml
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"
FieldWhat it is
kindAlways board.
nameThe board's name in the studio.
dependences.platformThe platform versions it works with (a semver range). The studio only offers this board for workflows on a matching platform.
ui.schemaThe board schema file, relative to the manifest.
capabilitiesWhat 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.micropythonThe 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.imageURL of the MicroPython firmware image — usually the port's official build from micropython.org.
firmware.flash.chipThe chip, as esptool --chip names it (esp32, esp32s3, esp32c3…).
firmware.flash.offsetWhere 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

workspace/board.schema.yaml
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:

FieldWhat it is
gpioThe GPIO number — what machine.Pin(gpio) takes.
typeWhat the pin can do: digital_in, digital_out, analog_in, analog_out, pwm, touch, i2c, spi, uart.
displayNameHow 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. Keep 115200 first: it's what MicroPython's REPL runs at, and the driver talks to the board through it.
  • wifi — defaultSsid, defaultPassword, defaultHost and defaultPort, 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

package.json
{
  "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 Verdaccio

Install 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.

On this page