lorawan_device/nb_device/
mod.rs

1//! A non-blocking LoRaWAN device implementation which uses an explicitly defined state machine
2//! for driving the protocol state against pin and timer events. Depends on a non-async radio
3//! implementation.
4use super::radio::RadioBuffer;
5use super::*;
6use crate::nb_device::radio::PhyRxTx;
7use mac::{Mac, SendData};
8
9pub(crate) mod state;
10
11pub mod radio;
12#[cfg(test)]
13mod test;
14
15type TimestampMs = u32;
16
17pub struct Device<R, C, RNG, const N: usize, const D: usize = 1>
18where
19    R: PhyRxTx + Timings,
20    C: CryptoFactory + Default,
21    RNG: RngCore,
22{
23    state: State,
24    shared: Shared<R, RNG, N, D>,
25    crypto: PhantomData<C>,
26}
27
28impl<R, C, RNG, const N: usize, const D: usize> Device<R, C, RNG, N, D>
29where
30    R: PhyRxTx + Timings,
31    C: CryptoFactory + Default,
32    RNG: RngCore,
33{
34    pub fn new(region: region::Configuration, radio: R, rng: RNG) -> Device<R, C, RNG, N, D> {
35        Device {
36            crypto: PhantomData,
37            state: State::default(),
38            shared: Shared {
39                radio,
40                rng,
41                tx_buffer: RadioBuffer::new(),
42                mac: Mac::new(region, R::MAX_RADIO_POWER, R::ANTENNA_GAIN),
43                downlink: Vec::new(),
44            },
45        }
46    }
47
48    pub fn join(&mut self, join_mode: JoinMode) -> Result<Response, Error<R>> {
49        match join_mode {
50            JoinMode::OTAA { deveui, appeui, appkey } => {
51                self.handle_event(Event::Join(NetworkCredentials::new(appeui, deveui, appkey)))
52            }
53            JoinMode::ABP { devaddr, appskey, newskey } => {
54                self.shared.mac.join_abp(newskey, appskey, devaddr);
55                Ok(Response::JoinSuccess)
56            }
57        }
58    }
59
60    pub fn get_radio(&mut self) -> &mut R {
61        &mut self.shared.radio
62    }
63
64    pub fn get_datarate(&mut self) -> region::DR {
65        self.shared.mac.configuration.data_rate
66    }
67
68    pub fn set_datarate(&mut self, datarate: region::DR) {
69        self.shared.mac.configuration.data_rate = datarate
70    }
71
72    pub fn ready_to_send_data(&self) -> bool {
73        matches!(&self.state, State::Idle(_)) && self.shared.mac.is_joined()
74    }
75
76    pub fn send(&mut self, data: &[u8], fport: u8, confirmed: bool) -> Result<Response, Error<R>> {
77        self.handle_event(Event::SendDataRequest(SendData { data, fport, confirmed }))
78    }
79
80    pub fn get_fcnt_up(&self) -> Option<u32> {
81        self.shared.mac.get_fcnt_up()
82    }
83
84    pub fn get_session(&self) -> Option<&mac::Session> {
85        self.shared.mac.get_session()
86    }
87
88    pub fn set_session(&mut self, s: mac::Session) {
89        self.shared.mac.set_session(s)
90    }
91
92    pub fn get_session_keys(&self) -> Option<mac::SessionKeys> {
93        self.shared.mac.get_session_keys()
94    }
95
96    pub fn take_downlink(&mut self) -> Option<Downlink> {
97        self.shared.downlink.pop()
98    }
99
100    pub fn handle_event(&mut self, event: Event<R>) -> Result<Response, Error<R>> {
101        let (new_state, result) = self.state.handle_event::<R, C, RNG, N, D>(
102            &mut self.shared.mac,
103            &mut self.shared.radio,
104            &mut self.shared.rng,
105            &mut self.shared.tx_buffer,
106            &mut self.shared.downlink,
107            event,
108        );
109        self.state = new_state;
110        result
111    }
112}
113
114pub(crate) struct Shared<R: PhyRxTx + Timings, RNG: RngCore, const N: usize, const D: usize> {
115    pub(crate) radio: R,
116    pub(crate) rng: RNG,
117    pub(crate) tx_buffer: RadioBuffer<N>,
118    pub(crate) mac: Mac,
119    pub(crate) downlink: Vec<Downlink, D>,
120}
121
122#[derive(Debug)]
123pub enum Response {
124    NoUpdate,
125    TimeoutRequest(TimestampMs),
126    JoinRequestSending,
127    JoinSuccess,
128    NoJoinAccept,
129    UplinkSending(mac::FcntUp),
130    DownlinkReceived(mac::FcntDown),
131    NoAck,
132    ReadyToSend,
133    SessionExpired,
134    RxComplete,
135}
136
137#[derive(Debug)]
138pub enum Error<R: PhyRxTx> {
139    Radio(R::PhyError),
140    State(state::Error),
141    Mac(mac::Error),
142}
143
144impl<R: PhyRxTx> From<mac::Error> for Error<R> {
145    fn from(mac_error: mac::Error) -> Error<R> {
146        Error::Mac(mac_error)
147    }
148}
149
150pub enum Event<'a, R>
151where
152    R: PhyRxTx,
153{
154    Join(NetworkCredentials),
155    SendDataRequest(SendData<'a>),
156    RadioEvent(radio::Event<'a, R>),
157    TimeoutFired,
158}
159
160impl<'a, R> core::fmt::Debug for Event<'a, R>
161where
162    R: PhyRxTx,
163{
164    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165        let event = match self {
166            Event::Join(_) => "Join",
167            Event::SendDataRequest(_) => "SendDataRequest",
168            Event::RadioEvent(_) => "RadioEvent",
169            Event::TimeoutFired => "TimeoutFired",
170        };
171        write!(f, "lorawan_device::Event::{event}")
172    }
173}