Skip to content

Guided LoRa updates

Run one Python command to update a directly reachable Repeater or Room Server. The wizard identifies the board, chooses compatible firmware, prepares the transfer, installs it, checks the result, and restores the original radio, RX power-saving, and OTA settings. On supported nRF52 boards it also checks for a newer compatible signed bootloader and includes that update in the plan.

Connect a source radio

Use either connection:

Computer connection Source radio Destination
Raspberry Pi → USB ESP32 Repeater with OTA and ota.peer support Repeater or Room Server
Computer or Pi → USB Full Companion with USB OTA source and direct admin support Repeater or Room Server

A Station G2 Repeater connected to a Pi can manage and serve an update to an Ikoka Stick Repeater through that single USB connection. It does not need a second local radio or the phone app. Install a source firmware build containing ota.peer first; older Repeater firmware cannot manage the remote node through USB. Check with ota.peer status in its USB console.

The Full Companion source also needs a current build containing the wizard's version 2 direct admin support. An older Full Companion may serve OTA files but cannot provide this guarded admin connection; the wizard will ask for a source firmware upgrade instead of proceeding with that connection. Check get ota.direct from its USB console for v=2. Update replies use a separate mailbox; the wizard does not consume ordinary queued messages or change saved contact routes. Some Full Companion boards borrow queue space for OTA and retain up to 128 unread messages during an update. If more are waiting, sync them with an app first; the wizard refuses to discard them to make room.

The source and destination must already use the same main-channel settings and hear each other directly. The destination needs working LoRa OTA support and its admin password. A stock nRF52 bootloader may need a matching local installation before it can accept remote updates.

The wizard sends discovery, admin commands, and OTA traffic directly with no relay hops. An older destination may put its first login reply in a flood envelope; the wizard accepts it only if it arrives with zero hops, establishes a direct return route, then requires a fresh direct identity check.

Close other apps using the USB port before starting.

Start

From the MeshCore checkout, run:

python3 tools/lora_ota/ota_wizard.py

On Windows, use py in place of python3. No command-line options are needed. The menu provides these actions:

  1. Update a nearby radio — the default.
  2. Check radios / save a preparation request — inspect without installing.
  3. Prepare an update bundle on this computer — no radios needed.
  4. Recover an interrupted update — restore saved settings.
  5. Settings — change transfer, bootloader, connection, or build preferences.
  6. Exit.

The first live connection installs USB and verification dependencies in a private Python environment if needed. On Raspberry Pi OS, install python3-venv if Python reports that it is missing. Preparing a signed input file can also install signature-verification support on first use, without installing the USB software.

For an update, the wizard selects the USB radio and destination automatically when there is only one of each. Otherwise, choose from the displayed lists. Enter the destination admin password once, choose firmware, then review the plan and press Enter to start. Password entry is hidden and passwords are never saved. After an action, the menu is shown again.

Choose firmware from GitHub releases

The firmware menu offers:

  • Latest compatible GitHub release (recommended): downloads the published stable file for the detected board and role.
  • Choose another GitHub release: lists compatible releases, including clearly labelled previews, so you can choose a version.
  • Local firmware file or prepared bundle: accepts an application DFU ZIP, BIN, HEX, full mOTA, or a bundle prepared on another computer.
  • Build custom Full firmware from source: offered on a computer with the firmware build tools and Bash. It is not offered by default on a Pi.

You do not need to copy release URLs or manually download published files. The wizard checks the board and role, searches release files for an exact running base when preparing a differential update, and checks for a compatible signed bootloader on supported nRF52 destinations. Firmware that is already installed or older is left in place.

The final plan shows the package sizes, firmware version, and any bootloader or trusted update signer being installed. After reboot, the wizard checks the running firmware hash and version, or the bootloader identity and version, before reporting success.

Differential or full updates

