diff --git a/src/async_io.rs b/src/async_io.rs index 18d90b2..62eaec2 100644 --- a/src/async_io.rs +++ b/src/async_io.rs @@ -10,7 +10,7 @@ use alloc::{ }; use critical_section::Mutex; -use crate::at_commands::{ATCommand, ATParseError, ATResponseParser, SimpleATCommand}; +use crate::at_commands::{ATCommand, ATError, ATResponseParser, SimpleATCommand}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum KeypadButton { @@ -110,7 +110,7 @@ impl Keypad { pub struct AsyncIO { at_command: Rc>>>>, handling_at_command: Rc>>, - at_response: Rc>>>>, + at_response: Rc, ATError>>>>>, pub keypad: Keypad, } @@ -134,16 +134,19 @@ pub struct ATPromise { } impl ATPromise { - pub fn poll(&self, io: &mut AsyncIO) -> Option>> { + pub fn poll(&self, io: &mut AsyncIO) -> Option> { match io.poll_at_response() { - Some(response) => match response { - Some(response) => { - let mut parser = ATResponseParser::from(response); - Some(Some(T::parse_response(&mut parser))) + Some(response) => { + log::info!("Response: {:?}", response); + match response { + Ok(response) => { + let mut parser = ATResponseParser::from(response); + Some(T::parse_response(&mut parser)) + } + Err(err) => Some(Err(err)), } - None => Some(None), - }, - None => Some(None), + } + None => None, } } } @@ -173,7 +176,7 @@ impl AsyncIO { }) } - pub unsafe fn set_at_response(&self, response_str: Vec) { + pub unsafe fn set_at_response(&self, response_str: Result, ATError>) { critical_section::with(|cs| { *self.handling_at_command.borrow_ref_mut(cs) = false; let mut command = self.at_command.borrow_ref_mut(cs); @@ -183,15 +186,15 @@ impl AsyncIO { }) } - fn poll_at_response(&self) -> Option>> { + fn poll_at_response(&self) -> Option, ATError>> { critical_section::with(|cs| { let command = self.at_command.borrow_ref(cs); let mut response = self.at_response.borrow_ref_mut(cs); - if command.is_some() || *self.handling_at_command.borrow_ref(cs) { - return Some(None); + if command.is_some() { + return None; } if let Some(resp) = response.take() { - return Some(Some(resp)); + return Some(resp); } None }) diff --git a/src/at_commands.rs b/src/at_commands.rs index 0e40454..f33aff9 100644 --- a/src/at_commands.rs +++ b/src/at_commands.rs @@ -2,6 +2,7 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{borrow::ToOwned, format}; use core::fmt::Write; +use esp_hal::time::{Duration, Instant}; use esp_hal::{Blocking, delay::Delay, gpio::Output, uart::Uart}; static RESPONSES: [&'static str; 3] = ["OK", "ERROR", "DOWNLOAD"]; @@ -54,7 +55,12 @@ impl<'a, 'd> ATCommands<'a, 'd> { self.flush_rx(); } - pub fn raw_command(&mut self, cmd: &T, command: String) -> Vec { + pub fn raw_command( + &mut self, + cmd: &T, + command: String, + timeout: Duration, + ) -> Result, ATError> { self.uart.flush().unwrap(); self.flush_rx(); let command = command.clone() + "\r"; @@ -62,7 +68,7 @@ impl<'a, 'd> ATCommands<'a, 'd> { self.uart.flush().unwrap(); self.delay.delay_millis(500); log::info!("Wrote command {}", command); - self.read_response(cmd, command) + self.read_response(cmd, command, timeout) } pub fn raw_two_part_command( @@ -70,7 +76,8 @@ impl<'a, 'd> ATCommands<'a, 'd> { cmd: &T, command: String, additional: String, - ) -> Vec { + timeout: Duration, + ) -> Result, ATError> { self.uart.flush().unwrap(); self.flush_rx(); self.uart.write_str(&(command.clone() + "\r")).unwrap(); @@ -90,7 +97,7 @@ impl<'a, 'd> ATCommands<'a, 'd> { } self.uart.flush().unwrap(); - self.read_response(cmd, command) + self.read_response(cmd, command, timeout) } pub fn readline(&mut self) -> Option { @@ -102,16 +109,23 @@ impl<'a, 'd> ATCommands<'a, 'd> { } } - fn read_response(&mut self, cmd: &T, start: String) -> Vec { + fn read_response( + &mut self, + cmd: &T, + start: String, + timeout: Duration, + ) -> Result, ATError> { + let start_time = Instant::now(); + let mut response = None; while response.is_none() { + if (start_time + timeout) < Instant::now() { + return Err(ATError::Timeout); + } self.flush_rx(); - log::info!("{:?}", self.lines); response = self.parse_response(cmd, &start); } - log::info!("{:?}", response); - response.unwrap() } @@ -119,7 +133,7 @@ impl<'a, 'd> ATCommands<'a, 'd> { &mut self, cmd: &T, start: &String, - ) -> Option> { + ) -> Option, ATError>> { let start_idx = self .lines .iter() @@ -127,6 +141,8 @@ impl<'a, 'd> ATCommands<'a, 'd> { .find(|(_, l)| l.starts_with(start)) .map(|(i, _)| i); + log::info!("{:?}", self.lines); + if let Some(start_idx) = start_idx { let mut response = Vec::new(); @@ -136,6 +152,8 @@ impl<'a, 'd> ATCommands<'a, 'd> { } response.push(line.trim().to_string()); + log::info!("{:?}", response); + let mut parser = ATResponseParser::from(response.clone()); match cmd.try_parse(&mut parser) { @@ -148,11 +166,11 @@ impl<'a, 'd> ATCommands<'a, 'd> { for _ in 0..parser.lines { output_lines.push(self.lines.remove(start_idx)); } - return Some(output_lines); + return Some(Ok(output_lines)); } Err(err) => match err { - ATParseError::EOF => {} - ATParseError::InvalidResponse => panic!("Invalid response"), + ATError::EOF => {} + _ => return Some(Err(err)), }, } } @@ -215,11 +233,11 @@ impl ATResponseParser { result.cloned() } - pub fn expect(&mut self, text: String) -> Result<(), ATParseError> { + pub fn expect(&mut self, text: String) -> Result<(), ATError> { let line = if let Some(curr_line) = self.curr_line.take() { curr_line } else { - self.readline().ok_or(ATParseError::EOF)? + self.readline().ok_or(ATError::EOF)? }; if let Some(text) = line.strip_prefix(&text) { @@ -227,34 +245,35 @@ impl ATResponseParser { Ok(()) } else { self.curr_line = Some(line); - Err(ATParseError::InvalidResponse) + Err(ATError::InvalidResponse) } } } #[derive(Clone, Copy, Debug)] -pub enum ATParseError { +pub enum ATError { EOF, InvalidResponse, + Timeout, } 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; + fn execute(&self, at_commands: &mut ATCommands) -> Result, ATError>; + 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>; + fn execute(&self, at_commands: &mut ATCommands) -> Result, ATError>; + fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATError>; } impl SimpleATCommand for T { - fn execute(&self, at_commands: &mut ATCommands) -> Vec { + fn execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { self.execute(at_commands) } - fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATParseError> { + fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATError> { let result = Self::parse_response(parser); result.map(|_| ()) } @@ -298,11 +317,11 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command(self, "ATI".to_string(), Duration::from_millis(2000)) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + fn parse_response(parser: &mut ATResponseParser) -> Result { parser.expect("Manufacturer: ".to_string())?; let manufacturer = parser.readline().unwrap(); parser.expect("Model: ".to_string())?; @@ -322,7 +341,7 @@ impl ATCommand for ATInformationCommand { }); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -347,11 +366,15 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command( + self, + format!("AT+CPIN={}", self.0), + Duration::from_millis(2000), + ) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(EnterPinResult::Ok); @@ -361,7 +384,7 @@ impl ATCommand for EnterPinCommand { return Ok(EnterPinResult::ErrorMessage(status.to_string())); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -386,11 +409,11 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command(self, "AT+CPIN?".to_string(), Duration::from_millis(2000)) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + 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() { @@ -407,7 +430,7 @@ impl ATCommand for CheckPinCommand { return Ok(CheckPinResult::ErrorMessage(status.to_string())); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -422,11 +445,15 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command( + self, + format!("AT+CMGF={}", self.0 as u8), + Duration::from_millis(2000), + ) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(SimpleATResponse::Ok); @@ -434,7 +461,7 @@ impl ATCommand for SelectSMSFormatCommand { return Ok(SimpleATResponse::Error); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -458,11 +485,11 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command(self, "AT+CMGF?".to_string(), Duration::from_millis(2000)) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + 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() { @@ -477,7 +504,7 @@ impl ATCommand for CheckSMSFormatCommand { return Ok(CheckSMSFormatResult::Error); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -499,11 +526,11 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command(self, "AT+CSCS=?".to_string(), Duration::from_millis(2000)) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + 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(); @@ -514,7 +541,7 @@ impl ATCommand for ListTECharacterSetsCommand { return Ok(TECharacterSets::Charsets(results)); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -542,11 +569,15 @@ 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 execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { + at_commands.raw_command( + self, + format!("AT+CSCS=\"{}\"", self.0.into_str()), + Duration::from_millis(2000), + ) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + fn parse_response(parser: &mut ATResponseParser) -> Result { while let Some(line) = parser.readline() { if line.starts_with("OK") { return Ok(SimpleATResponse::Ok); @@ -554,7 +585,7 @@ impl ATCommand for SetTECharsetCommand { return Ok(SimpleATResponse::Error); } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } @@ -583,15 +614,16 @@ pub struct SendSMSCommand { impl ATCommand for SendSMSCommand { type Response = SendSMSResponse; - fn execute(&self, at_commands: &mut ATCommands) -> Vec { + fn execute(&self, at_commands: &mut ATCommands) -> Result, ATError> { at_commands.raw_two_part_command( self, format!("AT+CMGS=\"{}\"", self.destination), self.message.clone(), + Duration::from_millis(30000), ) } - fn parse_response(parser: &mut ATResponseParser) -> Result { + 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)); @@ -618,6 +650,6 @@ impl ATCommand for SendSMSCommand { } } - Err(ATParseError::EOF) + Err(ATError::EOF) } } diff --git a/src/state.rs b/src/state.rs index d4094a1..eaabf04 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,7 +3,7 @@ use esp_hal::{Blocking, delay::Delay}; use crate::{ async_io::{ATPromise, AsyncIO}, - at_commands::{ATCommand, ATParseError}, + at_commands::{ATCommand, ATError}, display::{Color, Display, Position, Rgb565}, font::{FontRenderer, HorizontalAlignment, VerticalAlignment}, }; @@ -118,7 +118,7 @@ impl<'a> StateManager<'a> { pub struct ATCommandHelper { command: Option, promise: Option>, - response: Option>, + response: Option>, } impl ATCommandHelper @@ -133,19 +133,16 @@ where } } - pub fn poll(&mut self, io: &mut AsyncIO) -> Option> { + pub fn poll(&mut self, io: &mut AsyncIO) -> Option> { if let Some(resp) = self.response.clone() { return Some(resp); } else { if let Some(promise) = &mut self.promise { match promise.poll(io) { - Some(response) => match response { - Some(response) => { - self.response = Some(response); - self.response.clone() - } - None => None, - }, + Some(response) => { + self.response = Some(response); + self.response.clone() + } None => None, } } else if let Some(cmd) = self.command.take() { diff --git a/src/states.rs b/src/states.rs index a3467b7..b54b6f2 100644 --- a/src/states.rs +++ b/src/states.rs @@ -11,9 +11,9 @@ use esp_hal::time::{Duration, Instant}; use crate::{ async_io::{ATPromise, KeypadButton, NumberInput, TextInput}, at_commands::{ - ATCommand, ATInformationCommand, ATParseError, CheckPinCommand, CheckPinResult, - EnterPinCommand, EnterPinResult, SMSFormat, SelectSMSFormatCommand, SendSMSCommand, - SendSMSResponse, SetTECharsetCommand, SimpleATResponse, TECharset, + ATCommand, ATError, ATInformationCommand, CheckPinCommand, CheckPinResult, EnterPinCommand, + EnterPinResult, SMSFormat, SelectSMSFormatCommand, SendSMSCommand, SendSMSResponse, + SetTECharsetCommand, SimpleATResponse, TECharset, }, display::{Position, Rgb565}, font::{HorizontalAlignment, VerticalAlignment}, @@ -53,7 +53,7 @@ impl DotsMessage { pub struct ATCommandState where - F: FnOnce(Result) -> Box, + F: FnOnce(Result) -> Box, { message: DotsMessage, command: Cmd, @@ -63,7 +63,7 @@ where impl ATCommandState where - F: FnOnce(Result) -> Box, + F: FnOnce(Result) -> Box, { pub fn with(message: String, command: Cmd, after: F) -> ATCommandState { ATCommandState { @@ -80,7 +80,7 @@ where impl State for ATCommandState where - F: FnOnce(Result) -> Box, + F: FnOnce(Result) -> Box, { fn init(&mut self, data: &mut StateData) { self.promise = Some(data.io.send_at_command(self.command.clone()).unwrap()); @@ -92,12 +92,9 @@ where if let Some(promise) = &self.promise { match promise.poll(&mut data.io) { - Some(response) => match response { - Some(response) => { - return Some((self.fun.clone())(response)); - } - None => {} - }, + Some(response) => { + return Some((self.fun.clone())(response)); + } None => self.promise = None, } return None; @@ -141,18 +138,34 @@ impl State for InitATState { self.message.poll(); if let Some(resp) = self.ati.poll(&mut data.io) { - resp.unwrap() + match resp { + Ok(_) => {} + Err(err) => { + return Some(Box::new(TextState { + text: format!("Err: {:?}", err), + after: InitATState::default(), + })); + } + } } else { self.message.message = "Checking ATI".to_owned(); return None; }; if let Some(resp) = self.sms_charset.poll(&mut data.io) { - match resp.unwrap() { - SimpleATResponse::Ok => {} - SimpleATResponse::Error => { + match resp { + Ok(resp) => match resp { + SimpleATResponse::Ok => {} + SimpleATResponse::Error => { + return Some(Box::new(TextState { + text: "ERROR!".to_owned(), + after: InitATState::default(), + })); + } + }, + Err(err) => { return Some(Box::new(TextState { - text: "ERROR!".to_owned(), + text: format!("Err: {:?}", err), after: InitATState::default(), })); } @@ -163,11 +176,19 @@ impl State for InitATState { }; if let Some(resp) = self.te_charset.poll(&mut data.io) { - match resp.unwrap() { - SimpleATResponse::Ok => {} - SimpleATResponse::Error => { + match resp { + Ok(resp) => match resp { + SimpleATResponse::Ok => {} + SimpleATResponse::Error => { + return Some(Box::new(TextState { + text: "ERROR!".to_owned(), + after: InitATState::default(), + })); + } + }, + Err(err) => { return Some(Box::new(TextState { - text: "ERROR!".to_owned(), + text: format!("Err: {:?}", err), after: InitATState::default(), })); } @@ -230,19 +251,27 @@ impl State for EnterPinState { fn update(&mut self, data: &mut StateData) -> Option> { if let Some(helper) = &mut self.helper { match helper.poll(&mut data.io) { - Some(response) => match response.unwrap() { - EnterPinResult::Ok => { - return Some(Box::new(MainMenuState::default())); - } - EnterPinResult::Error => { + Some(response) => match response { + Ok(response) => match response { + EnterPinResult::Ok => { + return Some(Box::new(MainMenuState::default())); + } + EnterPinResult::Error => { + return Some(Box::new(TextState { + text: "ERROR!".to_owned(), + after: EnterPinState::default(), + })); + } + EnterPinResult::ErrorMessage(msg) => { + return Some(Box::new(TextState { + text: format!("Error:\n{}", msg), + after: EnterPinState::default(), + })); + } + }, + Err(err) => { return Some(Box::new(TextState { - text: "ERROR!".to_owned(), - after: EnterPinState::default(), - })); - } - EnterPinResult::ErrorMessage(msg) => { - return Some(Box::new(TextState { - text: format!("Error:\n{}", msg), + text: format!("Error:\n{:?}", err), after: EnterPinState::default(), })); } @@ -365,17 +394,23 @@ impl State for MessageState { destination: self.number.clone(), message: self.input.read().clone(), }, - |resp| match resp.unwrap() { - SendSMSResponse::MessageRefrence(refrence) => Box::new(TextState { - text: format!("SMS sent\nRef: {}", refrence), - after: MainMenuState::default(), - }), - SendSMSResponse::Error => Box::new(TextState { - text: "ERROR!".to_owned(), - after: MainMenuState::default(), - }), - SendSMSResponse::ErrorMessage(msg) => Box::new(TextState { - text: format!("Error:\n{}", msg), + |resp| match resp { + Ok(resp) => match resp { + SendSMSResponse::MessageRefrence(refrence) => Box::new(TextState { + text: format!("SMS sent\nRef: {}", refrence), + after: MainMenuState::default(), + }), + SendSMSResponse::Error => Box::new(TextState { + text: "ERROR!".to_owned(), + after: MainMenuState::default(), + }), + SendSMSResponse::ErrorMessage(msg) => Box::new(TextState { + text: format!("Error:\n{}", msg), + after: MainMenuState::default(), + }), + }, + Err(err) => Box::new(TextState { + text: format!("Error:\n{:?}", err), after: MainMenuState::default(), }), },