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 ;
use Write;
use ;
;
;
;
;
;
Same example with atat_derive:
use ;
use ;
;
;
;
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-exports [atat_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.