Use structs instead of enums for AT commands
This commit is contained in:
parent
ea70f50e4a
commit
7e4b2e3162
@ -1,50 +1,9 @@
|
|||||||
use core::cell::RefCell;
|
use core::{cell::RefCell, marker::PhantomData};
|
||||||
|
|
||||||
use alloc::{borrow::ToOwned, rc::Rc, string::String};
|
use alloc::{rc::Rc, string::String};
|
||||||
use critical_section::Mutex;
|
use critical_section::Mutex;
|
||||||
|
|
||||||
#[derive(Clone)]
|
use crate::at_commands::ATCommand;
|
||||||
pub enum ATCommand {
|
|
||||||
/// ATI
|
|
||||||
ATInformation,
|
|
||||||
/// AT+CPIN=num
|
|
||||||
EnterPin(String),
|
|
||||||
/// AT+CPIN?
|
|
||||||
CheckPin,
|
|
||||||
/// AT+CMGF=num
|
|
||||||
SelectSMSFormat(SMSFormat),
|
|
||||||
/// AT+CMGF?
|
|
||||||
CheckSMSFormat,
|
|
||||||
/// AT+CSCS=?
|
|
||||||
ListTECharacterSets,
|
|
||||||
SetTECharSet(Charset),
|
|
||||||
SendSMS(String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum SMSFormat {
|
|
||||||
PDUMode = 0,
|
|
||||||
TextMode = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum Charset {
|
|
||||||
IRA,
|
|
||||||
UCS2,
|
|
||||||
HEX,
|
|
||||||
GSM,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Charset {
|
|
||||||
pub fn into_str(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Charset::IRA => "IRA".to_owned(),
|
|
||||||
Charset::UCS2 => "UCS2".to_owned(),
|
|
||||||
Charset::HEX => "HEX".to_owned(),
|
|
||||||
Charset::GSM => "GSM".to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
@ -92,9 +51,20 @@ impl Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ConstructedATCommand {
|
||||||
|
/// Single-part command
|
||||||
|
Single(String),
|
||||||
|
/// Two-part AT-command
|
||||||
|
AddInfo(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for ConstructedATCommand {}
|
||||||
|
unsafe impl Sync for ConstructedATCommand {}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AsyncIO {
|
pub struct AsyncIO {
|
||||||
at_command: Rc<Mutex<RefCell<Option<ATCommand>>>>,
|
at_command: Rc<Mutex<RefCell<Option<ConstructedATCommand>>>>,
|
||||||
at_response: Rc<Mutex<RefCell<Option<String>>>>,
|
at_response: Rc<Mutex<RefCell<Option<String>>>>,
|
||||||
pub button: Button,
|
pub button: Button,
|
||||||
}
|
}
|
||||||
@ -112,21 +82,41 @@ impl Default for AsyncIO {
|
|||||||
unsafe impl Send for AsyncIO {}
|
unsafe impl Send for AsyncIO {}
|
||||||
unsafe impl Sync for AsyncIO {}
|
unsafe impl Sync for AsyncIO {}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ATPromise<T: ATCommand> {
|
||||||
|
_data: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ATCommand> ATPromise<T> {
|
||||||
|
pub fn poll(&self, io: &mut AsyncIO) -> Option<Option<T::Response>> {
|
||||||
|
match io.poll_at_response() {
|
||||||
|
Some(response) => match response {
|
||||||
|
Some(response) => Some(Some(T::parse_response(response))),
|
||||||
|
None => Some(None),
|
||||||
|
},
|
||||||
|
None => Some(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsyncIO {
|
impl AsyncIO {
|
||||||
pub fn send_at_command(&self, command: ATCommand) -> Result<(), ()> {
|
pub fn send_at_command<Resp, T: ATCommand<Response = Resp>>(
|
||||||
|
&self,
|
||||||
|
command: T,
|
||||||
|
) -> Result<(), ()> {
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut borrow = self.at_command.borrow_ref_mut(cs);
|
let mut borrow = self.at_command.borrow_ref_mut(cs);
|
||||||
if borrow.is_some() {
|
if borrow.is_some() {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
*borrow = Some(command);
|
*borrow = Some(command.execute());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn check_at_command(&self) -> Option<ATCommand> {
|
pub unsafe fn check_at_command(&self) -> Option<ConstructedATCommand> {
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let borrow = self.at_command.borrow_ref(cs);
|
let borrow = self.at_command.borrow_ref(cs);
|
||||||
borrow.clone()
|
borrow.clone()
|
||||||
|
|||||||
@ -4,6 +4,8 @@ use alloc::{borrow::ToOwned, format};
|
|||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use esp_hal::{Blocking, delay::Delay, gpio::Output, uart::Uart};
|
use esp_hal::{Blocking, delay::Delay, gpio::Output, uart::Uart};
|
||||||
|
|
||||||
|
use crate::async_io::ConstructedATCommand;
|
||||||
|
|
||||||
static RESPONSES: [&'static str; 3] = ["OK", "ERROR", "DOWNLOAD"];
|
static RESPONSES: [&'static str; 3] = ["OK", "ERROR", "DOWNLOAD"];
|
||||||
|
|
||||||
pub struct ATCommands<'a, 'd> {
|
pub struct ATCommands<'a, 'd> {
|
||||||
@ -155,3 +157,145 @@ impl<'a, 'd> ATCommands<'a, 'd> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ATCommand: Send + Sync {
|
||||||
|
type Response;
|
||||||
|
fn execute(&self) -> ConstructedATCommand;
|
||||||
|
fn parse_response(text: String) -> Self::Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Clone, Copy)]
|
||||||
|
pub enum SMSFormat {
|
||||||
|
PDUMode = 0,
|
||||||
|
TextMode = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
22
src/main.rs
22
src/main.rs
@ -181,25 +181,9 @@ fn thread_2_main(async_io: AsyncIO, mut at_commands: ATCommands<'static, 'static
|
|||||||
loop {
|
loop {
|
||||||
if let Some(command) = unsafe { async_io.check_at_command() } {
|
if let Some(command) = unsafe { async_io.check_at_command() } {
|
||||||
let response = match command {
|
let response = match command {
|
||||||
async_io::ATCommand::ATInformation => at_commands.raw_command("ATI".to_string()),
|
async_io::ConstructedATCommand::Single(cmd) => at_commands.raw_command(cmd),
|
||||||
async_io::ATCommand::EnterPin(pin) => {
|
async_io::ConstructedATCommand::AddInfo(cmd, add) => {
|
||||||
at_commands.raw_command(format!("AT+CPIN={}", pin))
|
at_commands.raw_two_part_command(cmd, add)
|
||||||
}
|
|
||||||
async_io::ATCommand::CheckPin => at_commands.raw_command("AT+CPIN?".to_string()),
|
|
||||||
async_io::ATCommand::SelectSMSFormat(smsformat) => {
|
|
||||||
at_commands.raw_command(format!("AT+CMGF={}", smsformat as u8))
|
|
||||||
}
|
|
||||||
async_io::ATCommand::CheckSMSFormat => {
|
|
||||||
at_commands.raw_command("AT+CMGF?".to_string())
|
|
||||||
}
|
|
||||||
async_io::ATCommand::ListTECharacterSets => {
|
|
||||||
at_commands.raw_command("AT+CSCS=?".to_string())
|
|
||||||
}
|
|
||||||
async_io::ATCommand::SetTECharSet(charset) => {
|
|
||||||
at_commands.raw_command(format!("AT+CSCS=\"{}\"", charset.into_str()))
|
|
||||||
}
|
|
||||||
async_io::ATCommand::SendSMS(da, text) => {
|
|
||||||
at_commands.raw_two_part_command(format!("AT+CMGS={}", da), text)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
unsafe { async_io.set_at_response(response) };
|
unsafe { async_io.set_at_response(response) };
|
||||||
|
|||||||
@ -9,7 +9,10 @@ use alloc::{
|
|||||||
use esp_hal::time::{Duration, Instant};
|
use esp_hal::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
async_io::{ATCommand, Charset, SMSFormat},
|
at_commands::{
|
||||||
|
ATInformationCommand, CheckPinCommand, EnterPinCommand, ListTECharacterSetsCommand,
|
||||||
|
SMSFormat, SelectSMSFormatCommand, SendSMSCommand, SetTECharsetCommand, TECharset,
|
||||||
|
},
|
||||||
display::{Position, Rgb565},
|
display::{Position, Rgb565},
|
||||||
font::{HorizontalAlignment, VerticalAlignment},
|
font::{HorizontalAlignment, VerticalAlignment},
|
||||||
state::{State, StateData, TextSettings},
|
state::{State, StateData, TextSettings},
|
||||||
@ -55,38 +58,36 @@ impl State for InitATState {
|
|||||||
// Send next AT command
|
// Send next AT command
|
||||||
let res: Option<Box<dyn State>> = match self.inner_state {
|
let res: Option<Box<dyn State>> = match self.inner_state {
|
||||||
0 => {
|
0 => {
|
||||||
data.io.send_at_command(ATCommand::ATInformation).unwrap();
|
data.io.send_at_command(ATInformationCommand).unwrap();
|
||||||
self.message = "Checking info".to_owned();
|
self.message = "Checking info".to_owned();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
data.io
|
data.io
|
||||||
.send_at_command(ATCommand::EnterPin("1234".to_owned()))
|
.send_at_command(EnterPinCommand("1234".to_owned()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.message = "Entering PIN".to_owned();
|
self.message = "Entering PIN".to_owned();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
data.io.send_at_command(ATCommand::CheckPin).unwrap();
|
data.io.send_at_command(CheckPinCommand).unwrap();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
3 => {
|
3 => {
|
||||||
data.io
|
data.io.send_at_command(ListTECharacterSetsCommand).unwrap();
|
||||||
.send_at_command(ATCommand::ListTECharacterSets)
|
|
||||||
.unwrap();
|
|
||||||
self.message = "Checking\ncharsets".to_owned();
|
self.message = "Checking\ncharsets".to_owned();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
4 => {
|
4 => {
|
||||||
data.io
|
data.io
|
||||||
.send_at_command(ATCommand::SelectSMSFormat(SMSFormat::TextMode))
|
.send_at_command(SelectSMSFormatCommand(SMSFormat::TextMode))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.message = "Selecting SMS\nformat".to_owned();
|
self.message = "Selecting SMS\nformat".to_owned();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
5 => {
|
5 => {
|
||||||
data.io
|
data.io
|
||||||
.send_at_command(ATCommand::SetTECharSet(Charset::IRA))
|
.send_at_command(SetTECharsetCommand(TECharset::IRA))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.message = "Selecting SMS\nformat".to_owned();
|
self.message = "Selecting SMS\nformat".to_owned();
|
||||||
None
|
None
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user