esp32-phone/src/at_commands.rs

333 lines
8.3 KiB
Rust

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<String>,
}
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<String> {
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<String> {
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::<Vec<String>>();
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
}
}