One page from zero to a verified PASS, whichever kind of user you are.
Pick your row, follow one section, and end at the same place: a device (or
simulator) explaining itself through NOBRO_* reports.
| You have | Start at |
|---|---|
| A compatible board and a prebuilt starter image | Zero-code path |
| Rust + a probe/UF2 board | Hardware quick start |
| Just a laptop | Toolchains and IDEs |
Build or obtain an image for the exact board profile, flash it explicitly, then read its
fixed NOBRO_* report over the transport that application exposes.
- A supported board and its normal upload mechanism (UF2, Arduino, or a debug probe).
- Rust (
rustup target add thumbv7em-none-eabihf) and Python 3.10+. - The compiler and image tools required by the selected port.
python sdk/cli/nobro.py flash --helpApplications that expose a serial report end with an explicit verdict:
=== imu on nosd ===
magic = 1313164366 (0x4E42...)
all_pass = 1 (0x1)
PASS: all_pass=1
Image generation and upload settings are board-specific. Do not reuse an address, image, or boot layout from another profile.
The bootloader and the application are separate USB devices. They may advertise
different VID/PID values, strings, storage volumes, or serial endpoints, and the host may
assign them different device paths. UsbConfig controls only the mounted application
backend; it does not rename or reconfigure the bootloader.
SoftDevice and no-SoftDevice profiles also use different flash layouts. Select the image from the installed bootloader's own metadata rather than inferring the layout from the board shape or from the application's last USB identity.
If application enumeration stops making progress, a backend that supports
UsbStack::force_reenumeration() can detach and reattach the application identity. Rate
limit that recovery, continue calling poll(), and wait for configured() before doing
I/O. Suspended is a valid host power state and should normally be allowed to resume
through poll(); repeatedly forcing enumeration during ordinary suspend creates a
reconnect loop. Forced re-enumeration does not enter DFU. Conversely, failure to see a
bootloader volume or serial endpoint proves only that the host did not enumerate it, not
whether the processor reached bootloader code.
The app seals a fixed-layout report (NOBRO_*_REPORT) and consumers check magic,
completed, all_pass, and its diagnostic checksum. That unkeyed value detects
accidental snapshot corruption only; it is not authentication. A silent target or unavailable peripheral is
not a passing result.
- Serial boards: most demos also print their report line over USB-CDC/UART; any
serial monitor shows the same
all_pass=1. - No hardware at all: the Python simulators under
bindings/pythonand the host test suite (cargo teston the portable crates,tools/checks/ci_matrix.sh) exercise the same contracts on your desktop.
- SPI transfers use EasyDMA. The current portable I2C provider uses bounded CPU-polled
legacy TWI and reports
TransferMode::Polling; contract checks prevent it from being advertised as DMA. - Sensor samples move through the kernel as zero-copy tickets (
SamplePool): producers publish a slot, consumers borrow it - payloads are not copied through queues. - Resource and timing claims must be measured for the final target and workload.
The zero-toolchain path requires a prebuilt image supplied by a release or another trusted builder: flash it by drag-and-drop, watch the board explain itself in a browser, and design your first app as blocks — no Rust, no compiler, no IDE.
You need an nRF52840 board whose installed UF2 bootloader metadata explicitly reports SoftDevice S140.
- Double-tap the RESET button. A USB drive appears (its
INFO_UF2.TXTshould saySoftDevice: S140). - Drag
nobrortos-starter-s140.uf2onto the drive. The board reboots on its own.
There is currently no published release artifact in this repository. A trusted builder runs
python tools/release/package_prebuilt_uf2.py --build and hands you the file from
_work/prebuilt/.
Open packages/web-flasher/index.html in Chrome or Edge and click
Open report console, then pick the board's serial port. The starter streams its
self-verification and the console translates it into plain sentences:
✅ PASS CDC: all checks passing
NobroRTOS IMU who=0x71 addr=104 i2c=1 reads=1240 err=0 accel=1002mg ... PASS
If the required sensor is unavailable, you see exactly which check failed — the same first-fault discipline every NobroRTOS app has.
(No browser with Web Serial? Any serial monitor at 115200 shows the same lines; the repository's Python report tools provide the decoder from a source checkout.)
Open packages/block-editor/index.html, add periodic/control/service tasks and
wires, and export app.json. Validate the exact firmware input:
python sdk/cli/nobro.py app your-app.jsonTurn that same file into native firmware with one command:
python sdk/cli/nobro.py firmware your-app.json --buildThe graph describes scheduling and topology. Bind physical sensors, actuators, and payload transport in the provider layer; the editor does not pretend those drivers exist merely because a task has a hardware-like name.
| You are here | Next rung |
|---|---|
| No-code starter | Arduino library (packages/arduino, no Rust needed) |
| Arduino sketches | Python host tools from this repository |
| Python | C/C++ modules, then the full Rust workspace |
The SDK project flow keeps generated work under ignored _work/projects/ by default:
python sdk/cli/nobro.py project new rover
python sdk/cli/nobro.py project run _work/projects/rover
python sdk/cli/nobro.py project report _work/projects/rover/reports/simulation.jsonrun explains the graph-derived contract and admission headroom, compiles a host
graph regenerated from the same workload.json, runs the bounded simulation, and
decodes its report. The generated scaffold is a host model, not a flashable image.
To opt into a priced capability, edit only the workload's top-level features
object, for example "features": {"capacity-report": true}. The SDK catalog drives
validation, generation, and explanation; unavailable or unpriced combinations fail
instead of being reported as free.
That same file is a native-firmware input—there is no second feature file:
python sdk/cli/nobro.py firmware _work/projects/rover/workload.json \
--out _work/firmware --buildThe no-SoftDevice catalog entry is checked by linked feature-off/feature-on images, including symbol absence/presence and measured resource deltas. Target rows without that evidence remain unavailable.
The concise firmware path starts from tutorials/rover-one-file/app.nobro and generates
a real no_std nRF crate plus the workload consumed by admission/explain:
python sdk/cli/nobro.py firmware tutorials/rover-one-file/app.nobro --build
python sdk/cli/nobro.py project explain _work/projects/rover/workload.jsonThe nRF52840 S140/no-SoftDevice, UNO R4 RA4M1, and SAMD21 profiles own distinct linker,
startup, clock, interrupt, DMA, and boot contracts. The generator never chooses one from
a connected endpoint. Generated files live in ignored _work/projects by default.
Omitting budget means unmeasured and produces no deadline-admission claim; provide a
measured execution bound when deadline admission is required.
An optional wake 25us line after board admits a measured compare-wake-to-dispatch
upper bound. It is an engineering input, not a value inferred from a board name.
Periodic task lines may also shape their first release and use a constrained deadline:
control motor every 5ms phase 1ms deadline 4ms budget 400us
periodic imu every 10ms -> motor
phase must be below every, and deadline must not exceed it. The generator checks
that execution plus blocking fits the deadline and emits the same values into build-time
admission and the target dispatcher.
Generated nRF projects select nobro-hal's board-specific BASEPRI critical-section
provider. Do not add Cortex-M's critical-section-single-core feature: it uses PRIMASK,
conflicts with the selected provider, and would mask the admitted timebase. High-priority
deadline/watchdog handlers must restrict themselves to the documented lock-free handoff.
NobroRTOS is not tied to the Arduino IDE or VS Code. The core builds from a terminal on Linux, macOS, or Windows with Rust, Python, the selected target support, and any external flash utility required by that target.
rustup target add thumbv7em-none-eabihf # or your board's target
cd core
cargo build -p kernel-selftest --release # build firmware
cd ..
python3 sdk/cli/nobro.py flash jlink --bin app.bin --addr 0x1000 # J-Link (nRF)
python3 sdk/cli/nobro.py flash uf2 --file app.uf2 --drive <DRIVE> # UF2-capable board
python3 sdk/cli/nobro.py flash arduino --port <PORT> --fqbn <FQBN> --build-dir <DIR> # ESP/AVRsdk/cli/nobro.py flash is one flashing abstraction over J-Link, UF2 drag-drop, and arduino-cli;
override the J-Link path with the JLINK_EXE env var. tools/build/bin2uf2.py converts raw
binaries only for its explicit nRF52840, RP2040, and RP2350-Arm family IDs.
bash tools/checks/platforms/check_portability.sh # builds the portable core for all 6 MCU families
bash tools/checks/ci_matrix.sh # host tests + portability + port builds + validators
bash tools/checks/lint_gate.sh # clippy -D warnings gate- Any editor + rust-analyzer (Neovim, Helix, Zed, Emacs, IntelliJ-Rust, VS Code).
- No editor at all: the CLI above is complete.
- Arduino IDE / PlatformIO: needed for ESP32/AVR Arduino applications (which run
vendor firmware), via
arduino-cli- not for the NobroRTOS Rust core.
python sdk/cli/nobro.py app my_robot/app.json
python sdk/cli/nobro.py firmware my_robot/app.json --buildDeclare tasks and wires once. The validator, Python host simulator, block editor, and native generator consume the same strict JSON. Provider selection is a separate typed step so hardware metadata does not become a second scheduler model.
- Linux/macOS/Windows: identical Cargo + Python flow; only the flashing backend differs.
- CI:
tools/checks/ci_matrix.shis a single exit-coded gate for GitHub Actions / GitLab CI. - C / C++:
bindings/c+bindings/cppexpose the module ABI for non-Rust codebases. - Web flasher / PlatformIO packaging: the SDK manifest (
sdk/sdk-manifest.json, validated bytools/checks/release/check_sdk_manifest.py) declares the Arduino + PlatformIO surfaces.