use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{borrow::ToOwned, format}; use core::fmt::Write; use core::marker::PhantomData; use esp_hal::{Blocking, delay::Delay, gpio::Output, uart::Uart}; use crate::async_io::ConstructedATCommand; static RESPONSES: [&'static str; 3] = ["OK", "ERROR", "DOWNLOAD"]; pub struct ATCommands<'a, 'd> { rst: Output<'a>, pwr_key: Output<'a>, uart: Uart<'d, Blocking>, delay: Delay, buffer: String, lines: Vec, } impl<'a, 'd> ATCommands<'a, 'd> { pub fn new( rst: Output<'a>, pwr_key: Output<'a>, uart: Uart<'d, Blocking>, ) -> ATCommands<'a, 'd> { ATCommands { rst, pwr_key, uart, delay: Delay::new(), buffer: String::new(), lines: Vec::new(), } } pub fn init(&mut self) { self.pwr_key.set_low(); self.delay.delay_millis(1_000); self.pwr_key.set_high(); self.delay.delay_millis(1_000); self.rst.set_low(); self.delay.delay_millis(1_000); self.rst.set_high(); self.delay.delay_millis(1_000); self.pwr_key.set_low(); self.delay.delay_millis(1_000); self.pwr_key.set_high(); self.delay.delay_millis(1_000); log::info!("ATCommands Ready!"); self.delay.delay_millis(1_000); self.flush_rx(); } pub fn raw_command(&mut self, command: String) -> String { self.uart.flush().unwrap(); self.flush_rx(); let command = command.clone() + "\r"; self.uart.write_str(&command).unwrap(); self.uart.flush().unwrap(); self.delay.delay_millis(500); log::info!("Wrote command {}", command); self.read_response(command) } pub fn raw_two_part_command(&mut self, command: String, additional: String) -> String { self.uart.flush().unwrap(); self.flush_rx(); self.uart.write_str(&(command.clone() + "\r")).unwrap(); self.uart.flush().unwrap(); self.delay.delay_millis(500); log::info!("Wrote command {}", command); self.flush_rx(); let text = additional.clone() + "\x1A"; if let Some(ascii) = text.as_ascii() { log::info!("{:?}", &ascii.as_bytes()); self.uart.write(&ascii.as_bytes()).unwrap(); log::info!("Wrote additional {}", additional); } else { self.uart.write_str("???\x1A").unwrap(); log::info!("Wrote ???"); } self.uart.flush().unwrap(); self.read_response(command) } pub fn readline(&mut self) -> Option { self.flush_rx(); if self.lines.len() > 0 { Some(self.lines.remove(0)) } else { None } } fn read_response(&mut self, start: String) -> String { let mut response = None; while response.is_none() { self.flush_rx(); log::info!("{:?}", self.lines); response = self.parse_response(&start); } response.unwrap() } fn parse_response(&mut self, start: &String) -> Option { let start_idx = self .lines .iter() .enumerate() .find(|(_, l)| l.starts_with(start)) .map(|(i, _)| i); if let Some(start_idx) = start_idx { let mut response = String::new(); for (i, line) in self.lines.iter().cloned().skip(start_idx).enumerate() { if i == 0 { continue; } if response.len() > 0 { response += "\n"; } response += &line; if RESPONSES.iter().any(|r| line.starts_with(r)) { for _ in 0..=i { self.lines.remove(start_idx); } return Some(response); } } } None } pub fn flush_rx(&mut self) { let mut buffer = [0u8; 1024]; while { self.delay.delay_millis(10); self.uart.read_ready() } { let length = self.uart.read(&mut buffer).unwrap(); self.buffer += str::from_utf8(&buffer[..length]).unwrap(); } let lines = self .buffer .split("\n") .map(|s| s.to_string()) .collect::>(); self.buffer = lines.last().unwrap().to_string(); let full_lines_len = lines.len() - 1; for line in lines.into_iter().take(full_lines_len) { self.lines.push(line); } } } pub trait ATCommand: Send + Sync { type Response: core::fmt::Debug; fn execute(&self) -> ConstructedATCommand; fn parse_response(text: String) -> Self::Response; } #[derive(Debug, Clone)] pub struct StubATCommand(()); impl ATCommand for StubATCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { panic!("Should never be called") } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct ATInformationCommand; impl ATCommand for ATInformationCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single("ATI".to_string()) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct EnterPinCommand(pub String); impl ATCommand for EnterPinCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single(format!("AT+CPIN={}", self.0)) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct CheckPinCommand; impl ATCommand for CheckPinCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single("AT+CPIN?".to_string()) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone, Copy)] pub enum SMSFormat { PDUMode = 0, TextMode = 1, } #[derive(Debug, Clone)] pub struct SelectSMSFormatCommand(pub SMSFormat); impl ATCommand for SelectSMSFormatCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single(format!("AT+CMGF={}", self.0 as u8)) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct CheckSMSFormatCommand; impl ATCommand for CheckSMSFormatCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single("AT+CMGF?".to_string()) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct ListTECharacterSetsCommand; impl ATCommand for ListTECharacterSetsCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single("AT+CSCS=?".to_string()) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub enum TECharset { IRA, UCS2, HEX, GSM, } impl TECharset { pub fn into_str(&self) -> String { match self { TECharset::IRA => "IRA".to_owned(), TECharset::UCS2 => "UCS2".to_owned(), TECharset::HEX => "HEX".to_owned(), TECharset::GSM => "GSM".to_owned(), } } } #[derive(Debug, Clone)] pub struct SetTECharsetCommand(pub TECharset); impl ATCommand for SetTECharsetCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::Single(format!("AT+CSCS=\"{}\"", self.0.into_str())) } fn parse_response(text: String) -> Self::Response { text } } #[derive(Debug, Clone)] pub struct SendSMSCommand { pub destination: String, pub message: String, } impl ATCommand for SendSMSCommand { type Response = String; fn execute(&self) -> ConstructedATCommand { ConstructedATCommand::AddInfo( format!("AT+CMGS=\"{}\"", self.destination), self.message.clone(), ) } fn parse_response(text: String) -> Self::Response { text } }