esp_at_driver/
lib.rs

1#![no_std]
2
3// This has some nice examples of expected AT command usage:
4// https://docs.espressif.com/projects/esp-at/en/latest/AT_Command_Examples/TCP-IP_AT_Examples.html
5
6// Ah, the hal's directly implement these traits:
7// https://docs.rs/stm32f30x-hal/0.2.0/stm32f30x_hal/serial/struct.Rx.html
8
9// TODO: Can we avoid using the block! macro?
10use nb::block;
11
12type ReadString = heapless::String<heapless::consts::U512>;
13
14#[derive(Debug)]
15pub struct EspAt<RX, TX>
16where
17    TX: embedded_hal::serial::Write<u8>,
18    RX: embedded_hal::serial::Read<u8>,
19{
20    tx: TX,
21    rx: RX,
22}
23
24impl<RX, TX> EspAt<RX, TX>
25where
26    TX: embedded_hal::serial::Write<u8>,
27    RX: embedded_hal::serial::Read<u8>,
28{
29    pub fn new(tx: TX, rx: RX) -> Self {
30        Self { tx, rx }
31    }
32
33    pub fn set_wifi_mode(&mut self, mode: WifiMode) -> Result<(), GenericError<TX::Error, RX::Error>> {
34        match mode {
35            WifiMode::Disabled         => self.write_line(b"AT+CWMODE=0,1")?,
36            WifiMode::Station          => self.write_line(b"AT+CWMODE=1,1")?,
37            WifiMode::SoftAP           => self.write_line(b"AT+CWMODE=2,1")?,
38            WifiMode::StationAndSoftAP => self.write_line(b"AT+CWMODE=3,1")?,
39        }
40        let reply = self.read_line()?;
41        if reply == "OK" {
42            Ok(())
43        } else {
44            Err(GenericError::ATError(reply))
45        }
46    }
47
48    fn read_line(&mut self) -> Result<ReadString, GenericError<TX::Error, RX::Error>> {
49        let mut line = ReadString::new();
50        let mut prev_value = 'a';
51        loop {
52            let value = match block!(self.rx.read()) {
53                Ok(word) => word as char,
54                Err(e) => { return Err(GenericError::ReadError(e)); }
55            };
56
57            if value != '\r' && value != '\n' {
58                if let Err(()) = line.push(value) {
59                    return Err(GenericError::ATResponseTooLong(line));
60                }
61            }
62            else if prev_value == '\r' && value == '\n' {
63                return Ok(line);
64            }
65            prev_value = value;
66        }
67    }
68
69    fn write_line(&mut self, data: &[u8]) -> Result<(), GenericError<TX::Error, RX::Error>> {
70        for x in data {
71            self.write_byte(*x)?;
72        }
73        self.write_byte(b'\r')?;
74        self.write_byte(b'\n')?;
75
76        Ok(())
77    }
78
79    fn write_byte(&mut self, data: u8) -> Result<(), GenericError<TX::Error, RX::Error>> {
80        match block!(self.tx.write(data)) {
81            Ok(()) => Ok(()),
82            Err(e) => Err(GenericError::WriteError(e)),
83        }
84    }
85}
86
87/// `RXE` and `TXE` will be the `Error` type(s) of your serial port
88/// implementation, as defined by `embedded_hal::serial::Read<u8>::Error`
89/// and `embedded_hal::serial::Write<u8>::Error` respectively.
90#[derive(Debug)] // TODO: This is bad right??
91pub enum GenericError<TXE, RXE> {
92    WriteError(TXE),
93    ReadError(RXE),
94    ATError(ReadString),
95    ATResponseTooLong(ReadString),
96}
97
98pub enum WifiMode {
99    /// Completely disable wifi RF activity.
100    Disabled,
101    /// Act as a regular wifi client https://en.wikipedia.org/wiki/Station_(networking)
102    Station,
103    /// Act as a wifi Access Point https://en.wikipedia.org/wiki/Wireless_access_point
104    SoftAP,
105    /// Act as both a regular wifi client and a wifi Access Point
106    StationAndSoftAP,
107}