1use 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
43pub 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 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 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 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 pub fn iwconfig(&mut self) -> Result<(&str, &str, u8), EspAtError<ER, EW>> {
241 self.write_str("AT+CWJAP?")?;
242 self.write_crlf()?;
243
244 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 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 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 let _ = self.read();
288 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 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 pub fn skip_read(&mut self) -> Result<(), EspAtError<ER, EW>> {
332 loop {
333 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 pub fn read(&mut self) -> Result<&[u8], EspAtError<ER, EW>> {
351 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 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 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 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}