1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
use crate::error::Error; use crate::Mode; use heapless::{ArrayLength, Vec}; pub trait AtatErr {} /// This trait needs to be implemented for every response type. /// /// Example: /// ``` /// use atat::AtatResp; /// /// pub struct GreetingText { /// pub text: heapless::String<heapless::consts::U64>, /// } /// /// impl AtatResp for GreetingText {} /// ``` pub trait AtatResp {} pub trait AtatUrc { /// The type of the response. Usually the enum this trait is implemented on. type Response; /// Parse the response into a `Self::Response` instance. fn parse(resp: &[u8]) -> Result<Self::Response, Error>; } /// This trait needs to be implemented for every command type. /// /// It can also be derived by the [`atat_derive`] crate. /// /// [`atat_derive`]: https://crates.io/crates/atat_derive /// /// Example: /// ``` /// use atat::{AtatCmd, AtatResp, Error}; /// use core::fmt::Write; /// use heapless::Vec; /// /// pub struct SetGreetingText<'a> { /// pub text: &'a str, /// } /// /// pub struct NoResponse; /// /// impl AtatResp for NoResponse {}; /// /// impl<'a> AtatCmd for SetGreetingText<'a> { /// type CommandLen = heapless::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) /// } /// } /// ``` pub trait AtatCmd { /// The max length of the command. /// /// Example: For the command "AT+RST" you would specify /// /// ``` /// type CommandLen = heapless::consts::U6; /// ``` type CommandLen: ArrayLength<u8>; /// The type of the response. Must implement the `AtatResp` trait. type Response: AtatResp; /// Return the command as a heapless `Vec` of bytes. fn as_bytes(&self) -> Vec<u8, Self::CommandLen>; /// Parse the response into a `Self::Response` instance. fn parse(&self, resp: &[u8]) -> Result<Self::Response, Error>; /// Whether or not this command can be aborted. fn can_abort(&self) -> bool { false } /// The max timeout in milliseconds. fn max_timeout_ms(&self) -> u32 { 1000 } /// Force the ingress manager into receive state immediately after sending /// the command. fn force_receive_state(&self) -> bool { false } } pub trait AtatClient { /// Send an AT command. /// /// `cmd` must implement [`AtatCmd`]. /// /// This function will block until a response is received, if in Timeout or /// Blocking mode. In Nonblocking mode, the send can be called until it no /// longer returns `nb::Error::WouldBlock`, or `self.check_response(cmd)` can /// be called, with the same result. /// /// This function will also make sure that atleast `self.config.cmd_cooldown` /// has passed since the last response or URC has been received, to allow /// the slave AT device time to deliver URC's. fn send<A: AtatCmd>(&mut self, cmd: &A) -> nb::Result<A::Response, Error>; /// Checks if there are any URC's (Unsolicited Response Code) in /// queue from the ingress manager. /// /// Example: /// ``` /// use atat::atat_derive::{AtatResp, AtatUrc}; /// /// #[derive(Clone, AtatResp)] /// pub struct MessageWaitingIndication { /// #[at_arg(position = 0)] /// pub status: u8, /// #[at_arg(position = 1)] /// pub code: u8, /// } /// /// #[derive(Clone, AtatUrc)] /// pub enum Urc { /// #[at_urc("+UMWI")] /// MessageWaitingIndication(MessageWaitingIndication), /// } /// /// // match client.check_urc::<Urc>() { /// // Some(Urc::MessageWaitingIndication(MessageWaitingIndication { status, code })) => { /// // // Do something to act on `+UMWI` URC /// // } /// // } /// ``` fn check_urc<URC: AtatUrc>(&mut self) -> Option<URC::Response> { let mut return_urc = None; self.peek_urc_with::<URC, _>(|urc| { return_urc = Some(urc); true }); return_urc } fn peek_urc_with<URC: AtatUrc, F: FnOnce(URC::Response) -> bool>(&mut self, f: F); /// Check if there are any responses enqueued from the ingress manager. /// /// The function will return `nb::Error::WouldBlock` until a response or an /// error is available, or a timeout occurs and `config.mode` is Timeout. /// /// This function is usually only called through [`send`]. /// /// [`send`]: #method.send fn check_response<A: AtatCmd>(&mut self, cmd: &A) -> nb::Result<A::Response, Error>; /// Get the configured mode of the client. /// /// Options are: /// - `NonBlocking` /// - `Blocking` /// - `Timeout` fn get_mode(&self) -> Mode; }