esp_ieee802154/
lib.rs

1//! Low-level [IEEE 802.15.4] driver for the ESP32-C6 and ESP32-H2.
2//!
3//! Implements the PHY/MAC layers of the IEEE 802.15.4 protocol stack, and
4//! supports sending and receiving of raw frames.
5//!
6//! This library is intended to be used to implement support for higher-level
7//! communication protocols, for example [esp-openthread].
8//!
9//! Note that this crate currently requires you to enable the `unstable` feature
10//! on `esp-hal`.
11//!
12//! NOTE: Coexistence with Wi-Fi or Bluetooth is currently not possible. If you do it anyway,
13//! things will break.
14//!
15//! [IEEE 802.15.4]: https://en.wikipedia.org/wiki/IEEE_802.15.4
16//! [esp-openthread]: https://github.com/esp-rs/esp-openthread
17//!
18//! ## Feature Flags
19#![doc = document_features::document_features!()]
20#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
21#![no_std]
22
23use core::cell::RefCell;
24
25use byte::{BytesExt, TryRead};
26use critical_section::Mutex;
27use esp_config::*;
28use esp_hal::{clock::PhyClockGuard, peripherals::IEEE802154};
29use heapless::Vec;
30use ieee802154::mac::{self, FooterMode, FrameSerDesContext};
31
32use self::{
33    frame::FRAME_SIZE,
34    pib::{CONFIG_IEEE802154_CCA_THRESHOLD, IEEE802154_FRAME_EXT_ADDR_SIZE},
35    raw::*,
36};
37pub use self::{
38    frame::{Frame, ReceivedFrame},
39    pib::{CcaMode, PendingMode},
40    raw::RawReceived,
41};
42
43mod fmt;
44mod frame;
45mod hal;
46mod pib;
47mod raw;
48
49/// IEEE 802.15.4 errors
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum Error {
52    /// The requested data is bigger than available range, and/or the offset is
53    /// invalid
54    Incomplete,
55    /// The requested data content is invalid
56    BadInput,
57}
58
59impl From<byte::Error> for Error {
60    fn from(err: byte::Error) -> Self {
61        match err {
62            byte::Error::Incomplete | byte::Error::BadOffset(_) => Error::Incomplete,
63            byte::Error::BadInput { .. } => Error::BadInput,
64        }
65    }
66}
67
68struct QueueConfig {
69    rx_queue_size: usize,
70}
71
72pub(crate) const CONFIG: QueueConfig = QueueConfig {
73    rx_queue_size: esp_config_int!(usize, "ESP_IEEE802154_CONFIG_RX_QUEUE_SIZE"),
74};
75
76/// IEEE 802.15.4 driver configuration
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct Config {
79    pub auto_ack_tx: bool,
80    pub auto_ack_rx: bool,
81    pub enhance_ack_tx: bool,
82    pub promiscuous: bool,
83    pub coordinator: bool,
84    pub rx_when_idle: bool,
85    pub txpower: i8,
86    pub channel: u8,
87    pub cca_threshold: i8,
88    pub cca_mode: CcaMode,
89    pub pan_id: Option<u16>,
90    pub short_addr: Option<u16>,
91    pub ext_addr: Option<u64>,
92}
93
94impl Default for Config {
95    fn default() -> Self {
96        Self {
97            auto_ack_tx: Default::default(),
98            auto_ack_rx: Default::default(),
99            enhance_ack_tx: Default::default(),
100            promiscuous: Default::default(),
101            coordinator: Default::default(),
102            rx_when_idle: Default::default(),
103            txpower: 10,
104            channel: 15,
105            cca_threshold: CONFIG_IEEE802154_CCA_THRESHOLD,
106            cca_mode: CcaMode::Ed,
107            pan_id: None,
108            short_addr: None,
109            ext_addr: None,
110        }
111    }
112}
113
114/// IEEE 802.15.4 driver
115#[derive(Debug)]
116pub struct Ieee802154<'a> {
117    _align: u32,
118    transmit_buffer: [u8; FRAME_SIZE],
119    _phy_clock_guard: PhyClockGuard<'a>,
120}
121
122impl<'a> Ieee802154<'a> {
123    /// Construct a new driver, enabling the IEEE 802.15.4 radio in the process
124    ///
125    /// NOTE: Coexistence with Wi-Fi or Bluetooth is currently not possible. If you do it anyway,
126    /// things will break.
127    pub fn new(radio: IEEE802154<'a>) -> Self {
128        Self {
129            _align: 0,
130            transmit_buffer: [0u8; FRAME_SIZE],
131            _phy_clock_guard: esp_ieee802154_enable(radio),
132        }
133    }
134
135    /// Set the configuration for the driver
136    pub fn set_config(&mut self, cfg: Config) {
137        set_auto_ack_tx(cfg.auto_ack_tx);
138        set_auto_ack_rx(cfg.auto_ack_rx);
139        set_enhance_ack_tx(cfg.enhance_ack_tx);
140        set_promiscuous(cfg.promiscuous);
141        set_coordinator(cfg.coordinator);
142        set_rx_when_idle(cfg.rx_when_idle);
143        set_tx_power(cfg.txpower);
144        set_channel(cfg.channel);
145        set_cca_theshold(cfg.cca_threshold);
146        set_cca_mode(cfg.cca_mode);
147
148        if let Some(pan_id) = cfg.pan_id {
149            set_panid(0, pan_id);
150        }
151
152        if let Some(short_addr) = cfg.short_addr {
153            set_short_address(0, short_addr);
154        }
155
156        if let Some(ext_addr) = cfg.ext_addr {
157            let mut address = [0u8; IEEE802154_FRAME_EXT_ADDR_SIZE];
158            address.copy_from_slice(&ext_addr.to_be_bytes()); // LE or BE?
159
160            set_extended_address(0, address);
161        }
162    }
163
164    /// Start receiving frames
165    pub fn start_receive(&mut self) {
166        ieee802154_receive();
167    }
168
169    /// Return the raw data of a received frame
170    pub fn raw_received(&mut self) -> Option<RawReceived> {
171        ieee802154_poll()
172    }
173
174    /// Get a received frame, if available
175    pub fn received(&mut self) -> Option<Result<ReceivedFrame, Error>> {
176        if let Some(raw) = ieee802154_poll() {
177            let maybe_decoded = if raw.data[0] as usize > raw.data.len() {
178                // try to decode up to data.len()
179                mac::Frame::try_read(&raw.data[1..][..raw.data.len()], FooterMode::Explicit)
180            } else {
181                mac::Frame::try_read(&raw.data[1..][..raw.data[0] as usize], FooterMode::Explicit)
182            };
183
184            let result = match maybe_decoded {
185                Ok((decoded, _)) => {
186                    // crc is not written to rx buffer
187                    let rssi = if (raw.data[0] as usize > raw.data.len()) || (raw.data[0] == 0) {
188                        raw.data[raw.data.len() - 1] as i8
189                    } else {
190                        raw.data[raw.data[0] as usize - 1] as i8
191                    };
192
193                    Ok(ReceivedFrame {
194                        frame: Frame {
195                            header: decoded.header,
196                            content: decoded.content,
197                            payload: Vec::from_slice(decoded.payload).unwrap(),
198                            footer: decoded.footer,
199                        },
200                        channel: raw.channel,
201                        rssi,
202                        lqi: rssi_to_lqi(rssi),
203                    })
204                }
205                Err(err) => Err(err.into()),
206            };
207
208            Some(result)
209        } else {
210            None
211        }
212    }
213
214    /// Transmit a frame
215    pub fn transmit(&mut self, frame: &Frame) -> Result<(), Error> {
216        let frm = mac::Frame {
217            header: frame.header,
218            content: frame.content,
219            payload: &frame.payload,
220            footer: frame.footer,
221        };
222
223        let mut offset = 1usize;
224        self.transmit_buffer
225            .write_with(
226                &mut offset,
227                frm,
228                &mut FrameSerDesContext::no_security(FooterMode::Explicit),
229            )
230            .unwrap();
231        self.transmit_buffer[0] = (offset - 1) as u8;
232
233        ieee802154_transmit(self.transmit_buffer.as_ptr(), false); // what about CCA?
234
235        Ok(())
236    }
237
238    /// Transmit a raw frame
239    pub fn transmit_raw(&mut self, frame: &[u8]) -> Result<(), Error> {
240        self.transmit_buffer[1..][..frame.len()].copy_from_slice(frame);
241        self.transmit_buffer[0] = frame.len() as u8;
242
243        ieee802154_transmit(self.transmit_buffer.as_ptr(), false); // what about CCA?
244
245        Ok(())
246    }
247
248    /// Set the transmit done callback function.
249    pub fn set_tx_done_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
250        critical_section::with(|cs| {
251            let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
252            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
253            tx_done_callback.replace(cb);
254        });
255    }
256
257    /// Clear the transmit done callback function.
258    pub fn clear_tx_done_callback(&mut self) {
259        critical_section::with(|cs| {
260            let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
261            tx_done_callback.take();
262        });
263    }
264
265    /// Set the receive available callback function.
266    pub fn set_rx_available_callback(&mut self, callback: &'a mut (dyn FnMut() + Send)) {
267        critical_section::with(|cs| {
268            let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
269            let cb: &'static mut (dyn FnMut() + Send) = unsafe { core::mem::transmute(callback) };
270            rx_available_callback.replace(cb);
271        });
272    }
273
274    /// Clear the receive available callback function.
275    pub fn clear_rx_available_callback(&mut self) {
276        critical_section::with(|cs| {
277            let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
278            rx_available_callback.take();
279        });
280    }
281
282    /// Set the transmit done callback function.
283    pub fn set_tx_done_callback_fn(&mut self, callback: fn()) {
284        critical_section::with(|cs| {
285            let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
286            tx_done_callback_fn.replace(callback);
287        });
288    }
289
290    /// Clear the transmit done callback function.
291    pub fn clear_tx_done_callback_fn(&mut self) {
292        critical_section::with(|cs| {
293            let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
294            tx_done_callback_fn.take();
295        });
296    }
297
298    /// Set the receive available callback function.
299    pub fn set_rx_available_callback_fn(&mut self, callback: fn()) {
300        critical_section::with(|cs| {
301            let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
302            rx_available_callback_fn.replace(callback);
303        });
304    }
305
306    /// Clear the receive available callback function.
307    pub fn clear_rx_available_callback_fn(&mut self) {
308        critical_section::with(|cs| {
309            let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
310            rx_available_callback_fn.take();
311        });
312    }
313}
314
315impl Drop for Ieee802154<'_> {
316    fn drop(&mut self) {
317        self.clear_tx_done_callback();
318        self.clear_tx_done_callback_fn();
319        self.clear_rx_available_callback();
320        self.clear_rx_available_callback_fn();
321    }
322}
323
324/// Convert from RSSI (Received Signal Strength Indicator) to LQI (Link Quality
325/// Indication)
326///
327/// RSSI is a measure of incoherent (raw) RF power in a channel. LQI is a
328/// cumulative value used in multi-hop networks to assess the cost of a link.
329pub fn rssi_to_lqi(rssi: i8) -> u8 {
330    if rssi < -80 {
331        0
332    } else if rssi > -30 {
333        0xff
334    } else {
335        let lqi_convert = ((rssi as u32).wrapping_add(80)) * 255;
336        (lqi_convert / 50) as u8
337    }
338}
339
340static TX_DONE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
341    Mutex::new(RefCell::new(None));
342
343static RX_AVAILABLE_CALLBACK: Mutex<RefCell<Option<&'static mut (dyn FnMut() + Send)>>> =
344    Mutex::new(RefCell::new(None));
345
346#[allow(clippy::type_complexity)]
347static TX_DONE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
348
349#[allow(clippy::type_complexity)]
350static RX_AVAILABLE_CALLBACK_FN: Mutex<RefCell<Option<fn()>>> = Mutex::new(RefCell::new(None));
351
352fn tx_done() {
353    trace!("tx_done callback");
354
355    critical_section::with(|cs| {
356        let mut tx_done_callback = TX_DONE_CALLBACK.borrow_ref_mut(cs);
357        let tx_done_callback = tx_done_callback.as_mut();
358
359        if let Some(tx_done_callback) = tx_done_callback {
360            tx_done_callback();
361        }
362
363        let mut tx_done_callback_fn = TX_DONE_CALLBACK_FN.borrow_ref_mut(cs);
364        let tx_done_callback_fn = tx_done_callback_fn.as_mut();
365
366        if let Some(tx_done_callback_fn) = tx_done_callback_fn {
367            tx_done_callback_fn();
368        }
369    });
370}
371
372fn rx_available() {
373    trace!("rx available callback");
374
375    critical_section::with(|cs| {
376        let mut rx_available_callback = RX_AVAILABLE_CALLBACK.borrow_ref_mut(cs);
377        let rx_available_callback = rx_available_callback.as_mut();
378
379        if let Some(rx_available_callback) = rx_available_callback {
380            rx_available_callback();
381        }
382
383        let mut rx_available_callback_fn = RX_AVAILABLE_CALLBACK_FN.borrow_ref_mut(cs);
384        let rx_available_callback_fn = rx_available_callback_fn.as_mut();
385
386        if let Some(rx_available_callback_fn) = rx_available_callback_fn {
387            rx_available_callback_fn();
388        }
389    });
390}