Python package for OTA updating the bootloader on nrf chips
Project description
nrf-ota
Flash firmware to Nordic nRF5x devices over BLE from Python. Implements the Nordic Legacy DFU protocol (nRF5 SDK ≤ 15.x) and works on Linux, macOS, and Windows.
Installation
pip install nrf-ota
CLI
No install required — run directly with uvx:
uvx nrf-ota firmware.zip # interactive device picker
uvx nrf-ota https://example.com/firmware.zip # download then flash
uvx nrf-ota firmware.zip --device OD216205 # select by name
uvx nrf-ota firmware.zip --device FC:06:1C:C8:DE:47 # select by address
Accepts a local ZIP path or an HTTP(S) URL. Scans for nearby BLE devices, lets you pick one, and flashes the firmware. If the device is running application firmware the bootloader is triggered automatically.
Library
import asyncio
from nrf_ota import perform_dfu, scan_for_devices
async def main():
devices = await scan_for_devices(timeout=5.0)
# local file
await perform_dfu("firmware.zip", devices[0], on_progress=lambda pct: print(f"\r{pct:.0f}%", end=""))
# or a URL — downloads and flashes in one call
await perform_dfu("https://example.com/firmware.zip", devices[0])
asyncio.run(main())
API
perform_dfu(zip_path, device, *, on_progress=None, on_log=None, packets_per_notification=...)
Performs a full OTA update — triggers the bootloader if needed, waits for the device to reboot into DFU mode, transfers the firmware, and activates it.
| Parameter | Type | Description |
|---|---|---|
zip_path |
str | DFUZipInfo |
Local path, HTTP(S) URL, or pre-parsed DFUZipInfo |
device |
BLEDevice | str |
Device from scan_for_devices, or a raw Bluetooth address |
on_progress |
Callable[[float], None] |
Called with percentage (0–100) as firmware is sent |
on_log |
Callable[[str], None] |
Called with status messages |
packets_per_notification |
int |
Packets sent per receipt notification. Default: 8 on macOS, 10 elsewhere. |
Raises DFUError on failure, DeviceNotFoundError if the bootloader can't be found after reboot.
DFUZipInfo
Named tuple returned by parse_dfu_zip. Can be passed directly to perform_dfu to skip re-parsing:
from nrf_ota import perform_dfu, parse_dfu_zip
info = parse_dfu_zip("firmware.zip")
print(f"{info.bin_file} {len(info.firmware):,} bytes")
await perform_dfu(info, device)
scan_for_devices(timeout=5.0) -> list[BLEDevice]
Scans for nearby named BLE devices and returns a list of bleak.BLEDevice objects.
Exceptions
| Exception | Description |
|---|---|
DFUError |
Base exception for all DFU failures |
DeviceNotFoundError |
Bootloader not found after reboot |
Platform notes
Works on Linux, macOS, and Windows via bleak. On macOS, the default packets_per_notification is lowered to 8 (from 10) to stay within CoreBluetooth's write-without-response flow control limits.
Development
git clone https://github.com/OpenDisplay-org/nrf-ota.git
cd nrf-ota
uv sync --all-extras
uv run pytest tests/ -v
uv run ruff check .
uv run mypy src/nrf_ota
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nrf_ota-0.3.0.tar.gz.
File metadata
- Download URL: nrf_ota-0.3.0.tar.gz
- Upload date:
- Size: 76.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0010efbb193057cbf0921cb748056b482c16445638a2f6fd9560d96ec9b2dea
|
|
| MD5 |
2a45371284ae29a1326c8cc7ea0a952f
|
|
| BLAKE2b-256 |
64c5ecdb03b45f0d64d43894898120a775a9bd77ba6d8fc56cd1cbb5a142e7d3
|
Provenance
The following attestation bundles were made for nrf_ota-0.3.0.tar.gz:
Publisher:
release.yml on OpenDisplay-org/nrf-ota
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nrf_ota-0.3.0.tar.gz -
Subject digest:
d0010efbb193057cbf0921cb748056b482c16445638a2f6fd9560d96ec9b2dea - Sigstore transparency entry: 983922957
- Sigstore integration time:
-
Permalink:
OpenDisplay-org/nrf-ota@c0fa2ea3d03fba8e057242947980458e0ca039b2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenDisplay-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0fa2ea3d03fba8e057242947980458e0ca039b2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file nrf_ota-0.3.0-py3-none-any.whl.
File metadata
- Download URL: nrf_ota-0.3.0-py3-none-any.whl
- Upload date:
- Size: 22.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4eaf7a3f05974c4430b640782eaae98d0d36bdef60e118db00d70309722de01
|
|
| MD5 |
ae3ad63474804e784c70f7e2eadf142d
|
|
| BLAKE2b-256 |
dcda5141bc9288a46a133e7644e188dc2c667b2e23f90a59003b6ca043c1d6a4
|
Provenance
The following attestation bundles were made for nrf_ota-0.3.0-py3-none-any.whl:
Publisher:
release.yml on OpenDisplay-org/nrf-ota
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nrf_ota-0.3.0-py3-none-any.whl -
Subject digest:
f4eaf7a3f05974c4430b640782eaae98d0d36bdef60e118db00d70309722de01 - Sigstore transparency entry: 983922959
- Sigstore integration time:
-
Permalink:
OpenDisplay-org/nrf-ota@c0fa2ea3d03fba8e057242947980458e0ca039b2 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/OpenDisplay-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0fa2ea3d03fba8e057242947980458e0ca039b2 -
Trigger Event:
push
-
Statement type: