Scripts that automates the process of selecting, downloading, and applying firmware updates from the meshcore-dev/MeshCore & meshtastic/firmware GitHub repository via the USB port.
Download firmware.cmd (right click save)
Make sure file is named firmware.cmd and not firmware.cmd.txt
double click and run the file firmware.cmd
The Windows flasher recognizes the serial-only Seeed XIAO/XIAO Sense DFU
products (2886:0044 / 2886:0045) as well as T1000-E (2886:0057), while
still rejecting their application-mode IDs and rechecking the selected USB
identity after COM-port changes. It selects esptool 4/5 command spellings
automatically. Native stderr warnings do not abort successful operations in
Windows PowerShell 5; a nonzero tool exit still stops the flash.
Normal MeshCore identification leaves DTR/RTS deasserted on native ESP32 USB-Serial/JTAG devices (including Heltec V4), whose hardware interprets those signals as reset/download controls. This prevents the probe from accidentally entering download mode. nRF52 CDC and USB-UART probing retain their existing control-line behavior; intentional flashing resets are unchanged.
Regression checks (no connected radio needed):
powershell -NoProfile -ExecutionPolicy Bypass -File tests/test_firmware_usb_identity.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/test_firmware_native_commands.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/test_firmware_backup_dependencies.ps1Before an nRF52 logical USB backup, both flashers display the selected Python interpreter and the installed, supported, and repair versions of each package:
| Package | Supported stable versions | Pinned repair version |
|---|---|---|
MeshCore Python API (meshcore) |
>=2.3.9,<3 |
2.3.9.1 |
MeshCore CLI (meshcore-cli) |
>=1.6.3,<2 |
1.6.3 |
| PyNaCl | >=1.5,<2 |
1.6.2 |
Python 3.10 or newer is required. Compatible installations are reused after
checking that the USB backup APIs can be imported. Missing, incompatible, or
broken packages trigger one repair attempt using the exact versions above,
followed by a fresh-process API check. Installation failures remain visible
and stop the backup; the existing explicit confirmation before continuing
without a complete backup is unchanged. Linux uses its dedicated backup venv;
Windows uses the Python interpreter selected by firmware.cmd.
To display versions and check APIs without installing anything or connecting
to a radio (use python3 on Linux):
python tools/meshcore_backup.py dependencies --text
Add --install to repair that interpreter's backup dependencies. The Windows
launcher runs this helper as a file, avoiding Windows PowerShell 5's loss of
embedded quotes in inline Python version checks.
2025-05-23.23-47-50.mp4
Copy and run this in your linux terminal
cd ~ && git clone https://github.com/mikecarper/meshfirmware.git && cd meshfirmware && ./mcfirmware.shReadable Code
cd ~
git clone https://github.com/mikecarper/meshfirmware.git
cd meshfirmware
./mcfirmware.shmcfirmware.sh, mcsetup.sh, and mtfirmware.sh check whether they are running
on a Raspberry Pi whose active USB host controller uses the legacy dwc_otg
driver. When that bus is still at its default high speed, the scripts warn that
some hub/radio combinations can repeatedly re-enumerate or lock the host. The
warning specifically identifies a connected Terminus 1a40:0101 hub when
present.
The scripts can add dwc_otg.speed=1 to the Pi's single-line cmdline.txt and
create a one-time .meshfirmware-backup, but only after an explicit Yes.
They ask separately before rebooting. A reboot is required to activate the
setting. The mitigation caps that DWC USB bus at 12 Mbps, so USB Ethernet and
storage become slower; USB radio serial devices commonly already run at 12
Mbps. Set MESHFIRMWARE_PI_USB_CHECK=0 to suppress this startup check.
mcsetup.sh has a U) Reset USB connection action and offers recovery when
the radio does not answer its initial clock query. The MeshCore flasher,
mcfirmware.sh, records the selected identity without resetting it and offers
device-only USB recovery only from its manual post-failure recovery menu.
This is the device-only USB reset used to recover a stalled Station G2 USB
interface. It reconnects USB; it does not reboot the radio CPU, enter the
bootloader, erase flash, change settings, or toggle a GPIO relay. It is
separate from the firmware's reboot command and the flasher's bootloader-entry
sequence. It cannot repair a radio whose firmware itself is hung.
Recovery is optional (default No) and requires Linux and sudo. No healthy
probe triggers it automatically. Close any
serial terminal or stop the service using that radio first; recovery refuses
busy ports rather than stopping programs. The tools save the selected USB
identity before probing, reject hubs or a different/replaced device, and only
continue with a verified returning port. If recovery fails, select the radio
again instead of retrying a possibly reused tty number. The flasher's
MCFIRMWARE_NO_SUDO=1 mode does not perform USB resets.
On Raspberry Pi, the reset helper also resolves the live host-controller
driver from sysfs. It refuses to issue USBDEVFS_RESET through the legacy
dwc_otg driver because cancelling an active USB request can freeze the Pi.
When mcsetup.sh finds an unresponsive radio on that driver, it offers to test
the existing tty with Picocom, reboot the Raspberry Pi after confirmation,
continue without recovery, or quit. Picocom is installed through the detected
package manager when needed and is launched without resetting the serial
control lines on exit. If the tty is unavailable, use the radio's normal
bootloader entry or physically reconnect only that radio, then reselect it.
The script does not offer to replace dwc_otg with dwc2: live testing on a
Zero 2 W with nested hubs found repeated whole-tree disconnects under dwc2.
Before reading the clock and repeater settings, mcsetup.sh checks
get usb.logging. When logging is enabled, it offers to send
set usb.logging off reboot once and then exits cleanly while the radio
reconnects. Continuous debug and packet output can otherwise prevent the
serial reader's idle timeout and make individual setting reads appear stalled.
When logging is off, or an nRF52 exposes its logging stream on the separate
interface 02 tty, setup retains the verified baud and uses a 0.2-second idle
window with a 0.5-second hard deadline for single-value settings. A known noisy command tty is restricted to
the verified baud and a two-second ceiling; multiline commands retain their
separate, longer read behavior.
The shared Python helper is included in a checkout. Single-script downloads
fetch a copy with a checksum pinned by the script; a missing or mismatched
helper disables recovery. This Linux feature does not change mcsetup.cmd
or firmware.cmd on Windows.
Bootloader entry after the user confirms a flash is separate and still uses the mode transition required by that board. Generic ESP32 UART recovery no longer combines a 1200-baud touch with DTR/RTS: after a failed connection it offers one identity-resolved DTR/RTS toggle, default No. Native ESP32 1200-baud fallback is also explicitly confirmed before it can re-enumerate a device.
mcfirmware.sh checks RAK3401 and RAK4631/WisMesh Tag firmware before any
erase or DFU command. It compares identity strings embedded in the firmware
payload with the node's reported board/model and stable USB identity. A
matching pair continues automatically.
A mismatch, ambiguous identity, or unknown RAK payload cancels by default. If
the mismatch is intentional, the script displays a one-time token such as
unknown-to-rak4631 (connected-device-to-firmware-target). Type that exact token at
the prompt to continue. Enter or any other response cancels without erasing or
flashing.
For deliberate unattended use, pass only the exact token printed by the script:
MCFIRMWARE_BOARD_OVERRIDE=unknown-to-rak4631 ./mcfirmware.shThis override is intentionally specific to the detected firmware/device pair;
yes, force, and a token for another pair are rejected.
For nRF52 flash-wipe + flash, a firmware provider or cached catalog may be
missing the board's .erase entry. The script looks up the selected board in
the official MeshCore catalog and refreshes that catalog if needed. It keeps
the selected firmware or bootloader file and the active provider unchanged.
Only an exact, unique board match is selected automatically; otherwise you
must choose an erase package for the physical board or cancel. If no erase
packages are available, the script stops before erase or DFU. The one-time
board override does not bypass this erase-package lookup.
2026-07-09.11-05-48-no-audio.mp4
Copy and run this in your linux terminal
cd ~ && git clone https://github.com/mikecarper/meshfirmware.git && cd meshfirmware && ./mtfirmware.shReadable Code
cd ~
git clone https://github.com/mikecarper/meshfirmware.git
cd meshfirmware
./mtfirmware.shoutput_10mb2.mp4
Copy and run this in your linux terminal
cd ~ && git clone https://github.com/mikecarper/meshfirmware.git && cd meshfirmware && chmod +x mtcompile.sh && ./mtcompile.shReadable Code
cd ~
git clone https://github.com/mikecarper/meshfirmware.git
cd meshfirmware
chmod +x mtcompile.sh
./mtcompile.shfinal_output.mp4
The script does the following:
-
Updates a local cache file with GitHub release data if it is older than 6 hours.
-
Falls back to using the cached data if no internet connection is detected.
-
Parses the JSON release data to build a list of firmware release versions.
-
Appends labels (such as (alpha), (beta), (rc), or (pre-release)) based on the release tag.
-
Prepends the ! label if the release has known issues.
-
Uses lsusb to detect connected USB devices.
-
If more than one matching USB device exists, the user is prompted to choose the correct one.
-
Matches the detected device against available firmware files.
-
If more than one matching firmware file exists, the user is prompted to choose the correct one.
-
For ESP32 devices, the script adjusts the update script (e.g., changes baud rate from 115200 to 1200) as required.
Also allows the user to choose between an update or an install operation -
Stops any systemd service locking the device before proceeding and restarts it afterward.
On Raspberry Pis using the legacy dwc_otg USB host, mcfirmware.sh, mcsetup.sh, and mtfirmware.sh count physical devices on the affected bus (excluding hubs). The recommendation uses a three-tier scale:
- Favor USB 1.1: only nodes, node bootloaders, or serial adapters.
- Middle ground: USB Ethernet/Wi-Fi adapters alone or alongside nodes. USB 1.1 may suit light traffic; USB 2.0 preserves higher throughput. The script does not measure traffic demand, and the 12 Mbps cap is shared across the entire USB bus.
- Favor USB 2.0: storage/SD-card readers or unknown peripherals, even when nodes or networking are also present. An empty bus also defaults to this tier.
Generic serial adapters are labeled as possible nodes because USB descriptors cannot prove which firmware is running. Node DFU/UF2 drives are recognized through bootloader identity or mounted UF2 metadata belonging to that same USB device; ordinary storage is not assumed to be a bootloader just because it also has a serial interface. The check does not mount drives or reset devices.
Tiers 1 and 2 offer the existing opt-in boot setting and reboot prompts, with the network throughput tradeoff stated in the tier-2 prompt. Tier 3 only gives advice, including how to remove an existing 12 Mbps cap. Devices on other USB controllers do not affect the recommendation.
Run the script with the following syntax:
./mtfirmware.sh [OPTIONS] -
--version VERSION
Specify a firmware release version to auto-select (searches for tags containing the provided string). -
--install
Set the operation mode to install (used instead of update). -
--update
Set the operation mode to update (this is the default if not otherwise specified). -
--run
Automatically update firmware without prompting the user. -
-h, --help
Display the help message and exit.