ezsp-fwupd updates Silicon Labs Gecko-based Zigbee network co-processors that normally communicate
with a host through ASHv2 and EZSP.
The workspace provides:
ezsp-fwupd: a library that enters the standalone bootloader and uploads a GBL image;cli: an interactive command-line utility for flashing, querying, and inspecting OTA files;auto-updater: a manifest-driven updater that verifies the firmware version after reboot.
The high-level Fwupd implementation:
- Creates a temporary asynchronous ASHv2/EZSP connection on the supplied native serial port.
- Sends the EZSP command that launches the Gecko standalone bootloader.
- Stops the protocol actors and recovers the same native serial port.
- Disables application-level serial flow control and wakes the bootloader's ASCII menu.
- Selects menu option
1and transfers the GBL bytes with the externalxmodemimplementation. - Waits for the bootloader menu to return and selects option
2to run the application. - Restores the port's original timeout and flow-control settings.
The default Gecko UART bootloader uses 115,200 baud, 8 data bits, no parity, one stop bit, and no
flow control. The application firmware may use different flow control; configure the port for the
application before calling fwupd(). The library disables flow control only while the bootloader is
active.
Fwupd::fwupd accepts an iterator of raw GBL bytes. A Zigbee OTA container can be parsed with
OtaFile; its payload is the byte stream passed to the bootloader.
use std::fs::read;
use std::time::Duration;
use ezsp_fwupd::{FrameCount, Fwupd, OtaFile};
use indicatif::ProgressBar;
use le_stream::FromLeStream;
use serialport::FlowControl;
const BAUD_RATE: u32 = 115_200;
# async fn flash(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let ota = OtaFile::from_le_stream_exact(read(path)?.into_iter())?
.validate()
.map_err(|_| std::io::Error::other("invalid OTA magic"))?;
let firmware = ota.into_payload();
let progress = ProgressBar::new(firmware.frame_count() as u64);
let serial_port = serialport::new("/dev/ttyUSB0", BAUD_RATE)
.flow_control(FlowControl::Software)
.open_native()?;
let _serial_port = serial_port
.fwupd(
firmware,
Some(Duration::from_secs(1)),
Some(&progress),
)
.await?;
# Ok(())
# }The timeout applies to the synchronous Gecko menu and XMODEM stages. Supplying None keeps the
port's existing timeout. The optional progress bar advances once per standard 128-byte XMODEM block.
On success, the returned value is the original serial port with its previous settings restored.
For direct EZSP access, use make_uart(...).await. It returns an ezsp::Connection and a Tasks
owner. Keep Tasks alive while using the connection; Tasks::terminate().await stops and joins the
protocol actors and returns the underlying serial port.
GeckoBootloader exposes the lower-level ASCII menu operations. start_xmodem_upload() consumes
the bootloader's status response through its initial ASCII C; the high-level updater replays that
validated request to xmodem::Xmodem::send to negotiate CRC mode.
Run the CLI from the workspace with:
cargo run -p cli -- flash /dev/ttyUSB0 firmware.ota
cargo run -p cli -- query /dev/ttyUSB0
cargo run -p cli -- reset /dev/ttyUSB0
cargo run -p cli -- ota firmware.otaUse --help on the program or a subcommand for timeout and diagnostic options.
The automatic updater reads a JSON manifest, compares its version with the running firmware,
performs an upgrade or downgrade when required, waits for reboot, and validates the resulting
version. Its default manifest path is /etc/ezsp-firmware-update.json.
A missing manifest or an absent or null active entry means no update is configured; the updater
exits successfully. Other file-read errors and invalid manifest contents cause it to fail.
{
"active": {
"version": "1.2.3",
"filename": "/var/lib/ezsp/firmware-1.2.3.ota"
}
}Run it with:
cargo run -p auto-updater -- /dev/ttyUSB0- The project is under active development and its API may change.
- The implementation targets interactive Gecko standalone UART bootloaders and standard 128-byte XMODEM blocks.
xmodem0.4 counts unexpected block responses against its error budget but does not replay the same input block. A noisy serial link can therefore abort an update rather than recover through a retransmission.