1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: AGPL-3.0-only
//! `no_std` async driver for the HopeRF / Semtech **RFM69** sub-GHz FSK
//! transceiver. Generic over [`embedded-hal`](embedded_hal) and
//! [`embedded-hal-async`](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 `TrxError`s 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.
//!
//! ```ignore
//! 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`](embedded_hal_async::digital::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`](https://docs.rs/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`](https://docs.rs/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.
pub use Address;
pub use Error;
pub use Flags;
pub use Packet;
pub use Rfm69;
pub use ;
pub use ;