626 lines
17 KiB
Rust
626 lines
17 KiB
Rust
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<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<T: SimpleATCommand>(&mut self, cmd: &T, command: String) -> Vec<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(cmd, command)
|
|
}
|
|
|
|
pub fn raw_two_part_command<T: SimpleATCommand>(
|
|
&mut self,
|
|
cmd: &T,
|
|
command: String,
|
|
additional: String,
|
|
) -> Vec<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(cmd, 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<T: SimpleATCommand>(&mut self, cmd: &T, start: String) -> Vec<String> {
|
|
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<T: SimpleATCommand>(
|
|
&mut self,
|
|
cmd: &T,
|
|
start: &String,
|
|
) -> Option<Vec<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 = 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::<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 struct ATResponseParser {
|
|
curr_line: Option<String>,
|
|
text: Vec<String>,
|
|
index: usize,
|
|
lines: usize,
|
|
}
|
|
|
|
impl ATResponseParser {
|
|
pub fn from(text: Vec<String>) -> ATResponseParser {
|
|
ATResponseParser {
|
|
curr_line: None,
|
|
text,
|
|
index: 0,
|
|
lines: 0,
|
|
}
|
|
}
|
|
|
|
pub fn readline(&mut self) -> Option<String> {
|
|
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<String>;
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError>;
|
|
}
|
|
|
|
pub trait SimpleATCommand: Send + Sync {
|
|
fn execute(&self, at_commands: &mut ATCommands) -> Vec<String>;
|
|
fn try_parse(&self, parser: &mut ATResponseParser) -> Result<(), ATParseError>;
|
|
}
|
|
|
|
impl<T: ATCommand> SimpleATCommand for T {
|
|
fn execute(&self, at_commands: &mut ATCommands) -> Vec<String> {
|
|
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<String> {
|
|
at_commands.raw_command(self, "ATI".to_string())
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String> {
|
|
at_commands.raw_command(self, format!("AT+CPIN={}", self.0))
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String> {
|
|
at_commands.raw_command(self, "AT+CPIN?".to_string())
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String> {
|
|
at_commands.raw_command(self, format!("AT+CMGF={}", self.0 as u8))
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String> {
|
|
at_commands.raw_command(self, "AT+CMGF?".to_string())
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String>),
|
|
}
|
|
|
|
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<String> {
|
|
at_commands.raw_command(self, "AT+CSCS=?".to_string())
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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::<Vec<_>>();
|
|
|
|
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<String> {
|
|
at_commands.raw_command(self, format!("AT+CSCS=\"{}\"", self.0.into_str()))
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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<String> {
|
|
at_commands.raw_two_part_command(
|
|
self,
|
|
format!("AT+CMGS=\"{}\"", self.destination),
|
|
self.message.clone(),
|
|
)
|
|
}
|
|
|
|
fn parse_response(parser: &mut ATResponseParser) -> Result<Self::Response, ATParseError> {
|
|
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)
|
|
}
|
|
}
|