Set up a node
For a new Aspen, prepare a private image in your browser and use MeshCore flasher. Then connect the MeshCore app and send your first command.
Before you transmit
- Radio profile: choose a frequency, bandwidth, spreading factor, coding rate and transmit power permitted where you operate. Every role on the node uses this profile, and your companion radio must match it.
- Credentials: set your own administrator, management and room passwords. Keep owner keys and seeds on your own computer, outside the repository and firmware images.
- Backup: on an existing node, back up role identities, settings, programs and stored data before changing firmware or moving host services.
- Supported hardware: Seeed Wio SX1262 with a XIAO ESP32-S3R8 (8 MiB flash, PSRAM) or XIAO nRF52840, and an antenna fitted before transmitting. Heltec V3/V4 and other board ports need their own wiring, flash-layout, memory and role checks; the XIAO packages must not be flashed to them.
The guides use 912.525 MHz / BW 250 kHz / SF7 / CR5 / TX 2 dBm with three-byte paths as an example. Use it only where it is permitted.
Source-build options
Choose a build profile for development or a custom installation
Aspen runs all selected roles on-chip on the ESP32. Birch runs the roles off-chip through a WiFi shared modem; Willow implements those host services in Hew. Pine is the hardware-limited nRF52840 on-device variation. Each product has its own version and GitHub release.
The setups share a firmware foundation, not an identical set of features on every board. The configurations below target XIAO hardware. Cedar is used for lab tests only.
| Setup | Build or service | Choose it for |
|---|---|---|
| Generic Aspen, current source | public_aspen | One configurable image: native roles, Lua/Wasm, saved packet programs, HTTPS and room frontends. One KISS socket with MKISS, two companion clients and two dashboard viewers. |
| ESP32 WiFi modem | Xiao_S3_WIO_kiss_wifi | KISS connections and a radio dashboard, with roles running on a host. |
| ESP32 native roles | Xiao_S3_WIO_onchip | Repeater, room, companion and optional observer without the bot. |
| ESP32 bot and management | Xiao_S3_WIO_onchip_beta | Lua/Wasm bot, RF and browser administration, durable program installation and rollback. |
| ESP32 network services | Xiao_S3_WIO_onchip_https | The managed bot plus approved HTTPS services, package fetch and optional metrics. Provides three external KISS sockets instead of four. |
| ESP32 bot, RAM only | Xiao_S3_WIO_onchip_bot | Lua experiments. Custom programs run from RAM; the bundled program returns after restart. |
| Go host | Go role services + native_lua worker | Independent roles over KISS. The Linux worker runs Lua and optional Wasm with OpenSSL networking; the setting keeps the name native_lua for both. |
| Willow Hew host | experiments/hew-roles | Native Hew host services over the WiFi modem, with separate Base, dashboard and broker executables. Follow the pinned revision's service limits and migration guide. |
| nRF52840 Lua + repeater | nrfmast_fleet_lua | Production Lua bot beside a native repeater, with durable programs and optional BLE. |
| nRF52840 note bot | nrfmast, nrfmast_rx | Smaller native-command images. nrfmast transmits; the default nrfmast_rx only receives. |
| ESP32 role, remote modem | Phyless_Xiao_S3_repeater, _room, _client | One role on an indoor ESP32, using a separate WiFi modem as its radio. |
The current-source public_aspen image includes Lua/Wasm, two saved packet programs and runtime-configured room frontends. Select services through saved configuration. Custom builds can omit Wasm with ONCHIP_BOT_WASM=0. See image selection and the Wasm build options.
Standalone ESP32 (Aspen)
The ESP32 runs the selected repeater, room, companion and observer roles, the command bot and the shared modem. RF roles keep running if WiFi drops.
New board: browser setup and MeshCore flasher
-
Choose your settings
Download the Aspen release ZIP and select it in the browser setup. Enter your local radio profile, WiFi and the services you want. The page creates credentials locally; they are not uploaded.
-
Save the private files and flash
Keep the private setup ZIP, including your operator key and passwords. Download the private
-merged.bin, then choose it with Custom Firmware at flasher.meshcore.io. The wizard has the port-selection and reset steps. -
Check the node
After reset, USB diagnostics report
Public setup loadedand the selected roles become ready. With WiFi enabled, log in athttp://aspen.local/admin(or the printed IP address) and confirm the frequency and power before sending messages. Then connect and test.
Preview the browser settings
The combined first-install image is for a new board only. It erases any existing identities, settings and data. Use application-only updates on an existing Aspen.
If the setup is missing or invalid, the node reports RF and administration disabled; restore private setup over USB and does not create identities or transmit.
Prefer an offline USB setup, or need an existing operator key?
The Python setup tool takes your private profile, builds SPIFFS and checks existing storage before writing it. Use it for an existing operator authority, separate administrator passwords or environments where browser setup is unavailable.
Build a custom Aspen image from source
For the current generic image, follow the public_aspen build steps. The commands below create a separate private custom profile.
With Git, Python 3 and PlatformIO installed, create the private build configuration from the repository root:
make aspen-config
$EDITOR firmware/platformio.onchip.ini
Set the radio profile, hostname, WiFi and passwords, then build the managed bot image:
make -C firmware/esp32 bot-firmware \
CONFIG="$PWD/firmware/platformio.onchip.ini" \
BUILD="$PWD/.tmp/onchip-MeshCore" ENV=Xiao_S3_WIO_onchip_beta \
ONCHIP_BOT_WASM=1
Use ENV=Xiao_S3_WIO_onchip_https for network services. Build commands do not flash or restart hardware. The first flash and update steps cover writing a blank board and later firmware-only uploads.
Expected: http://NODE_IP/ lists services, keys and readiness; the companion listens on NODE_IP:5000 and KISS on NODE_IP:8001. Managed builds add http://NODE_IP/admin. Role enable and disable choices apply at the next boot.
Already have a MeshCore node?
Keep it running while you try Aspen on a new supported board. They communicate over the same native MeshCore protocols when their radio profiles match.
Replacing a different firmware is a migration, not an Aspen update. Its flash layout and saved state may differ. Preserve its identity and useful state using that firmware's backup/export tools before deciding to replace it. The browser's combined Aspen image erases the board and creates new radio identities; it does not import the previous node's keys.
For an initialized private Aspen moving to the generic application, use the retained-state migration procedure, not new-board setup.
Birch: Go host with a WiFi modem
A Linux computer runs the roles; an ESP32 + SX1262 WiFi modem at the antenna provides the radio. For an existing modem, download Birch 0.1.1, extract it and check SHA256SUMS. Use Debian 12 x86_64 with Python 3.11, libcjson1 and libssl3; no Go compiler is needed.
python3 birch-v0.1.1-linux-x86_64-SOURCE12/install-birch.py \
--prefix "$HOME/.local/opt/birch-0.1.1"
Replace SOURCE12 with the extracted directory suffix. Edit its private config/meshcore-host.json: choose the modem address, enabled roles and room password environment. Run bin/meshcore-host -config CONFIG -check before starting it. The installed configuration follows the modem's profile without retuning it.
Build a WiFi modem and host from source
If you need a modem, use Git, Python 3, PlatformIO and Go, then run from the repository root:
make firmware-config
$EDITOR firmware/platformio.local.ini
make firmware
make firmware-upload UPLOAD_PORT='/dev/serial/by-id/<radio-device>'
make host-config
$EDITOR meshcore-host.json
make host-run
- In the firmware configuration, set WiFi and the modem's hostname.
- In
meshcore-host.json, setradio_address, enable the roles you need and choose the shared radio profile. Supply the room password throughMESHCORE_ROOM_PASSWORD, or setroom_public: truefor an open room. - If the modem is a standalone node that manages its own radio settings, set
"phy_authority": "modem"so the host does not retune it. - Name the upload device explicitly when several radios are attached.
Expected: the modem serves KISS at RADIO_IP:8001 and a read-only dashboard at http://RADIO_IP/. The host reports at http://127.0.0.1:9080/status, and /readyz returns HTTP 200 when the enabled roles and connections are ready. Without mDNS, use the modem's DHCP address.
Go host guide: capacity, services, native worker, backup and rollback
Existing UART shared modems
A UART shared modem serves wired queued-protocol clients through a 3.3 V USB-UART adapter: D7/GPIO44 (RX), D6/GPIO43 (TX), 115200 8N1.
The Birch host uses a WiFi/TCP modem, not this UART connection or a serial-to-TCP bridge.
Willow: Hew host with a WiFi modem
Use the WiFi modem from the Birch setup above. Willow runs relay, room, bot and observer services on a Linux x86-64 host. Its native Base companion, HTTP dashboard and local MQTT broker run separately. Choose each service from its own guide and check its supported commands and limits.
Willow is experimental. Extract the Willow 0.0.2 source ZIP, check its SHA256SUMS, and use Hew 0.6.0-rc7 with the dependencies in the Willow README. Build at the final installation path, keep the source and native-worker inputs there, and check your selected services before handing over existing identities:
cd experiments/hew-roles
make compiler-version
make native-worker build/hew-host-release
python3 -B willow.py check --state /path/to/private/willow
python3 -B willow.py run --release --state /path/to/private/willow
Before these commands, follow the offline migration guide to create the private state directory from a stopped Birch or Willow installation. check verifies state and worker inputs without opening the modem. run starts the selected roles; inspect their connection and readiness with python3 -B willow.py health --state /path/to/private/willow.
Stop the previous owner before handover. Never run Birch and Willow with the same role identity at once. Keep the modem configuration owner separate, use HEW_WORKERS=4 for the host services, and migrate into a new directory rather than overwriting the original state. Do not deploy from a changing source tree.
Willow requirements and capabilities · Build, health and service setup · Offline migration and rollback
Standalone nRF52840 (Pine)
Pine runs a native repeater (Pine-Relay) and a separately addressed Lua bot (Pine-Bot) on a XIAO nRF52840. It has no WiFi, HTTPS or Wasm. You administer it from a MeshCore app connected to another companion radio.
The Pine download updates a node that is already initialized; it cannot set up a blank board. Build the image from source with:
make -C firmware/nrf52840 build ENV=nrfmast_fleet_lua \
LUA_ARCHIVE=/path/to/lua-5.5.1.tar.gz
Install the application firmware.uf2 or DFU firmware.zip, not a full-device image. Updates keep identities, credentials and stored Lua programs and data.
Expected: after you advertise your companion, Pine-Relay's administrator console answers bot status, bot limits and source status, and Pine-Bot answers a private !ping with Pong.
Keep USB recovery within reach. An interrupted BLE update can leave Pine needing USB recovery. Use UF2 over USB, or attend BLE updates with the node physically accessible.
Pine production Lua: provisioning, limits and recovery · Operating Pine
ESP32 role with a remote modem
An indoor ESP32 runs one repeater, room or companion and keeps its identity and settings. A separate ESP32 + SX1262 WiFi modem at the radio site owns tuning, channel access and the transmit queue. Configure the modem first, then give the role matching radio settings and its WiFi connection.
make remote-radio-config
$EDITOR firmware/platformio.phyless.ini
make remote-radio-firmware PHYLESS_ENV=Phyless_Xiao_S3_repeater
make remote-radio-upload PHYLESS_ENV=Phyless_Xiao_S3_repeater \
UPLOAD_PORT='/dev/serial/by-id/<indoor-esp32>'
Choose Phyless_Xiao_S3_room for a room or Phyless_Xiao_S3_client for a companion. To run several roles indoors, use a Go host instead.
Connect and test
-
Connect the MeshCore app
Install the MeshCore app by Liam Cottle on your phone or computer. In the app choose Connect → WiFi, enter Aspen's IP address and port 5000, and enable Auto Reconnect. Choose Skip Setup on a configured node so the app does not replace its shared radio settings. Port 8001 is KISS, not the app endpoint.
-
Advertise, then message the bot
Send your companion's advert, then open MeshCore command bot (or the bot name shown on the dashboard). Send a private
!pingand expectPong. If the contact is missing, sendrole advert bot zerohopin Aspen's authenticated/adminconsole.The Aspen 0.1.11 download also answers local commands from its own Base. Use a separate companion radio on the same complete radio profile to communicate with the bot across RF.
-
Explore the commands
!helplists the commands you may use;!help NAMEshows usage and an example. See the built-in commands.
Connecting a host, or checking local versus RF messages
A host companion normally listens at 127.0.0.1:5000. For LAN access, set companion_listen to its LAN address and companion_allow_remote: true.
A local reflection is the shared modem's copy of a completed transmission. Use another radio for RF reception; its signal readings describe that radio path.
Keep local services private. Companion TCP, KISS and the ESP32 dashboard and /admin page use unencrypted connections. Keep them on a trusted LAN or a protected tunnel.
Update without losing state
An application-only update keeps role identities, saved settings and stored programs and data. Contacts and routes that the bot learned in memory need a fresh advert after the restart.
Aspen: keep its setup and update one application slot
Download the release and retain an encrypted node backup. For a managed Aspen, use the signed WiFi application update with the operator.seed from your private setup ZIP. Check the selected running hash and healthy roles afterward.
For USB recovery, use the slot-aware USB command. It reads the current OTA selection before choosing where to write the application.
Do not use the combined first-install image for an update. The MeshCore flasher's ordinary custom BIN path writes 0x10000; Aspen may be running its other OTA slot. Use the signed updater or slot-aware USB workflow, and leave bootloader, partitions, OTA selection, NVS and SPIFFS in place.
| Node | Update with | Avoid |
|---|---|---|
| Standalone ESP32 | Application BIN over USB, or a signed WiFi update on a managed node | Filesystem provisioning, erase or full-flash commands; they remove stored programs and data |
| Go host | Rebuild and restart the host service | Deleting .state/host; it holds role identities, contacts and stored data |
| Willow host | Build a pinned installation, stop services and rebind a frozen state copy | Changing an active build tree, deleting private state or running duplicate identities |
| Pine | Application UF2 over USB, or an attended BLE DFU update | Unattended BLE updates on a node you cannot reach |
ESP32 updates use BIN files, not UF2, unless the installed bootloader supports UF2. After a timeout, read the node's status before repeating an install or restore. Never erase identity storage to clear a radio-profile mismatch.
ESP32 persistence and recovery · Host state and credentials · Pine BLE update recovery