atat/
lib.rs

1//! A helper crate to abstract away the state management and string parsing of
2//! AT command communication.
3//!
4//! It works by creating structs for each AT command, that each implements
5//! [`AtatCmd`]. With corresponding response structs that each implements
6//! [`AtatResp`].
7//!
8//! This can be simplified alot using the [`atat_derive`] crate!
9//!
10//! [`AtatCmd`]: trait.AtatCmd.html
11//! [`AtatResp`]: trait.AtatResp.html
12//! [`atat_derive`]: <https://crates.io/crates/atat_derive>
13//!
14//! # Examples
15//!
16//! ### Command and response example without `atat_derive`:
17//! ```
18//! use atat::{AtatCmd, AtatResp, Error, InternalError};
19//! use core::fmt::Write;
20//! use heapless::{String, Vec};
21//!
22//! pub struct SetGreetingText<'a> {
23//!     pub text: &'a str,
24//! }
25//!
26//! pub struct GetGreetingText;
27//!
28//! pub struct NoResponse;
29//!
30//! impl AtatResp for NoResponse {};
31//!
32//! pub struct GreetingText {
33//!     pub text: String<64>,
34//! };
35//!
36//! impl AtatResp for GreetingText {};
37//!
38//! impl<'a> AtatCmd for SetGreetingText<'a> {
39//!     type Response = NoResponse;
40//!     const MAX_LEN: usize = 64;
41//!
42//!     fn write(&self, mut buf: &mut [u8]) -> usize {
43//!         let buf_len = buf.len();
44//!         use embedded_io::Write;
45//!         write!(buf, "AT+CSGT={}", self.text);
46//!         buf_len - buf.len()
47//!     }
48//!
49//!     fn parse(&self, resp: Result<&[u8], InternalError>) -> Result<Self::Response, Error> {
50//!         Ok(NoResponse)
51//!     }
52//! }
53//!
54//! impl AtatCmd for GetGreetingText {
55//!     type Response = GreetingText;
56//!     const MAX_LEN: usize = 8;
57//!
58//!     fn write(&self, mut buf: &mut [u8]) -> usize {
59//!         let cmd = b"AT+CSGT?";
60//!         let len = cmd.len();
61//!         buf[..len].copy_from_slice(cmd);
62//!         len
63//!     }
64//!
65//!     fn parse(&self, resp: Result<&[u8], InternalError>) -> Result<Self::Response, Error> {
66//!         // Parse resp into `GreetingText`
67//!         Ok(GreetingText {
68//!             text: String::try_from(core::str::from_utf8(resp.unwrap()).unwrap()).unwrap(),
69//!         })
70//!     }
71//! }
72//! ```
73//!
74//! ### Same example with `atat_derive`:
75//! ```
76//! use atat::atat_derive::{AtatCmd, AtatResp};
77//! use heapless::String;
78//!
79//! #[derive(Clone, AtatCmd)]
80//! #[at_cmd("+CSGT", NoResponse)]
81//! pub struct SetGreetingText<'a> {
82//!     #[at_arg(position = 0, len = 32)]
83//!     pub text: &'a str,
84//! }
85//!
86//! #[derive(Clone, AtatCmd)]
87//! #[at_cmd("+CSGT?", GreetingText)]
88//! pub struct GetGreetingText;
89//!
90//! #[derive(Clone, AtatResp)]
91//! pub struct NoResponse;
92//!
93//! #[derive(Clone, AtatResp)]
94//! pub struct GreetingText {
95//!     #[at_arg(position = 0)]
96//!     pub text: String<64>,
97//! };
98//! ```
99//!
100//! ### Basic usage example (More available in examples folder):
101//! ```ignore
102//!
103//! use cortex_m::asm;
104//! use hal::{
105//!     gpio::{
106//!         gpioa::{PA2, PA3},
107//!         Alternate, Floating, Input, AF7,
108//!     },
109//!     pac::{interrupt, Peripherals, USART2},
110//!     prelude::*,
111//!     serial::{Config, Event::Rxne, Rx, Serial},
112//!     timer::{Event, Timer},
113//! };
114//!
115//! use atat::{atat_derive::{AtatResp, AtatCmd}};
116//!
117//! use heapless::{spsc::Queue, String};
118//!
119//! use crate::rt::entry;
120//! static mut INGRESS: Option<atat::IngressManager> = None;
121//! static mut RX: Option<Rx<USART2>> = None;
122//!
123//!
124//! #[derive(Clone, AtatResp)]
125//! pub struct NoResponse;
126//!
127//! #[derive(Clone, AtatCmd)]
128//! #[at_cmd("", NoResponse, timeout_ms = 1000)]
129//! pub struct AT;
130//!
131//! #[entry]
132//! fn main() -> ! {
133//!     let p = Peripherals::take().unwrap();
134//!
135//!     let mut flash = p.FLASH.constrain();
136//!     let mut rcc = p.RCC.constrain();
137//!     let mut pwr = p.PWR.constrain(&mut rcc.apb1r1);
138//!
139//!     let mut gpioa = p.GPIOA.split(&mut rcc.ahb2);
140//!
141//!     let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
142//!
143//!     let tx = gpioa.pa2.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
144//!     let rx = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl);
145//!
146//!     let mut timer = Timer::tim7(p.TIM7, 1.hz(), clocks, &mut rcc.apb1r1);
147//!     let at_timer = Timer::tim6(p.TIM6, 100.hz(), clocks, &mut rcc.apb1r1);
148//!
149//!     let mut serial = Serial::usart2(
150//!         p.USART2,
151//!         (tx, rx),
152//!         Config::default().baudrate(115_200.bps()),
153//!         clocks,
154//!         &mut rcc.apb1r1,
155//!     );
156//!
157//!     serial.listen(Rxne);
158//!
159//!     static mut RES_QUEUE: ResQueue<256> = Queue::new();
160//!     static mut URC_QUEUE: UrcQueue<256, 10> = Queue::new();
161//!     static mut COM_QUEUE: ComQueue = Queue::new();
162//!
163//!     let queues = Queues {
164//!         res_queue: unsafe { RES_QUEUE.split() },
165//!         urc_queue: unsafe { URC_QUEUE.split() },
166//!         com_queue: unsafe { COM_QUEUE.split() },
167//!     };
168//!
169//!     let (tx, rx) = serial.split();
170//!     let (mut client, ingress) =
171//!         ClientBuilder::new(tx, timer, atat::Config::new(atat::Mode::Timeout)).build(queues);
172//!
173//!     unsafe { INGRESS = Some(ingress) };
174//!     unsafe { RX = Some(rx) };
175//!
176//!     // configure NVIC interrupts
177//!     unsafe { cortex_m::peripheral::NVIC::unmask(hal::stm32::Interrupt::TIM7) };
178//!     timer.listen(Event::TimeOut);
179//!
180//!     // if all goes well you should reach this breakpoint
181//!     asm::bkpt();
182//!
183//!     loop {
184//!         asm::wfi();
185//!
186//!         match client.send(&AT) {
187//!             Ok(response) => {
188//!                 // Do something with response here
189//!             }
190//!             Err(e) => {}
191//!         }
192//!     }
193//! }
194//!
195//! #[interrupt]
196//! fn TIM7() {
197//!     let ingress = unsafe { INGRESS.as_mut().unwrap() };
198//!     ingress.digest();
199//! }
200//!
201//! #[interrupt]
202//! fn USART2() {
203//!     let ingress = unsafe { INGRESS.as_mut().unwrap() };
204//!     let rx = unsafe { RX.as_mut().unwrap() };
205//!     if let Ok(d) = nb::block!(rx.read()) {
206//!         ingress.write(&[d]);
207//!     }
208//! }
209//! ```
210//! # Optional Cargo Features
211//!
212//! - **`derive`** *(enabled by default)* - Re-exports [`atat_derive`] to allow
213//!   deriving `Atat__` traits.
214
215// #![deny(warnings)]
216#![allow(clippy::multiple_crate_versions)]
217#![allow(clippy::missing_errors_doc)]
218#![allow(clippy::unused_unit)]
219#![allow(clippy::use_self)]
220#![allow(clippy::too_many_lines)]
221#![allow(clippy::module_name_repetitions)]
222#![allow(clippy::used_underscore_binding)]
223#![allow(clippy::type_complexity)]
224#![allow(clippy::fallible_impl_from)]
225#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
226#![allow(async_fn_in_trait)]
227
228// This mod MUST go first, so that the others see its macros.
229pub(crate) mod fmt;
230
231mod config;
232pub mod digest;
233mod error;
234pub mod helpers;
235mod ingress;
236mod response;
237pub mod response_slot;
238mod traits;
239#[cfg(test)]
240mod tx_mock;
241pub mod urc_channel;
242pub use nom;
243
244pub mod asynch;
245pub mod blocking;
246
247#[cfg(feature = "bytes")]
248pub use serde_bytes;
249
250#[cfg(feature = "bytes")]
251pub use heapless_bytes;
252
253#[cfg(feature = "derive")]
254pub use atat_derive;
255#[cfg(feature = "derive")]
256pub mod derive;
257
258#[cfg(feature = "derive")]
259pub use self::derive::AtatLen;
260
261#[cfg(feature = "derive")]
262pub use serde_at;
263
264#[cfg(feature = "derive")]
265pub use heapless;
266
267pub use config::Config;
268pub use digest::{AtDigester, AtDigester as DefaultDigester, DigestResult, Digester, Parser};
269pub use error::{CmeError, CmsError, ConnectionError, Error, InternalError};
270pub use ingress::{AtatIngress, Error as IngressError, Ingress};
271pub use response::Response;
272pub use response_slot::ResponseSlot;
273pub use traits::{AtatCmd, AtatResp, AtatUrc};
274pub use urc_channel::{UrcChannel, UrcSubscription};
275
276#[cfg(test)]
277#[cfg(feature = "defmt")]
278mod tests {
279    //! This module is required in order to satisfy the requirements of defmt, while running tests.
280    //! Note that this will cause all log `defmt::` log statements to be thrown away.
281
282    use core::ptr::NonNull;
283
284    #[defmt::global_logger]
285    struct Logger;
286    impl defmt::Write for Logger {
287        fn write(&mut self, _bytes: &[u8]) {}
288    }
289
290    unsafe impl defmt::Logger for Logger {
291        fn acquire() -> Option<NonNull<dyn defmt::Write>> {
292            Some(NonNull::from(&Logger as &dyn defmt::Write))
293        }
294
295        unsafe fn release(_: NonNull<dyn defmt::Write>) {}
296    }
297
298    defmt::timestamp!("");
299
300    #[export_name = "_defmt_panic"]
301    fn panic() -> ! {
302        panic!()
303    }
304}