Read messages

This commit is contained in:
Sofia 2026-06-04 19:04:01 +03:00
parent 9c582a4de1
commit 4c0f284b62
2 changed files with 205 additions and 6 deletions

View File

@ -66,7 +66,6 @@ impl<'a, 'd> ATCommands<'a, 'd> {
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, timeout)
}
@ -82,7 +81,6 @@ impl<'a, 'd> ATCommands<'a, 'd> {
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();
@ -185,7 +183,9 @@ impl<'a, 'd> ATCommands<'a, 'd> {
self.uart.read_ready()
} {
let length = self.uart.read(&mut buffer).unwrap();
self.buffer += str::from_utf8(&buffer[..length]).unwrap();
for character in &buffer[..length] {
self.buffer += str::from_utf8(&[character.clone()]).unwrap_or("?")
}
}
let lines = self
@ -231,6 +231,13 @@ impl ATResponseParser {
result.cloned()
}
pub fn params(&mut self) -> Option<Params> {
self.readline().map(|l| Params {
raw_params: l.to_owned(),
idx: 0,
})
}
pub fn expect(&mut self, text: String) -> Result<(), ATError> {
let line = if let Some(curr_line) = self.curr_line.take() {
curr_line
@ -246,6 +253,59 @@ impl ATResponseParser {
Err(ATError::InvalidResponse)
}
}
pub fn peek(&self) -> Option<String> {
if let Some(curr_line) = &self.curr_line {
return Some(curr_line.clone());
}
if let Some(text) = self.text.get(self.index) {
Some(text.clone())
} else {
None
}
}
}
pub struct Params {
raw_params: String,
idx: u32,
}
impl Params {
pub fn read_raw(&mut self) -> Option<String> {
if self.idx > 0 {
self.raw_params = self.raw_params.strip_prefix(",").unwrap().to_owned()
}
let returned;
if let Some(rest) = self.raw_params.strip_prefix("\"") {
let mut parts = rest.split("\"");
returned = parts.next().unwrap().to_owned();
self.raw_params = parts.collect::<Vec<_>>().join("\"")
} else {
let mut parts = self.raw_params.split(",");
returned = parts.next().unwrap().to_owned();
self.raw_params = parts.collect::<Vec<_>>().join(",")
}
Some(returned)
}
pub fn read_u32(&mut self) -> Option<u32> {
if let Some(part) = self.read_raw() {
u32::from_str_radix(&part, 10).ok()
} else {
None
}
}
pub fn read_string(&mut self) -> Option<String> {
if let Some(part) = self.read_raw() {
Some(part)
} else {
None
}
}
}
#[derive(Clone, Copy, Debug)]
@ -651,3 +711,122 @@ impl ATCommand for SendSMSCommand {
Err(ATError::EOF)
}
}
#[derive(Debug, Clone)]
pub enum ListSMSMessagesResponse {
Ok(Vec<SMSMessageListing>),
Error(String),
}
#[derive(Debug, Clone)]
pub struct SMSMessageListing {
index: u32,
stat: SMSMessageStat,
from_addr: String,
to_addr: String,
timestamp: String,
text: String,
}
impl ATResponse for ListSMSMessagesResponse {
fn is_error(&self) -> bool {
match self {
ListSMSMessagesResponse::Error(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub enum SMSMessageStat {
RecUnread,
RecRead,
StoUnsent,
StoSent,
All,
}
impl SMSMessageStat {
pub fn into_str(&self) -> String {
match self {
SMSMessageStat::RecUnread => "REC UNREAD",
SMSMessageStat::RecRead => "REC READ",
SMSMessageStat::StoUnsent => "STO UNSENT",
SMSMessageStat::StoSent => "STO SENT",
SMSMessageStat::All => "ALL",
}
.to_string()
}
pub fn from(text: &str) -> SMSMessageStat {
match text {
"REC UNREAD" => Self::RecUnread,
"REC READ" => Self::RecRead,
"STO UNSENT" => Self::StoUnsent,
"STO SENT" => Self::StoSent,
_ => Self::All,
}
}
}
#[derive(Debug, Clone)]
pub struct ListSMSMessages {
pub stat: SMSMessageStat,
}
impl ATCommand for ListSMSMessages {
type Response = ListSMSMessagesResponse;
fn execute(&self, at_commands: &mut ATCommands) -> Result<Vec<String>, ATError> {
at_commands.raw_command(
self,
format!("AT+CMGL=\"{}\"", self.stat.into_str()),
Duration::from_millis(5000),
)
}
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATError> {
let mut messages = Vec::new();
while let Ok(_) = parser.expect("+CMGL: ".to_owned()) {
let mut params = parser.params().unwrap();
let idx = params.read_u32().unwrap();
let stat = SMSMessageStat::from(&params.read_string().unwrap());
let from_addr = params.read_string().unwrap();
let to_addr = params.read_string().unwrap();
let timestamp = params.read_string().unwrap();
let mut message = String::new();
while let Some(line) = parser.peek() {
if line.starts_with("OK") || line.starts_with("+CMGL: ") {
break;
}
message += &parser.readline().unwrap();
}
messages.push(SMSMessageListing {
index: idx,
stat,
from_addr,
to_addr,
timestamp,
text: message,
});
}
log::info!("Messages so far: {:?}", messages);
while let Some(line) = parser.readline() {
if line.starts_with("OK") {
return Ok(ListSMSMessagesResponse::Ok(messages));
}
if line.starts_with("+CMS ERROR: ") {
return Ok(ListSMSMessagesResponse::Error(
line.strip_prefix("+CMS ERROR: ").unwrap().to_owned(),
));
}
}
Err(ATError::EOF)
}
}

View File

@ -12,8 +12,8 @@ use crate::{
async_io::{ATPromise, KeypadButton, NumberInput, TextInput},
at_commands::{
ATCommand, ATError, ATInformationCommand, CheckPinCommand, CheckPinResult, EnterPinCommand,
EnterPinResult, SMSFormat, SelectSMSFormatCommand, SendSMSCommand, SendSMSResponse,
SetTECharsetCommand, SimpleATResponse, TECharset,
EnterPinResult, ListSMSMessages, SMSFormat, SMSMessageStat, SelectSMSFormatCommand,
SendSMSCommand, SendSMSResponse, SetTECharsetCommand, SimpleATResponse, TECharset,
},
display::{Position, Rgb565},
font::{HorizontalAlignment, VerticalAlignment},
@ -337,7 +337,27 @@ impl State for MainMenuState {
if let Some(option) = self.menu.poll(&mut data.io) {
match option {
MainMenuItem::SendSMS => return Some(Box::new(PhoneNumberState::default())),
MainMenuItem::ReadSMS => {}
MainMenuItem::ReadSMS => {
return Some(Box::new(ATCommandState::with(
"Loading messages".to_owned(),
ListSMSMessages {
stat: SMSMessageStat::All,
},
|resp| match resp {
Ok(messages) => {
log::info!("{:?}", messages);
Box::new(TextState {
text: format!("Messages read"),
after: MainMenuState::default(),
})
}
Err(err) => Box::new(TextState {
text: format!("Error:\n{:?}", err),
after: MainMenuState::default(),
}),
},
)));
}
}
}
None