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}