[−][src]Crate atat
A helper crate to abstract away the state management and string parsing of AT command communication.
It works by creating structs for each AT command, that each implements
AtatCmd
. With corresponding response structs that each implements
AtatResp
.
This can be simplified alot using the atat_derive
crate!
AtatCmd
: trait.AtatCmd.html AtatResp
: trait.AtatResp.html
atat_derive
: https://crates.io/crates/atat_derive
Examples
Command and response example without atat_derive
:
use atat::{AtatCmd, AtatResp, Error}; use core::fmt::Write; use heapless::{consts, String, Vec}; pub struct SetGreetingText<'a> { pub text: &'a str, } pub struct GetGreetingText; pub struct NoResponse; impl AtatResp for NoResponse {}; pub struct GreetingText { pub text: String<consts::U64>, }; impl AtatResp for GreetingText {}; impl<'a> AtatCmd for SetGreetingText<'a> { type CommandLen = consts::U64; type Response = NoResponse; fn as_bytes(&self) -> Vec<u8, Self::CommandLen> { let mut buf: Vec<u8, Self::CommandLen> = Vec::new(); write!(buf, "AT+CSGT={}", self.text); buf } fn parse(&self, resp: &[u8]) -> Result<Self::Response, Error> { Ok(NoResponse) } } impl AtatCmd for GetGreetingText { type CommandLen = consts::U8; type Response = GreetingText; fn as_bytes(&self) -> Vec<u8, Self::CommandLen> { Vec::from_slice(b"AT+CSGT?").unwrap() } fn parse(&self, resp: &[u8]) -> Result<Self::Response, Error> { // Parse resp into `GreetingText` Ok(GreetingText { text: String::from(core::str::from_utf8(resp).unwrap()), }) } }
Same example with atat_derive
:
use atat::atat_derive::{AtatCmd, AtatResp}; use heapless::{consts, String}; #[derive(Clone, AtatCmd)] #[at_cmd("+CSGT", NoResponse)] pub struct SetGreetingText<'a> { #[at_arg(position = 0, len = 32)] pub text: &'a str, } #[derive(Clone, AtatCmd)] #[at_cmd("+CSGT?", GreetingText)] pub struct GetGreetingText; #[derive(Clone, AtatResp)] pub struct NoResponse; #[derive(Clone, AtatResp)] pub struct GreetingText { #[at_arg(position = 0)] pub text: String<consts::U64>, };
Basic usage example (More available in examples folder):
use cortex_m::asm; use hal::{ gpio::{ gpioa::{PA2, PA3}, Alternate, Floating, Input, AF7, }, pac::{interrupt, Peripherals, USART2}, prelude::*, serial::{Config, Event::Rxne, Rx, Serial}, timer::{Event, Timer}, }; use atat::{atat_derive::{AtatResp, AtatCmd}}; use heapless::{consts, spsc::Queue, String}; use crate::rt::entry; static mut INGRESS: Option<atat::IngressManager> = None; static mut RX: Option<Rx<USART2>> = None; #[derive(Clone, AtatResp)] pub struct NoResponse; #[derive(Clone, AtatCmd)] #[at_cmd("", NoResponse, timeout_ms = 1000)] pub struct AT; #[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); let mut flash = p.FLASH.constrain(); let mut rcc = p.RCC.constrain(); let mut pwr = p.PWR.constrain(&mut rcc.apb1r1); let mut gpioa = p.GPIOA.split(&mut rcc.ahb2); let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr); let tx = gpioa.pa2.into_af7(&mut gpioa.moder, &mut gpioa.afrl); let rx = gpioa.pa3.into_af7(&mut gpioa.moder, &mut gpioa.afrl); let mut timer = Timer::tim7(p.TIM7, 1.hz(), clocks, &mut rcc.apb1r1); let at_timer = Timer::tim6(p.TIM6, 100.hz(), clocks, &mut rcc.apb1r1); let mut serial = Serial::usart2( p.USART2, (tx, rx), Config::default().baudrate(115_200.bps()), clocks, &mut rcc.apb1r1, ); serial.listen(Rxne); static mut RES_QUEUE: ResQueue<consts::U256, consts::U5> = Queue(heapless::i::Queue::u8()); static mut URC_QUEUE: UrcQueue<consts::U256, consts::U10> = Queue(heapless::i::Queue::u8()); static mut COM_QUEUE: ComQueue<consts::U3> = Queue(heapless::i::Queue::u8()); let queues = Queues { res_queue: unsafe { RES_QUEUE.split() }, urc_queue: unsafe { URC_QUEUE.split() }, com_queue: unsafe { COM_QUEUE.split() }, }; let (tx, rx) = serial.split(); let (mut client, ingress) = ClientBuilder::new(tx, timer, atat::Config::new(atat::Mode::Timeout)).build(queues); unsafe { INGRESS = Some(ingress) }; unsafe { RX = Some(rx) }; // configure NVIC interrupts unsafe { cortex_m::peripheral::NVIC::unmask(hal::stm32::Interrupt::TIM7) }; timer.listen(Event::TimeOut); // if all goes well you should reach this breakpoint asm::bkpt(); loop { asm::wfi(); match client.send(&AT) { Ok(response) => { // Do something with response here } Err(e) => {} } } } #[interrupt] fn TIM7() { let ingress = unsafe { INGRESS.as_mut().unwrap() }; ingress.parse_at(); } #[interrupt] fn USART2() { let ingress = unsafe { INGRESS.as_mut().unwrap() }; let rx = unsafe { RX.as_mut().unwrap() }; if let Ok(d) = nb::block!(rx.read()) { ingress.write(&[d]); } }
Optional Cargo Features
derive
(enabled by default) - Re-exportsatat_derive
to allow derivingAtat__
traits.log-logging
(disabled by default) - Enable log statements on various log levels to aid debugging. Powered bylog
.defmt-default
(disabled by default) - Enable log statements at INFO, or TRACE, level and up, to aid debugging. Powered bydefmt
.defmt-trace
(disabled by default) - Enable log statements at TRACE level and up, to aid debugging. Powered bydefmt
.defmt-debug
(disabled by default) - Enable log statements at DEBUG level and up, to aid debugging. Powered bydefmt
.defmt-info
(disabled by default) - Enable log statements at INFO level and up, to aid debugging. Powered bydefmt
.defmt-warn
(disabled by default) - Enable log statements at WARN level and up, to aid debugging. Powered bydefmt
.defmt-error
(disabled by default) - Enable log statements at ERROR level and up, to aid debugging. Powered bydefmt
.
Re-exports
pub use atat_derive; |
pub use self::derive::AtatLen; |
pub use serde_at; |
pub use typenum; |
pub use heapless; |
Modules
derive | |
prelude | The prelude is a collection of all the traits in this crate |
Macros
atat_log |
Structs
Client | Client responsible for handling send, receive and timeout from the
userfacing side. The client is decoupled from the ingress-manager through
some spsc queue consumers, where any received responses can be dequeued. The
Client also has an spsc producer, to allow signaling commands like
|
ClientBuilder | Builder to set up a |
Config | Configuration of both the ingress manager, and the AT client. Some of these
parameters can be changed on the fly, through issuing a |
IngressManager | |
NoopUrcMatcher | A URC matcher that does nothing (it always returns |
Queues |
Enums
Command | Commands that can be sent from the client to the ingress manager, for configuration after initial setup. This is also used for stuff like clearing the receive buffer on command timeouts. |
Error | Errors returned by, or used within the crate |
Mode | Whether the AT client should block while waiting responses or return early. |
UrcMatcherResult | The type returned from a custom URC matcher. |
Traits
AtatClient | |
AtatCmd | This trait needs to be implemented for every command type. |
AtatResp | This trait needs to be implemented for every response type. |
AtatUrc | |
UrcMatcher | A user-defined URC matcher |
Functions
get_line | Helper function to take a subsection from |
Type Definitions
ComQueue | |
ResQueue | |
UrcQueue |