Automatic is the default transfer setting. It uses a differential package when a compatible builder and the exact running firmware are available and the package is smaller. A file with the same version label but a different body hash is not a usable base. Published bases are found through GitHub releases; a local base can be selected when the running image was custom. Unsigned full .mota files can also supply the new image for a differential update. Signed packages retain their signatures; prepared bundles retain their chosen transfer format.

On a computer, the differential builder can be prepared automatically with a compatible Rust toolchain. On a Pi without a builder, a full update is made by Python without compiling. The destination must support full updates, such as an Ikoka Stick with working QSPI storage.

To force a full application transfer, open Settings, choose Full for the transfer method, then start the update. Settings also lets you turn the bootloader check off for an application-only update, choose a USB port or source type, or set a builder path, custom source folder/version, or cache folder.

An internal-flash nRF52 destination that requires differential installation cannot use the full fallback. Use the computer-to-Pi workflow below.

Prepare on a computer, send from a Pi

All steps use the same menu; no command-line options are needed.

  1. On the Pi, choose Check radios / save a preparation request. Connect to the source and select the destination as prompted. Copy the JSON file shown after Preparation request: to the computer. It contains public board information and the exact running hash, without the password.
  2. On the computer, choose Prepare an update bundle on this computer. Select the saved request or enter its file path. Choose firmware from GitHub releases, or select a local custom image. The wizard searches for the exact base and offers a local base file if needed. Accept the proposed output filename or choose another one.
  3. Copy the resulting .ota.zip file to the Pi. Choose Update a nearby radio, then Local firmware file or prepared bundle, and select that ZIP.

The preparation step does not open a radio. When the builder and input files are already present, local preparation also works without network access. For a full bundle on a destination that supports it, choose Full in Settings before preparation; this needs neither a differential compiler nor USB dependencies.

The live destination is checked again before installation. A changed base hash or a different board stops a differential update.

If the connection is interrupted

Keep the same source radio connected and run the wizard again. Choose Recover an interrupted update, then select the saved session by its public-key prefix and progress state. The wizard checks the complete source and destination keys before restoring settings. A normal update also restores an unfinished session for the selected destination before proceeding.

Keep the recovery record until restoration is complete. A lost install reply is followed by a running-image check; the wizard does not blindly repeat an install command. A cancelled download is cleared only when its package ID matches this update. Automatic installation is restored only after installation or receive-slot clearance is confirmed; an uncertain result leaves it off and keeps the recovery record.

Temporary radio windows use the existing main-channel settings. Existing temporary schedules block an update before settings are changed, including on older firmware. The window covers both bootloader and firmware transfers, including reboot and setup time. Status checks normally run every ten seconds.

The cache is normally ~/.cache/meshcore/ota on Linux, ~/Library/Caches/MeshCore/ota on macOS, or %LOCALAPPDATA%\MeshCore\ota on Windows. It contains firmware, public preparation records, selected board keys, and restoration settings. Trusted signer keys added for an update remain installed on the destination.

Optional command-line use for automation

Interactive users can use the menus for every step. Existing command-line options remain available for scripts:

Option Purpose
--check Inspect without downloading or installing firmware
--port /dev/ttyUSB0 Choose a port; Windows example: --port COM5
--mode repeater or --mode companion Select the source type
--target PUBLIC_KEY Choose the full destination key; direct discovery is still required
--firmware FILE Use a local firmware image or prepared bundle
--base FILE Supply the exact running image for differential preparation
--full Force a full transfer where supported
--build Build custom Full firmware from source
--no-bootloader Update application firmware only
--prepare-only --request DEVICE.json --firmware FILE --output update.ota.zip Prepare without radios
--recover --target PUBLIC_KEY Restore settings and exit
--yes Accept the displayed plan; credentials and required choices must still be supplied

For unattended use, provide MESHCORE_ADMIN_PASSWORD in the process environment and pass the port, source type, and target key. The password is excluded from preferences, recovery records, and the custom build environment.

For individual commands and a manual board example, see Station G2 to Ikoka Stick. For routed updates and explicit multi-node setup, use the scripted LoRa OTA guide.