Move the AUTO_ADD_OVERWRITE_OLDEST assignment to after _store->loadPrefs() so existing bots with autoadd_config=0 saved on flash get the bit set on every boot. Previously the assignment was in the constructor (before load), so loadPrefs() would overwrite our default with the persisted zero, meaning the fix only took effect on factory-fresh prefs.
Colorado Mesh Bot Firmware
Companion-radio firmware for the Colorado Mesh community, built on top of
MeshCore. It adds an in-firmware
"firmware bot" that responds to chat commands like ping, trace, path,
status, neighbors, and magic8, with multi-bot response coordination so
nearby Colorado bots don't all reply at once.
The bot runs entirely on-device — no internet, no companion-app bridge, no cloud. Useful as a low-friction probe / utility node on the Colorado mesh.
What's different from upstream MeshCore
The release-built firmware applies a patch queue (patches/meshcore/) on top
of a pinned MeshCore commit. The patches add:
- A bot command registry (
help,cmd,ping,test,hello,about,roll,dice,status,channels,version,stats,magic8,path,trace,tracer,prefix,time,lora,id,neighbors) - Channel-aware response policy (
#bot,#testingfor normal commands;#emergencyfor emergency forwards toPublic; all other channels ignored) - Multi-bot response coordinator with hop-aware delay, bounded TTL, and fingerprint-based suppression so duplicates are avoided across the mesh
- Direct-message (DM) prefixless command parsing
- Two-byte path hash default for compact traces
- Private-key import/export disabled in release builds
- Host-side bot prefs (CLI tunable channels, delay, advert intervals, known-bot list)
See patches/meshcore/ for the full set; each patch is a standalone commit
re-exported from the submodule.
Supported boards
The release workflow builds every *_companion_radio_usb and
*_companion_radio_ble PlatformIO environment exposed by upstream MeshCore
— 133 boards across ESP32, NRF52, RP2040, and STM32 platforms (Heltec V3,
RAK 4631, LilyGo T-Echo, T-Beam, T-Deck, Xiao S3, Nano G2, ThinkNode,
Meshtiny, Pico W, and many more). See the
latest release assets
for the full list.
PR CI builds a smaller "representative" matrix (Heltec V3 USB+BLE, RAK 4631 USB+BLE) on every push, so the most common boards get fast feedback.
Install
Three ways to flash:
MeshCore web flasher (easiest)
- Open flasher.meshcore.io in Chrome/Edge.
- Pick "Custom firmware" and drag in the
*-merged.bin(ESP32) or*.uf2(NRF52) file from the latest release for your board. - Plug the board in, click "Connect", and flash.
PlatformIO (from source)
git clone --recurse-submodules https://github.com/Colorado-Mesh/meshcore-bot-firmware.git
cd meshcore-bot-firmware
python3 -m venv .venv && .venv/bin/python -m pip install --upgrade platformio
bash scripts/apply-patches.sh
cd vendor/MeshCore
pio run -e Heltec_v3_companion_radio_usb -t upload --upload-port /dev/cu.usbserial-0001
Swap the env name for your board (see
grep -rE '^\[env:' vendor/MeshCore/variants/).
esptool / nrfutil (manual)
ESP32 boards: esptool.py write_flash 0x0 <board>-merged.bin
NRF52 boards: drag the .uf2 onto the bootloader mass-storage volume, or
use adafruit-nrfutil dfu serial -pkg <board>.zip -p <port>.
Using the bot
Once flashed, the bot joins the channels configured in its prefs. By default
those are #bot, #testing, #emergency, and Public. Send commands on
#bot (or DM the bot directly) without any prefix — for example:
| Command | Response |
|---|---|
ping |
Pong |
hello |
Hello @[<your-name>], from <bot-name> |
path |
Compact route summary back to you, e.g. Path 3h@2B SNR -6.25 | 2751 -> ea4d -> 430d |
trace |
Active trace request along your reverse path |
status |
Bot uptime, battery, storage, send counters |
neighbors |
Nodes heard directly within the last hour |
version |
Firmware version + build date |
magic8 <question> |
Classic 8-ball answer |
help |
List all commands |
In any other channel the bot stays silent (except #emergency, which it
re-forwards to Public).
If you're testing alongside other Colorado bots, the response coordinator adds a hop-aware delay (~1.5 s per hop, capped at 8 s) so the closest bot wins the race and others suppress. This avoids spam on the channel.
Layout
vendor/MeshCore/— pinned upstream MeshCore submodule.patches/meshcore/— ordered patch queue applied to the submodule.colorado/— Colorado Mesh overlay files, fixtures, and notes.scripts/— wrapper scripts for patch, build, verify, and size-report workflows.tests/firmware_bot/— host-side C++ unit tests for the bot code (compile and run on your machine, no hardware required)..github/workflows/— PR CI (firmware-build.yml) and tag-driven release (release.yml).
Local development
Initialize the submodule and apply patches:
git submodule update --init --recursive
bash scripts/apply-patches.sh
Make firmware changes inside vendor/MeshCore, commit them in the submodule
worktree, then export the patch queue:
bash scripts/export-patches.sh origin/main
Build the four representative companion environments and write size reports:
bash scripts/build-representative.sh --baseline
Run host tests + safety checks:
python3 tests/firmware_bot/run_tests.py
bash scripts/check-bot-safety.sh
# Or all in one go:
MESHCORE_SKIP_APPLY_PATCHES=1 bash scripts/verify.sh --no-build
Releases
Releases are cut by pushing a cmesh-bot-vX.Y.Z tag. See
RELEASE.md for the full procedure.
Contributing
See CONTRIBUTING.md. The short version: edit inside
vendor/MeshCore, re-export the patch queue, run verify.sh, open a PR.
Patches stay in patches/meshcore/ so reviewers can see exactly what
deviates from upstream.
License
MIT, including upstream MeshCore. See LICENSE and vendor/MeshCore/license.txt.