embedded_drivers/
esp_at.rs

1//! Driver for esp-at.
2//!
3//! Ref: https://docs.espressif.com/projects/esp-at/en/latest/AT_Command_Set/index.html
4
5use crate::ByteMutWriter;
6use core::arch::asm;
7use core::fmt::Write;
8use core::str;
9use embedded_hal::serial;
10use nb::block;
11
12const CR: u8 = 0x0d;
13const LF: u8 = 0x0a;
14
15#[derive(Debug)]
16pub enum EspAtError<ER, EW> {
17    Busy,
18    BufOverflow,
19    NoConnection,
20    Error,
21    DirtyData,
22    Read,
23    SerialRead(ER),
24    SerialWrite(EW),
25}
26
27impl<ER, EW> EspAtError<ER, EW> {
28    fn from_read(err: ER) -> Self {
29        EspAtError::SerialRead(err)
30    }
31
32    fn from_write(err: EW) -> Self {
33        EspAtError::SerialWrite(err)
34    }
35}
36
37impl<ER, EW> From<core::convert::Infallible> for EspAtError<ER, EW> {
38    fn from(_err: core::convert::Infallible) -> Self {
39        unreachable!()
40    }
41}
42
43/// esp-at driver for ESP32, ESP8266, ESP8285.
44pub struct EspAt<S> {
45    serial: S,
46    cursor: usize,
47    read_buf: [u8; 1024],
48}
49
50impl<S, ER, EW> EspAt<S>
51where
52    S: serial::Read<u8, Error = ER> + serial::Write<u8, Error = EW>,
53{
54    pub fn new(serial: S) -> Self {
55        Self {
56            serial,
57            cursor: 0,
58            read_buf: [0u8; 1024],
59        }
60    }
61
62    fn write_byte(&mut self, byte: u8) -> Result<(), EspAtError<ER, EW>> {
63        block!(self.serial.write(byte)).map_err(EspAtError::from_write)
64    }
65
66    fn write_crlf(&mut self) -> Result<(), EspAtError<ER, EW>> {
67        self.write_byte(CR)?;
68        self.write_byte(LF)
69    }
70
71    fn write_all(&mut self, buf: &[u8]) -> Result<(), EspAtError<ER, EW>> {
72        for byte in buf {
73            self.write_byte(*byte)?;
74        }
75        Ok(())
76    }
77
78    fn write_str(&mut self, buf: &str) -> Result<(), EspAtError<ER, EW>> {
79        for byte in buf.as_bytes() {
80            self.write_byte(*byte)?;
81        }
82        Ok(())
83    }
84
85    fn write_quoted_str(&mut self, buf: &str) -> Result<(), EspAtError<ER, EW>> {
86        self.write_byte(b'"')?;
87        self.write_str(buf)?;
88        self.write_byte(b'"')
89    }
90
91    pub fn send_command(&mut self, cmd: &str) -> Result<(), EspAtError<ER, EW>> {
92        self.write_all(cmd.as_bytes())?;
93        self.write_crlf()?;
94        Ok(())
95    }
96
97    fn read_byte(&mut self) -> Result<u8, EspAtError<ER, EW>> {
98        block!(self.serial.read()).map_err(EspAtError::from_read)
99    }
100
101    pub fn last_read(&self) -> &str {
102        unsafe { str::from_utf8_unchecked(&self.read_buf[0..self.cursor]) }
103    }
104
105    pub fn read_response(&mut self) -> Result<&str, EspAtError<ER, EW>> {
106        let buflen = self.read_buf.len();
107        self.cursor = 0;
108        let mut i = 0;
109        loop {
110            match self.read_byte()? {
111                LF if i >= 1 && self.read_buf[i - 1] == CR => {
112                    if i >= 3 && &self.read_buf[i - 3..i - 1] == b"OK" {
113                        let resp = unsafe { str::from_utf8_unchecked(&self.read_buf[0..(i - 3)]) };
114                        self.cursor = i - 3;
115                        return Ok(resp.trim_end());
116                    }
117                    if i >= 6 && &self.read_buf[i - 6..i - 1] == b"ERROR" {
118                        self.cursor = i - 6;
119                        return Err(EspAtError::Error);
120                    }
121                    if i >= 10 && &self.read_buf[i - 10..i - 1] == b"busy p..." {
122                        return Err(EspAtError::Busy);
123                    }
124                    self.read_buf[i] = LF;
125                }
126                CR => {
127                    self.read_buf[i] = CR;
128                }
129                other => {
130                    self.read_buf[i] = other;
131                }
132            }
133            i += 1;
134            if i >= buflen {
135                self.skip_to_next();
136                return Err(EspAtError::BufOverflow);
137            }
138        }
139    }
140
141    fn read_until(&mut self, c: u8) -> Result<usize, EspAtError<ER, EW>> {
142        let buflen = self.read_buf.len();
143        let mut i = 0;
144        loop {
145            let b = self.read_byte()?;
146            self.read_buf[i] = b;
147            if b == c {
148                return Ok(i);
149            }
150            i += 1;
151            if i >= buflen {
152                self.skip_to_next();
153                return Err(EspAtError::BufOverflow);
154            }
155        }
156    }
157
158    fn read_nbytes(&mut self, n: usize) -> Result<&[u8], EspAtError<ER, EW>> {
159        if n > self.read_buf.len() {
160            for _ in 0..n {
161                self.read_byte()?;
162            }
163            return Err(EspAtError::BufOverflow);
164        }
165
166        for i in 0..n {
167            self.read_buf[i] = self.read_byte()?;
168        }
169        Ok(&self.read_buf[0..n])
170    }
171
172    pub fn skip_to_next(&mut self) {
173        loop {
174            match self.serial.read() {
175                Err(nb::Error::WouldBlock) => {
176                    return;
177                }
178                Err(_) => {
179                    continue;
180                }
181                Ok(_) => {
182                    continue;
183                }
184            }
185        }
186    }
187
188    /// ATE0
189    pub fn echo_off(&mut self) -> Result<(), EspAtError<ER, EW>> {
190        self.write_str("ATE0")?;
191        self.write_crlf()?;
192
193        self.read_response().map(|_| ())
194    }
195
196    /// ATE1
197    pub fn echo_on(&mut self) -> Result<(), EspAtError<ER, EW>> {
198        self.write_str("ATE1")?;
199        self.write_crlf()?;
200
201        self.read_response().map(|_| ())
202    }
203
204    // enter station mode
205    /*
206    drv.send_command("AT+CWMODE=1").unwrap();
207    let raw = drv.read_response();
208    writeln!(buf, "AT+CWMODE=>\n{:?}", raw.unwrap());
209    */
210
211    // AT+CIPSTA?
212    // AT+CIPSTA=<ip>[,<gateway>,<netmask>]
213    /*
214    +CIPSTA:ip:"192.168.1.9"
215    +CIPSTA:gateway:"192.168.1.1"
216    +CIPSTA:netmask:"255.255.0.0"
217    */
218    /// (ip, gateway, netmask)
219    pub fn ifconfig(&mut self) -> Result<(&str, &str, &str), EspAtError<ER, EW>> {
220        self.write_str("AT+CIPSTA?")?;
221        self.write_crlf()?;
222
223        self.read_response().map(|payload| {
224            let mut it = payload.lines();
225            let ip = it.next().unwrap();
226            let gateway = it.next().unwrap();
227            let netmask = it.next().unwrap();
228
229            (
230                &ip[12..ip.len() - 1],
231                &gateway[17..gateway.len() - 1],
232                &netmask[17..netmask.len() - 1],
233            )
234        })
235    }
236
237    // AT+CWJAP?
238    // AT+CWJAP=<ssid>,<pwd>[,<bssid>][,<pci_en>][,<reconn_interval>][,<listen_interval>][,<scan_mode>]
239    /// <ssid>,<bssid>,<channel>,<rssi>,<pci_en>,<reconn_interval>,<listen_interval>,<scan_mode>
240    pub fn iwconfig(&mut self) -> Result<(&str, &str, u8), EspAtError<ER, EW>> {
241        self.write_str("AT+CWJAP?")?;
242        self.write_crlf()?;
243
244        // +CWJAP:"...","04:d9:f5:c4:93:98",11,-68,0,0,0,0
245        self.read_response().and_then(|payload| {
246            if payload == "No AP" {
247                return Err(EspAtError::NoConnection);
248            }
249            let mut it = payload[7..].split(',');
250            let ssid = it.next().unwrap().trim_matches('"');
251            let bssid = it.next().unwrap().trim_matches('"');
252            let channel = it.next().unwrap().parse().unwrap_or(255);
253            Ok((ssid, bssid, channel))
254        })
255    }
256
257    // AT+CWJAP="apname","password"
258    pub fn cwjap(&mut self, ssid: &str, password: &str) -> Result<(), EspAtError<ER, EW>> {
259        self.write_all(b"AT+CWJAP=")?;
260        self.write_quoted_str(ssid)?;
261        self.write_byte(b',')?;
262        self.write_quoted_str(password)?;
263        self.write_crlf()?;
264
265        self.read_response().map(|_| ())
266    }
267
268    /// AT+PING
269    pub fn ping(&mut self, host: &str) -> Result<u32, EspAtError<ER, EW>> {
270        self.write_all(b"AT+PING=")?;
271        self.write_quoted_str(host)?;
272        self.write_crlf()?;
273
274        self.read_response().map(|payload| {
275            if payload.starts_with("+PING:") {
276                payload[6..].parse().unwrap_or(255)
277            } else {
278                payload[1..].parse().unwrap_or(255)
279            }
280        })
281    }
282
283    pub fn tcp_send(&mut self, host: &str, port: u16, buf: &[u8]) -> Result<(), EspAtError<ER, EW>> {
284        self.connect_tcp(host, port).map_err(|_| EspAtError::NoConnection)?;
285        self.send(buf)?;
286        // read 1 packet
287        let _ = self.read();
288        // read all remain packets
289        self.skip_read()?;
290        self.close()
291    }
292
293    pub fn connect_tcp(&mut self, host: &str, port: u16) -> Result<(), EspAtError<ER, EW>> {
294        self.write_all(b"AT+CIPSTART=\"TCP\",")?;
295        self.write_quoted_str(host)?;
296        self.write_byte(b',')?;
297        {
298            let mut data = [0u8; 6];
299            let mut buf = ByteMutWriter::new(&mut data[..]);
300            let _ = write!(buf, "{}", port);
301            self.write_str(buf.as_str())?;
302        }
303        self.write_crlf()?;
304
305        self.read_response().map(|_| ()).or_else(|err| {
306            if self.last_read().contains("ALREADY CONNECTED") {
307                Ok(())
308            } else {
309                Err(err)
310            }
311        })
312    }
313
314    /// AT+CIPSEND=x
315    pub fn send(&mut self, data: &[u8]) -> Result<(), EspAtError<ER, EW>> {
316        {
317            let mut buf = [0u8; 24];
318            let mut buf = ByteMutWriter::new(&mut buf[..]);
319            let _ = write!(buf, "AT+CIPSEND={}", data.len());
320            self.write_str(buf.as_str())?;
321        }
322        self.write_crlf()?;
323        self.read_until(b'>')?;
324        self.write_all(data)?;
325
326        self.read_response().map(|_| ())
327    }
328
329    /// Skip all possible data.
330    /// Skip and send "AT", if it returns OK, then all read data is received.
331    pub fn skip_read(&mut self) -> Result<(), EspAtError<ER, EW>> {
332        loop {
333            // TODO: use delay
334            for _ in 0..5_000_000 {
335                unsafe {
336                    asm!("nop");
337                }
338            }
339            self.skip_to_next();
340
341            let _ = self.send_command("AT");
342            if let Ok(_) = self.read_response() {
343                break;
344            }
345        }
346        Ok(())
347    }
348
349    /// Read a packet
350    pub fn read(&mut self) -> Result<&[u8], EspAtError<ER, EW>> {
351        // +IPD,103: ...
352        self.read_until(b'+')?;
353        let cursor = self.read_until(b':')?;
354        let nbytes = unsafe {
355            str::from_utf8_unchecked(&self.read_buf[4..cursor])
356                .parse()
357                .map_err(|_| EspAtError::DirtyData)?
358        };
359
360        self.read_nbytes(nbytes)
361    }
362
363    /// AT+CIPCLOSE
364    pub fn close(&mut self) -> Result<(), EspAtError<ER, EW>> {
365        self.skip_to_next();
366
367        self.write_all(b"AT+CIPCLOSE")?;
368        self.write_crlf()?;
369
370        self.read_response().map(|_| ())
371    }
372
373    /// AT+HTTPCLIENT
374    pub fn http_get(&mut self, url: &str) -> Result<&str, EspAtError<ER, EW>> {
375        self.write_str("AT+HTTPCLIENT=2,0,")?;
376        self.write_quoted_str(url)?;
377        self.write_str(",,,")?;
378        if url.starts_with("https") {
379            self.write_byte(b'2')?;
380        } else {
381            self.write_byte(b'1')?;
382        }
383        self.write_crlf()?;
384
385        // FIXME: trimed data
386        self.read_response().map(|payload| {
387            payload
388                .bytes()
389                .position(|c| c == b',')
390                .map(|pos| &payload[pos + 1..])
391                .unwrap_or("")
392        })
393    }
394
395    pub fn http_post(&mut self, url: &str, data: &str) -> Result<&str, EspAtError<ER, EW>> {
396        self.write_str("AT+HTTPCLIENT=3,0,")?;
397        self.write_quoted_str(url)?;
398        self.write_str(",,,")?;
399        if url.starts_with("https") {
400            self.write_str("2,")?;
401        } else {
402            self.write_str("1,")?;
403        }
404        self.write_quoted_str(data)?;
405        self.write_crlf()?;
406
407        self.read_response().map(|payload| {
408            payload
409                .bytes()
410                .position(|c| c == b',')
411                .map(|pos| &payload[pos + 1..])
412                .unwrap_or("")
413        })
414    }
415}