Firmware development

Point MeshBench at a MeshCore checkout. It builds it, loads it, and assigns it to every node of that role. Nothing is added to your checkout and nothing in it is modified.

meshbench dev -from ~/src/MeshCore
building simple_repeater from /home/alex/src/MeshCore
simple_repeater local-fix-relay-suppression (native)   0.6 MB
  in the workbench's firmware library
  assigned to every simple_repeater node

That is the whole loop. Edit, run it again, and the network is running your change. It needs MeshBench installed and a C++ compiler; nothing else.

The build lands in the firmware library as a *native* build: MeshCore compiled for this machine, which is the deterministic arm and the one every measurement uses. Native and emulated is the two-minute version of that choice.

What the command does#

your checkout any branch, unmodified host build in a temp directory firmware library named for the git branch every node of that role
The build happens in a temporary directory, so the checkout is only ever read. The build is named after its git branch, so two branches appear separately in the library rather than overwriting each other.

The first run also fetches the Crypto library MeshCore builds against, once, and caches it. There is nothing to install beforehand beyond a C++ compiler.

Watching for changes#

meshbench dev -from ~/src/MeshCore -watch

Rebuilds and reassigns whenever a source file under src/ or examples/ changes. Leave it running in a terminal beside your editor.

Choosing a role#

MeshCore is several applications from one tree. -role selects which:

rolenode kind it runs on
simple_repeatersimple and advanced repeaters
companion_radiocompanions, the node a phone attaches to
simple_room_serverroom servers

A build is assigned to the role it was built for. Assigning a repeater build to a companion would make it a different kind of node, not a different version of the same one.

Comparing your change against a baseline#

The reason to run a change on three hundred nodes is to find out whether it is an improvement. That is a sweep: two arms, the same network, the same seeds, only the firmware differing. Load a shipped -strict fixture as the arena, and Time and determinism is why the same seeds make a difference attributable to the firmware.

meshbench dev -from ~/src/MeshCore -name my-change -assign=false

then, in the workbench, define a sweep whose two arms name my-change and the release you branched from. See Experiments.

Three things decide whether such a comparison means anything:

Node storage is isolated per arm. A node keeps its preferences across runs, as hardware does, so a node that has run before loads its stored value rather than your changed default. A sweep runs each arm in its own storage root, so no arm inherits another's state; only runs made by hand outside a sweep need the firmware library's wipe button.

Give every role a build. A network with companions or a room server needs a build for each of them. Roles you did not change take the published release.

Include a control. Two arms built from identical source must produce identical numbers. When they do, a difference between real arms is attributable to the firmware. Every run records the checksum of each binary it attached, so "which build produced this number" is answerable from the results.

Versions are per role#

MeshCore tags one role at a time. repeater-v1.17.0 and companion-v1.17.0 are different releases, and a bare v1.17.0 matches neither.

If you build with PlatformIO#

An optional post-build script hands each build over automatically, so a normal PlatformIO build appears in the library without a separate command:

extra_scripts = post:meshbench.py

The environment name supplies the board and the role, for example Heltec_v3_companion_radio_usb. This is a convenience: meshbench dev needs nothing added to your tree.

MeshBench documentation. Built from the running application, not from mock-ups. Screenshots are window-only captures; see CLAUDE.md for the rule that keeps them current. Edit this page.