Expand description
no_std async driver for the HopeRF / Semtech RFM69 sub-GHz FSK
transceiver. Generic over embedded-hal and
embedded-hal-async 1.0 traits, so the same
driver runs on any MCU whose HAL provides them.
§Two API layers
The crate exposes two ways to drive a radio. Pick whichever fits.
§1. Low-level: Rfm69
Rfm69 is the bare transceiver: register setters, send, recv,
and an Rfm69::reset that does the post-power-up version check.
Use it directly if your application owns the radio from a single
task and you want minimal indirection.
§2. High-level: Stack / Runner (feature embassy)
For applications where multiple tasks want to share one radio — the
common embedded pattern of “always-listening node that occasionally
sends” — Stack and Runner provide an embassy-net-style
split:
- A long-running
Runnertask owns the radio exclusively and arbitrates between TX requests and incoming packets viaembassy_futures::select. - User tasks hold cheap
Stackhandles (Copy, clone freely) and callStack::send/Stack::recvconcurrently. The Runner serializes the half-duplex bus underneath. - A small MAC layer above the wire —
Flags::Ackretries withMacTimingknobs, broadcast filtering, RSSI capture — is provided by the Runner so user code stays simple. - Hardware health surfaces as
LinkState: a streak ofLINK_DOWN_STREAKconsecutiveTrxErrors flips the link toDown; the next success flips it back. Observe viaStack::wait_link_up/Stack::wait_link_down.
See examples/rp/src/bin/concurrent_demo.rs in the repository for
the canonical Stack/Runner setup pattern.
§The Transceiver boundary
Both layers meet at the Transceiver trait — a small async trait
with send / recv and a fixed TrxError vocabulary.
Stack / Runner are generic over TRX: Transceiver, so the
Stack is reusable against any backing radio (or a mock) that
implements the trait, not just Rfm69.
§Quick start
Construction follows the pattern below. The full HAL plumbing
(SPI device, pin modes, async DMA setup) is HAL-specific; see the
examples/rp/ binaries for runnable code on the RP2040.
use rfm69_async::{config, Address, Flags, Rfm69, Stack, StackResources, MacTiming};
// 1. Construct the bare driver from your HAL's SPI / GPIO / delay.
let mut rfm = Rfm69::new(spi, reset_pin, Some(dio0_pin), delay);
// 2. Apply a preset configuration in place. The same call can be re-issued
// later (e.g. from a `Transceiver::recover` impl) to re-initialize after
// a hardware fault.
config::my_defaults(&mut rfm, /* network_id */ 0x42, /* freq Hz */ 868_000_000).await?;
// 3. Split into a Stack handle and a Runner task.
static mut RES: StackResources<4> = StackResources::new();
let (stack, mut runner) = Stack::new(rfm, Address::Unicast(1), unsafe { &mut RES }, MacTiming::default());
// 4. Spawn the runner (here in the same task via embassy_futures::join).
// In real apps, spawn it as an embassy_executor task instead.
embassy_futures::join::join(
runner.run(),
async {
stack.send(Address::Unicast(2), Flags::Ack(3), b"hello").await?;
let pkt = stack.recv().await;
Ok::<_, rfm69_async::TxError>(())
},
).await;For the lower-level direct path, swap step 3+4 for rfm.send(&packet).await?
/ rfm.recv().await? calls in your own loop.
§Optional DIO0 pin
Rfm69::new takes dio0: Option<DIO0>. With a pin connected, the
driver awaits hardware-driven PacketSent / PayloadReady events
via Wait::wait_for_high
— preferred. With None::<SomePin> the driver falls back to
polling IrqFlags2. Both modes are functionally equivalent; the
pin-driven path is cheaper on the CPU.
§Cargo features
All features are off by default — opt in to what you need.
embassy— pulls inembassy-time/embassy-sync/embassy-futuresand enables theStack/Runnersurface (plusMacTiming,LinkState,StackResources,TxError). Required for any of the high-level API.log— routes the driver’s internalinfo!/debug!/warn!/error!macros through thelogfacade. Use this when piping output over USB CDC viaembassy-usb-logger, or any otherlog-based sink.defmt— derivesdefmt::Formaton the public error / address / packet types and routes the driver’s logging throughdefmt. Use fordefmt-rttover a debug probe.
log and defmt are not mutually exclusive — enabling both fans
the driver’s log output to both backends. With neither enabled the
logging macros expand to no-ops that still consume their arguments
(so unused_variables warnings don’t fire on debug-only locals).
§Errors
Two error types deliberately exist:
Error<SPI, RESET, DIO0>— the parametric error returned by the inherentRfm69::*methods. Preserves the underlying HAL’s error types verbatim, useful when you want to inspect the cause.TrxError— a fixed, lossy enum at theTransceiver/Stackboundary. The parametric error is collapsed here soStack/Runnerdon’t need an extra error-type generic. Mirrors theembassy-net::Stackpattern.
§MSRV
1.88. This section is the canonical explanation of the floor;
Cargo.toml, the CHANGELOG, and CI reference it rather than restate
it, so the reasoning lives in exactly one place.
The base crate (no features) builds on 1.87 — the floor
heapless = "0.9" sets; every other base dependency needs less. The
embassy feature is what raises it: the Stack/Runner code in
stack.rs uses a let-chain, stable since 1.88. A package declares a
single rust-version, and it has to cover the most-demanding feature,
so the declared floor is 1.88. No nightly features are used — the
crate builds on any stable toolchain at or above the floor.
Modules§
- config
- Configurations that are available to initialize the rfm69
- registers
- Register addresses, mode enums, and typed wrappers for the values the
Rfm69setters accept.
Structs§
- MacTiming
- Tunable MAC-layer timing knobs, owned by the
Runner. - Packet
- Packet that can be sent and received
- Rfm69
- The rfm69 transceiver
- Runner
- Long-running task that owns the radio. Spawn it once; call its
Runner::runmethod, which never returns. - Stack
- Cheap (
Copy) handle to aStack. Pass to user tasks; clone freely. - Stack
Resources - Caller-allocated channel buffers for a
Stack.
Enums§
- Address
- Error
- Error for rfm69 transceiver
- Flags
- Link
State - Coarse health flag for the radio, observed via
Stack. - TrxError
- Stable, lossy error enum for the transport-agnostic
Transceivertrait. - TxError
- Errors returned by
Stack::send.
Constants§
- LINK_
DOWN_ STREAK - Number of consecutive
TrxErrors on any radio operation that flipsLinkStatefromUptoDown. The next successful operation flips it back toUp.