RFM69-Async is an async driver for the SubGhz transceiver RFM69.
Examples are found in the examples/ folder separated by the chip manufacturer they are designed to run on. For example:
examples/rpare for the RP2040 chip.
The RP2040 binaries are:
blinky— board sanity check; no radio involved.rfm69— single-task send/receive loop.echo_clientandecho_server— paired roles for round-trip testing over two boards: client sends, server echoes back.concurrent_demo— splits send and receive across independent embassy tasks driven by a single radio Runner. Headline feature of theStack/RunnerAPI: user tasks callstack.send()andstack.recv()concurrently with no manual rx/tx interleaving.
- Install tools to debug/flash the firmware. For example to flash the firmware to the rpi pico via USB:
cargo install elf2uf2-rs- Change directory to the sample's base directory. For example:
cd examples/rp- Build the example
For example:
cargo build --bin rfm69 --release- Put the Pico in BOOTSEL mode
Hold the BOOTSEL button while plugging the USB cable in (or, if it is already
plugged in, hold BOOTSEL and tap RESET). The board enumerates as a USB
mass-storage device named RPI-RP2.
- Ensure the
RPI-RP2volume is mounted
elf2uf2-rs -d looks at the mount table to find the Pico, so simply having the
device appear in dmesg / lsblk is not enough — it must actually be mounted.
On desktops with auto-mount this happens automatically; on minimal setups mount
it manually, e.g.:
udisksctl mount -b /dev/sda1 # adjust device per `lsblk`- Flash the example
For example:
elf2uf2-rs -d target/thumbv6m-none-eabi/release/rfm69The bins under examples/rp/ ship two independent log transports. Pick whichever
matches your hardware setup:
The default. After the Pico finishes booting it enumerates a second time as a
USB CDC ACM device (/dev/ttyACM* on Linux, /dev/tty.usbmodem* on macOS,
COM* on Windows) driven by embassy-usb-logger. All log::* output from the
example bins and from the driver crate is forwarded over this serial link.
Read it with any terminal:
# Linux
screen /dev/ttyACM0 115200 # exit with Ctrl-A Ctrl-\
# or
picocom -b 115200 /dev/ttyACM0The first line you should see after a freshly flashed echo_server is
--- Staring echo server ---, followed by Reading version register... /
Version: 0x24 from the driver. Note that the bins wait a few seconds at boot
so the host has time to finish enumerating the USB device before logging starts.
This transport carries the driver's logs because the examples crate enables
the driver's log cargo feature. If you depend on rfm69-async in your own
project and want the same behaviour, enable the log feature on the driver
dependency:
rfm69-async = { version = "…", features = ["embassy", "log"] }The bins also pull in defmt-rtt and panic-probe. If you have a debug probe
wired to the Pico's SWD pins (e.g. the Raspberry Pi Debug Probe, or a second
Pico flashed with the debugprobe firmware), you can flash and stream
defmt-formatted output directly with probe-rs instead of UF2:
cargo install probe-rs-tools
probe-rs run --chip RP2040 target/thumbv6m-none-eabi/release/rfm69probe-rs run halts the target on a panic and prints the decoded panic
message; UF2 flashing has no equivalent. To also forward driver-internal logs
through this transport, additionally enable the driver's defmt feature
(already on in the examples).
A two-board automated round-trip test lives in hil-runner/ (host-side
assertion engine) plus examples/rp/src/bin/hil_send_client.rs +
hil_recv_server.rs (the test bins). The pipeline — build, picotool-flash
both boards, stream their USB-CDC log output, assert each side passes its
scenario — is driven by just.
Prerequisites:
- Two RP2040 boards wired identically to the rest of
examples/rp/and USB-connected to the host. picotool— install from upstream: https://github.com/raspberrypi/picotooljust—cargo install just.
Discover each board's picotool serial:
just hil-detectCreate a local .env at the repo root (gitignored) with four entries
copied from the output of just hil-detect plus
ls /dev/serial/by-id/ after flashing once manually:
HIL_BOARD_A=E660C0D1B3818C32 # client board
HIL_BOARD_B=E660C0D1B381AB12 # server board
HIL_PORT_A=/dev/serial/by-id/usb-...
HIL_PORT_B=/dev/serial/by-id/usb-...
Then:
just hil-testflashes both boards in the right order, then asserts that hil_send_client
emits HIL: PASS sent=100 ack_timeouts=0 and hil_recv_server emits
HIL: PASS received=100 within 60 s. Exit codes: 0 = success, 1 = explicit
HIL: FAIL on a board, 2 = timeout.
The test exercises the real RF/SPI/ACK round-trip — the bins use the same
config::my_defaults profile and Stack/Runner setup as the rest of the
examples, with an added 16-bit counter payload so the server can dedup
ACK-loss-driven retransmits.
User-visible changes are tracked in rfm69-async/CHANGELOG.md,
following the Keep a Changelog format.
Version bumps are driven by cargo-release;
configuration lives in rfm69-async/release.toml.
Edit the ## [Unreleased] section of the changelog with the user-facing
notes for the new release, then:
cargo install cargo-release # one-time
cd rfm69-async
cargo release 0.1.0 # dry-run preview
cargo release 0.1.0 --execute # bump Cargo.toml + examples path-dep,
# rename the changelog heading, commit, tag
git push --follow-tags # explicit — release.toml has push = false
cargo publish # explicit — release.toml has publish = falseRelease tags must be annotated. cargo release already creates annotated
tags, so the flow above is fine. If you ever tag by hand — e.g. recreating a
tag on main after a rebase-merge — use git tag -a vX.Y.Z -m "Release X.Y.Z",
never a lightweight git tag vX.Y.Z.
This work is licensed under the GNU Affero General Public License v3.0 only (LICENSE or https://www.gnu.org/licenses/agpl-3.0.html).
SPDX-License-Identifier: AGPL-3.0-only
A small number of files in examples/rp/ were copied or adapted from the
rp-rs/rp2040-project-template and embassy-rs/embassy projects and remain
under their original MIT OR Apache-2.0 licensing; see
licenses/THIRD-PARTY-NOTICES.md for
details. Each file's effective license is declared in its own
SPDX-License-Identifier header.
Versions 0.0.1 and 0.0.2 were released under the dual MIT OR Apache-2.0
license; that licensing remains in effect for those published versions.
Any contribution intentionally submitted for inclusion in the work by you shall be licensed under AGPL-3.0-only, without any additional terms or conditions.
The code is inspired by https://github.com/almusil/rfm69, which was inspired by older https://github.com/lolzballs/rfm69.