Skip to main content

Crate rfm69_async

Crate rfm69_async 

Source
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 Runner task owns the radio exclusively and arbitrates between TX requests and incoming packets via embassy_futures::select.
  • User tasks hold cheap Stack handles (Copy, clone freely) and call Stack::send / Stack::recv concurrently. The Runner serializes the half-duplex bus underneath.
  • A small MAC layer above the wire — Flags::Ack retries with MacTiming knobs, broadcast filtering, RSSI capture — is provided by the Runner so user code stays simple.
  • Hardware health surfaces as LinkState: a streak of LINK_DOWN_STREAK consecutive TrxErrors flips the link to Down; the next success flips it back. Observe via Stack::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 in embassy-time / embassy-sync / embassy-futures and enables the Stack / Runner surface (plus MacTiming, LinkState, StackResources, TxError). Required for any of the high-level API.
  • log — routes the driver’s internal info! / debug! / warn! / error! macros through the log facade. Use this when piping output over USB CDC via embassy-usb-logger, or any other log-based sink.
  • defmt — derives defmt::Format on the public error / address / packet types and routes the driver’s logging through defmt. Use for defmt-rtt over 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 inherent Rfm69::* methods. Preserves the underlying HAL’s error types verbatim, useful when you want to inspect the cause.
  • TrxError — a fixed, lossy enum at the Transceiver / Stack boundary. The parametric error is collapsed here so Stack / Runner don’t need an extra error-type generic. Mirrors the embassy-net::Stack pattern.

§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 Rfm69 setters 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::run method, which never returns.
Stack
Cheap (Copy) handle to a Stack. Pass to user tasks; clone freely.
StackResources
Caller-allocated channel buffers for a Stack.

Enums§

Address
Error
Error for rfm69 transceiver
Flags
LinkState
Coarse health flag for the radio, observed via Stack.
TrxError
Stable, lossy error enum for the transport-agnostic Transceiver trait.
TxError
Errors returned by Stack::send.

Constants§

LINK_DOWN_STREAK
Number of consecutive TrxErrors on any radio operation that flips LinkState from Up to Down. The next successful operation flips it back to Up.

Traits§

Transceiver