use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{borrow::ToOwned, format}; use core::fmt::Write; 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, cmd: &T, command: String) -> Vec { 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(cmd, command) } pub fn raw_two_part_command( &mut self, cmd: &T, command: String, additional: String, ) -> Vec { 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(cmd, 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, cmd: &T, start: String) -> Vec { let mut response = None; while response.is_none() { self.flush_rx(); log::info!("{:?}", self.lines); response = self.parse_response(cmd, &start); } log::info!("{:?}", response); response.unwrap() } fn parse_response( &mut self, cmd: &T, 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 = Vec::new(); for (i, line) in self.lines.clone().into_iter().skip(start_idx).enumerate() { if i == 0 { continue; } response.push(line.trim().to_string()); let mut parser = ATResponseParser::from(response.clone()); match cmd.try_parse(&mut parser) { Ok(_) => { let mut output_lines = Vec::new(); // Remove first line, which is just the command echoed self.lines.remove(start_idx); for _ in 0..parser.lines { output_lines.push(self.lines.remove(start_idx)); } return Some(output_lines); } Err(err) => match err { ATParseError::EOF => {} ATParseError::InvalidResponse => panic!("Invalid 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 struct ATResponseParser { curr_line: Option, text: Vec, index: usize, lines: usize, } impl ATResponseParser { pub fn from(text: Vec) -> ATResponseParser { ATResponseParser { curr_line: None, text, index: 0, lines: 0, } } pub fn readline(&mut self) -> Option { if let Some(curr_line) = self.curr_line.take() { return Some(curr_line); } let result = self.text.get(self.index); self.index += 1; if let Some(_) = result { self.lines += 1; } result.cloned() } pub fn expect(&mut self, text: String) -> Result<(), ATParseError> { let line = if let Some(curr_line) = self.curr_line.take() { curr_line } else { self.readline().ok_or(ATParseError::EOF)? }; if let Some(text) = line.strip_prefix(&text) { self.curr_line = Some(text.to_string()); Ok(()) } else { self.curr_line = Some(line); Err(ATParseError::InvalidResponse) } } } #[derive(Clone, Copy, Debug)] pub enum ATParseError { EOF, InvalidResponse, } pub trait ATCommand: Send + Sync { type Response: core::fmt::Debug + ATResponse; fn execute(&self, at_commands: &mut ATCommands) -> Vec; fn parse_response(parser: &mut ATResponseParser) -> Result; } pub trait SimpleATCommand: Send + Sync { fn execute(&self, at_commands: &mut ATCommands) -> Vec; fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATParseError>; } impl SimpleATCommand for T { fn execute(&self, at_commands: &mut ATCommands) -> Vec { self.execute(at_commands) } fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATParseError> { let result = Self::parse_response(parser); result.map(|_| ()) } } pub trait ATResponse { fn is_error(&self) -> bool; } #[derive(Debug, Clone)] pub enum SimpleATResponse { Ok, Error, } impl ATResponse for SimpleATResponse { fn is_error(&self) -> bool { match self { SimpleATResponse::Ok => false, SimpleATResponse::Error => true, } } } #[derive(Debug, Clone)] pub struct ATInformation { manufacturer: String, model: String, revision: String, imei: String, } impl ATResponse for ATInformation { fn is_error(&self) -> bool { false } } #[derive(Debug, Clone)] pub struct ATInformationCommand; impl ATCommand for ATInformationCommand { type Response = ATInformation; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, "ATI".to_string()) } fn parse_response(parser: &mut ATResponseParser) -> Result { parser.expect("Manufacturer: ".to_string())?; let manufacturer = parser.readline().unwrap(); parser.expect("Model: ".to_string())?; let model = parser.readline().unwrap(); parser.expect("Revision: ".to_string())?; let revision = parser.readline().unwrap(); parser.expect("IMEI: ".to_string())?; let imei = parser.readline().unwrap(); while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(ATInformation { manufacturer, model, revision, imei, }); } } Err(ATParseError::EOF) } } #[derive(Debug, Clone)] pub enum EnterPinResult { Ok, Error, ErrorMessage(String), } impl ATResponse for EnterPinResult { fn is_error(&self) -> bool { match self { EnterPinResult::Ok => false, _ => true, } } } #[derive(Debug, Clone)] pub struct EnterPinCommand(pub String); impl ATCommand for EnterPinCommand { type Response = EnterPinResult; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, format!("AT+CPIN={}", self.0)) } fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(EnterPinResult::Ok); } else if line.starts_with("ERROR") { return Ok(EnterPinResult::Error); } else if let Some(status) = line.strip_prefix("+CME ERROR: ") { return Ok(EnterPinResult::ErrorMessage(status.to_string())); } } Err(ATParseError::EOF) } } #[derive(Debug, Clone)] pub enum CheckPinResult { Status(String), Error, ErrorMessage(String), } impl ATResponse for CheckPinResult { fn is_error(&self) -> bool { match self { CheckPinResult::Status(_) => false, _ => true, } } } #[derive(Debug, Clone)] pub struct CheckPinCommand; impl ATCommand for CheckPinCommand { type Response = CheckPinResult; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, "AT+CPIN?".to_string()) } fn parse_response(parser: &mut ATResponseParser) -> Result { if let Ok(_) = parser.expect("+CPIN: ".to_string()) { let status = parser.readline().unwrap(); while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(CheckPinResult::Status(status)); } } } while let Some(line) = parser.readline() { if line.starts_with("ERROR") { return Ok(CheckPinResult::Error); } else if let Some(status) = line.strip_prefix("+CME ERROR: ") { return Ok(CheckPinResult::ErrorMessage(status.to_string())); } } Err(ATParseError::EOF) } } #[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 = SimpleATResponse; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, format!("AT+CMGF={}", self.0 as u8)) } fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(SimpleATResponse::Ok); } else if line.starts_with("ERROR") { return Ok(SimpleATResponse::Error); } } Err(ATParseError::EOF) } } #[derive(Debug, Clone)] pub enum CheckSMSFormatResult { Mode(String), Error, } impl ATResponse for CheckSMSFormatResult { fn is_error(&self) -> bool { match self { CheckSMSFormatResult::Mode(_) => false, _ => true, } } } #[derive(Debug, Clone)] pub struct CheckSMSFormatCommand; impl ATCommand for CheckSMSFormatCommand { type Response = CheckSMSFormatResult; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, "AT+CMGF?".to_string()) } fn parse_response(parser: &mut ATResponseParser) -> Result { if let Ok(_) = parser.expect("+CMGF: ".to_string()) { let mode = parser.readline().unwrap(); while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(CheckSMSFormatResult::Mode(mode)); } } } while let Some(line) = parser.readline() { if line.starts_with("ERROR") { return Ok(CheckSMSFormatResult::Error); } } Err(ATParseError::EOF) } } #[derive(Debug, Clone)] pub enum TECharacterSets { Charsets(Vec), } impl ATResponse for TECharacterSets { fn is_error(&self) -> bool { match self { _ => false, } } } #[derive(Debug, Clone)] pub struct ListTECharacterSetsCommand; impl ATCommand for ListTECharacterSetsCommand { type Response = TECharacterSets; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, "AT+CSCS=?".to_string()) } fn parse_response(parser: &mut ATResponseParser) -> Result { parser.expect("+CSCS: ".to_owned())?; let result = parser.readline().unwrap(); let result = result.strip_prefix("(").unwrap().strip_suffix(")").unwrap(); let results = result.split(",").map(|s| s.to_owned()).collect::>(); while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(TECharacterSets::Charsets(results)); } } Err(ATParseError::EOF) } } #[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 = SimpleATResponse; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_command(self, format!("AT+CSCS=\"{}\"", self.0.into_str())) } fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(SimpleATResponse::Ok); } else if line.starts_with("ERROR") { return Ok(SimpleATResponse::Error); } } Err(ATParseError::EOF) } } #[derive(Debug, Clone)] pub enum SendSMSResponse { MessageRefrence(String), Error, ErrorMessage(String), } impl ATResponse for SendSMSResponse { fn is_error(&self) -> bool { match self { SendSMSResponse::MessageRefrence(_) => false, _ => true, } } } #[derive(Debug, Clone)] pub struct SendSMSCommand { pub destination: String, pub message: String, } impl ATCommand for SendSMSCommand { type Response = SendSMSResponse; fn execute(&self, at_commands: &mut ATCommands) -> Vec { at_commands.raw_two_part_command( self, format!("AT+CMGS=\"{}\"", self.destination), self.message.clone(), ) } fn parse_response(parser: &mut ATResponseParser) -> Result { if let Ok(_) = parser.expect("+CMS ERROR: ".to_owned()) { let result = parser.readline().unwrap(); return Ok(SendSMSResponse::ErrorMessage(result)); } // Read first line out parser.readline(); if let Ok(_) = parser.expect("+CMGS: ".to_owned()) { let result = parser.readline().unwrap(); while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(SendSMSResponse::MessageRefrence(result)); } } } else if let Ok(_) = parser.expect("+CMS ERROR:".to_owned()) { let message = parser.readline().unwrap(); return Ok(SendSMSResponse::ErrorMessage(message)); } else { while let Some(line) = parser.readline() { if line.starts_with("ERROR") { return Ok(SendSMSResponse::Error); } } } Err(ATParseError::EOF) } }