esp8266_wifi_serial/
module.rs

1use core::fmt::Write;
2
3use embedded_hal::serial;
4use simple_clock::{Deadline, ElapsedTimer, SimpleClock};
5
6use crate::{
7    error::{Error, Result},
8    parser::CifsrResponse,
9    reader_part::{ReadData, ReaderPart},
10};
11
12const RESET_DELAY_US: u64 = 3_000_000;
13
14/// Raw response to a sent AT command.
15pub type RawResponse<'a, const N: usize> = core::result::Result<ReadData<'a, N>, ReadData<'a, N>>;
16
17/// The trait describes how to send a certain AT command.
18pub trait AtCommand: private::Sealed {
19    /// Sends the AT command and gets a corresponding response.
20    #[doc(hidden)]
21    fn send<Rx, Tx, C, const N: usize>(
22        self,
23        module: &mut Module<Rx, Tx, C, N>,
24    ) -> Result<RawResponse<'_, N>>
25    where
26        Rx: serial::Read<u8> + 'static,
27        Tx: serial::Write<u8> + 'static,
28        C: SimpleClock;
29}
30
31impl AtCommand for &str {
32    fn send<Rx, Tx, C, const N: usize>(
33        self,
34        module: &mut Module<Rx, Tx, C, N>,
35    ) -> Result<RawResponse<'_, N>>
36    where
37        Rx: serial::Read<u8> + 'static,
38        Tx: serial::Write<u8> + 'static,
39        C: SimpleClock,
40    {
41        module.send_at_command_str(self)
42    }
43}
44
45impl AtCommand for core::fmt::Arguments<'_> {
46    fn send<Rx, Tx, C, const N: usize>(
47        self,
48        module: &mut Module<Rx, Tx, C, N>,
49    ) -> Result<RawResponse<'_, N>>
50    where
51        Rx: serial::Read<u8> + 'static,
52        Tx: serial::Write<u8> + 'static,
53        C: SimpleClock,
54    {
55        module.send_at_command_fmt(self)
56    }
57}
58
59const NEWLINE: &[u8] = b"\r\n";
60
61/// Basic communication interface with the esp8266 module.
62///
63/// Provides basic functionality for sending AT commands and getting corresponding responses.
64#[derive(Debug)]
65pub struct Module<Rx, Tx, C, const N: usize>
66where
67    Rx: serial::Read<u8> + 'static,
68    Tx: serial::Write<u8> + 'static,
69    C: SimpleClock,
70{
71    pub(crate) reader: ReaderPart<Rx, N>,
72    pub(crate) writer: WriterPart<Tx>,
73    pub(crate) clock: C,
74    pub(crate) timeout: Option<u64>,
75}
76
77impl<'a, Rx, Tx, C, const N: usize> Module<Rx, Tx, C, N>
78where
79    Rx: serial::Read<u8> + 'static,
80    Tx: serial::Write<u8> + 'static,
81    C: SimpleClock,
82{
83    /// Establishes serial communication with the esp8266 module.
84    pub fn new(rx: Rx, tx: Tx, clock: C) -> Result<Self> {
85        let mut module = Self {
86            reader: ReaderPart::new(rx),
87            writer: WriterPart { tx },
88            clock,
89            timeout: None,
90        };
91        module.init()?;
92        Ok(module)
93    }
94
95    fn init(&mut self) -> Result<()> {
96        self.disable_echo()?;
97        Ok(())
98    }
99
100    fn reset_cmd(&mut self) -> Result<()> {
101        self.write_command(b"AT+RST")?;
102
103        // Workaround to ignore the framing errors.
104        let timer = ElapsedTimer::new(&self.clock);
105        while timer.elapsed() < RESET_DELAY_US {
106            core::hint::spin_loop();
107        }
108
109        self.read_until(ReadyCondition)?;
110
111        Ok(())
112    }
113
114    /// Sets the operation timeout to the timeout specified.
115    ///
116    /// If the specified value is `None`, the operations will block infinitely.
117    pub fn set_timeout(&mut self, us: Option<u64>) {
118        self.timeout = us;
119    }
120
121    /// Performs the module resetting routine.
122    pub fn reset(&mut self) -> Result<()> {
123        // FIXME: It is ok to receive errors like "framing" during the reset procedure.
124        self.reset_cmd().ok();
125        // Workaround to catch the framing errors.
126        for _ in 0..100 {
127            self.send_at_command_str("ATE1").ok();
128        }
129
130        self.disable_echo()?;
131        Ok(())
132    }
133
134    /// Sends an AT command and gets the response for it.
135    pub fn send_at_command<T: AtCommand>(&mut self, cmd: T) -> Result<RawResponse<'_, N>> {
136        cmd.send(self)
137    }
138
139    fn send_at_command_str(&mut self, cmd: &str) -> Result<RawResponse<'_, N>> {
140        self.write_command(cmd.as_ref())?;
141        self.read_until(OkCondition)
142    }
143
144    fn send_at_command_fmt(&mut self, args: core::fmt::Arguments) -> Result<RawResponse<'_, N>> {
145        self.write_command_fmt(args)?;
146        self.read_until(OkCondition)
147    }
148
149    fn disable_echo(&mut self) -> Result<()> {
150        self.send_at_command_str("ATE0").map(drop)
151    }
152
153    fn write_command(&mut self, cmd: &[u8]) -> Result<()> {
154        self.writer.write_bytes(cmd)?;
155        self.writer.write_bytes(NEWLINE)
156    }
157
158    pub(crate) fn write_command_fmt(&mut self, args: core::fmt::Arguments) -> Result<()> {
159        self.writer.write_fmt(args)?;
160        self.writer.write_bytes(NEWLINE)
161    }
162
163    pub(crate) fn read_until<'b, T>(&'b mut self, condition: T) -> Result<T::Output>
164    where
165        T: Condition<'b, N>,
166    {
167        let clock = &self.clock;
168        let deadline = self.timeout.map(|timeout| Deadline::new(clock, timeout));
169
170        loop {
171            match self.reader.read_bytes() {
172                Ok(_) => {
173                    if self.reader.buf().is_full() {
174                        return Err(Error::BufferFull);
175                    }
176                }
177                Err(nb::Error::WouldBlock) => {}
178                Err(nb::Error::Other(_)) => {
179                    return Err(Error::ReadBuffer);
180                }
181            };
182
183            if condition.is_performed(&self.reader.buf()) {
184                break;
185            }
186
187            if let Some(deadline) = deadline.as_ref() {
188                deadline.reached().map_err(|_| Error::Timeout)?;
189            }
190        }
191
192        let read_data = ReadData::new(self.reader.buf_mut());
193        Ok(condition.output(read_data))
194    }
195
196    pub(crate) fn get_network_info(&mut self) -> Result<CifsrResponse> {
197        // Get assigned SoftAP address.
198        let res = self.send_at_command("AT+CIFSR")?;
199        let raw_resp = res.expect("Malformed command");
200
201        let resp = CifsrResponse::parse(&raw_resp)
202            .unwrap_or_else(|| panic!("Unable to parse response: {:?}", raw_resp))
203            .1;
204        Ok(resp)
205    }
206}
207
208pub(crate) trait Condition<'a, const N: usize>: Copy {
209    type Output: 'a;
210
211    fn is_performed(self, buf: &[u8]) -> bool;
212
213    fn output(self, buf: ReadData<'a, N>) -> Self::Output;
214}
215
216#[derive(Clone, Copy)]
217struct ReadyCondition;
218
219impl ReadyCondition {
220    const MSG: &'static [u8] = b"ready\r\n";
221}
222
223impl<'a, const N: usize> Condition<'a, N> for ReadyCondition {
224    type Output = ReadData<'a, N>;
225
226    fn is_performed(self, buf: &[u8]) -> bool {
227        buf.ends_with(Self::MSG)
228    }
229
230    fn output(self, mut buf: ReadData<'a, N>) -> Self::Output {
231        buf.subslice(0, buf.len() - Self::MSG.len());
232        buf
233    }
234}
235
236#[derive(Clone, Copy)]
237pub(crate) struct CarretCondition;
238
239impl CarretCondition {
240    const MSG: &'static [u8] = b"> ";
241}
242
243impl<'a, const N: usize> Condition<'a, N> for CarretCondition {
244    type Output = ReadData<'a, N>;
245
246    fn is_performed(self, buf: &[u8]) -> bool {
247        buf.ends_with(Self::MSG)
248    }
249
250    fn output(self, mut buf: ReadData<'a, N>) -> Self::Output {
251        buf.subslice(0, buf.len() - Self::MSG.len());
252        buf
253    }
254}
255
256#[derive(Clone, Copy)]
257pub(crate) struct OkCondition;
258
259impl OkCondition {
260    const OK: &'static [u8] = b"OK\r\n";
261    const ERROR: &'static [u8] = b"ERROR\r\n";
262    const FAIL: &'static [u8] = b"FAIL\r\n";
263}
264
265// TODO optimize this condition.
266impl<'a, const N: usize> Condition<'a, N> for OkCondition {
267    type Output = RawResponse<'a, N>;
268
269    fn is_performed(self, buf: &[u8]) -> bool {
270        buf.ends_with(Self::OK) || buf.ends_with(Self::ERROR) || buf.ends_with(Self::FAIL)
271    }
272
273    fn output(self, mut buf: ReadData<'a, N>) -> Self::Output {
274        if buf.ends_with(Self::OK) {
275            buf.subslice(0, buf.len() - Self::OK.len());
276            Ok(buf)
277        } else if buf.ends_with(Self::ERROR) {
278            buf.subslice(0, buf.len() - Self::ERROR.len());
279            Err(buf)
280        } else {
281            buf.subslice(0, buf.len() - Self::FAIL.len());
282            Err(buf)
283        }
284    }
285}
286
287#[derive(Debug)]
288pub struct WriterPart<Tx> {
289    tx: Tx,
290}
291
292impl<Tx> WriterPart<Tx>
293where
294    Tx: serial::Write<u8> + 'static,
295{
296    fn write_fmt(&mut self, args: core::fmt::Arguments) -> Result<()> {
297        let writer = &mut self.tx as &mut (dyn serial::Write<u8, Error = Tx::Error> + 'static);
298        writer.write_fmt(args).map_err(|_| Error::WriteBuffer)
299    }
300
301    pub(crate) fn write_byte(&mut self, byte: u8) -> nb::Result<(), Error> {
302        self.tx
303            .write(byte)
304            .map_err(|err| err.map(|_| Error::WriteBuffer))
305    }
306
307    fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
308        for byte in bytes.iter() {
309            nb::block!(self.write_byte(*byte))?;
310        }
311        Ok(())
312    }
313}
314
315mod private {
316    pub trait Sealed {}
317
318    impl Sealed for &str {}
319    impl Sealed for core::fmt::Arguments<'_> {}
320}