Reimplement AT init state as async

This commit is contained in:
Sofia 2026-05-17 17:54:22 +03:00
parent 5475bb6cf9
commit 15bffd04a8
4 changed files with 162 additions and 47 deletions

89
src/async_io.rs Normal file
View File

@ -0,0 +1,89 @@
use core::cell::RefCell;
use alloc::{rc::Rc, string::String};
use critical_section::Mutex;
#[derive(Clone)]
pub enum ATCommand {
/// ATI
ATInformation,
/// AT+CPIN=num
EnterPin(String),
/// AT+CPIN?
CheckPin,
/// AT+CMGF=num
SelectSMSFormat(SMSFormat),
/// AT+CMGF?
CheckSMSFormat,
/// AT+CSCS=?
ListTECharacterSets,
}
#[derive(Clone)]
pub enum SMSFormat {
PDUMode = 0,
TextMode = 1,
}
#[derive(Clone)]
pub struct AsyncIO {
at_command: Rc<Mutex<RefCell<Option<ATCommand>>>>,
at_response: Rc<Mutex<RefCell<Option<String>>>>,
}
impl Default for AsyncIO {
fn default() -> Self {
Self {
at_command: Rc::new(Mutex::new(RefCell::new(None))),
at_response: Rc::new(Mutex::new(RefCell::new(None))),
}
}
}
unsafe impl Send for AsyncIO {}
unsafe impl Sync for AsyncIO {}
impl AsyncIO {
pub fn send_at_command(&self, command: ATCommand) -> Result<(), ()> {
critical_section::with(|cs| {
let mut borrow = self.at_command.borrow_ref_mut(cs);
if borrow.is_some() {
return Err(());
}
*borrow = Some(command);
Ok(())
})
}
pub unsafe fn check_at_command(&self) -> Option<ATCommand> {
critical_section::with(|cs| {
let borrow = self.at_command.borrow_ref(cs);
borrow.clone()
})
}
pub unsafe fn set_at_response(&self, response_str: String) {
critical_section::with(|cs| {
let mut command = self.at_command.borrow_ref_mut(cs);
let mut response = self.at_response.borrow_ref_mut(cs);
command.take();
*response = Some(response_str);
})
}
pub fn poll_at_response(&self) -> Option<Option<String>> {
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() {
return Some(None);
}
if let Some(resp) = response.take() {
return Some(Some(resp));
}
None
})
}
}

View File

@ -12,11 +12,17 @@ use core::{
mem::MaybeUninit, mem::MaybeUninit,
}; };
use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc, vec::Vec}; use alloc::{
use critical_section::{CriticalSection, Mutex}; borrow::ToOwned,
use embassy_executor::Spawner; boxed::Box,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use critical_section::Mutex;
use esp_hal::{ use esp_hal::{
clock::{ClockConfig, CpuClock}, clock::CpuClock,
delay::Delay, delay::Delay,
gpio::{InputConfig, Level, Output, OutputConfig}, gpio::{InputConfig, Level, Output, OutputConfig},
interrupt::software::SoftwareInterruptControl, interrupt::software::SoftwareInterruptControl,
@ -31,6 +37,7 @@ use esp_hal::{
use esp_backtrace as _; use esp_backtrace as _;
use crate::{ use crate::{
async_io::AsyncIO,
at_commands::ATCommands, at_commands::ATCommands,
display::{Display, SetAddressMode}, display::{Display, SetAddressMode},
state::StateManager, state::StateManager,
@ -39,6 +46,7 @@ use crate::{
extern crate alloc; extern crate alloc;
mod async_io;
mod at_commands; mod at_commands;
mod display; mod display;
mod font; mod font;
@ -127,15 +135,19 @@ fn main() -> ! {
.with_rx(peripherals.GPIO16) .with_rx(peripherals.GPIO16)
.with_tx(peripherals.GPIO17); .with_tx(peripherals.GPIO17);
let at_commands = ATCommands { let mut at_commands = ATCommands {
rst: sim_rst, rst: sim_rst,
pwr_key: sim_pwr_key, pwr_key: sim_pwr_key,
uart, uart,
delay: Delay::new(), delay: Delay::new(),
}; };
at_commands.init();
let async_io = AsyncIO::default();
let mut state_mgr = StateManager { let mut state_mgr = StateManager {
data: state::StateData::from(display, at_commands), data: state::StateData::from(display, async_io.clone()),
curr_state: Box::new(InitATState::default()), curr_state: Box::new(InitATState::default()),
}; };
@ -157,10 +169,8 @@ fn main() -> ! {
// let input_6 = Input::new(peripherals.GPIO26, pull_up_cfg); // let input_6 = Input::new(peripherals.GPIO26, pull_up_cfg);
// let input_7 = Input::new(peripherals.GPIO27, pull_up_cfg); // let input_7 = Input::new(peripherals.GPIO27, pull_up_cfg);
let messages = Arc::new(Mutex::new(RefCell::new(Vec::<String>::new()))); static mut THREAD_2_STACK: Stack<{ 30 * 1024 }> = esp_hal::system::Stack {
mem: MaybeUninit::new([0u8; 30 * 1024]),
static mut THREAD_2_STACK: Stack<1024> = esp_hal::system::Stack {
mem: MaybeUninit::new([0u8; 1024]),
}; };
let timg0 = TimerGroup::new(peripherals.TIMG0); let timg0 = TimerGroup::new(peripherals.TIMG0);
@ -174,35 +184,40 @@ fn main() -> ! {
&mut THREAD_2_STACK &mut THREAD_2_STACK
}, },
{ {
let messages = messages.clone(); let io = async_io.clone();
|| thread_2_main(messages) || thread_2_main(io, at_commands)
}, },
); );
let mut test_delay = Delay::new(); let mut test_delay = Delay::new();
loop { loop {
critical_section::with(|cs| { state_mgr.update();
let mut borrowed = messages.borrow(cs).borrow_mut(); state_mgr.draw();
for message in borrowed.iter() {
log::info!("Received: {}", message);
}
borrowed.clear();
})
// state_mgr.update();
// state_mgr.draw();
} }
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples
} }
fn thread_2_main(messages: Arc<Mutex<RefCell<Vec<String>>>>) { fn thread_2_main(async_io: AsyncIO, mut at_commands: ATCommands<'static, 'static>) {
let delay = Delay::new();
loop { loop {
delay.delay_millis(1000); if let Some(command) = unsafe { async_io.check_at_command() } {
log::info!("Sent message"); let response = match command {
critical_section::with(|cs| { async_io::ATCommand::ATInformation => at_commands.raw_command("ATI".to_string()),
let mut borrowed = messages.borrow(cs).borrow_mut(); async_io::ATCommand::EnterPin(pin) => {
borrowed.push("Message!".to_owned()); at_commands.raw_command(format!("AT+CPIN={}", pin))
}) }
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())
}
};
unsafe { async_io.set_at_response(response) };
}
} }
} }

View File

@ -2,6 +2,7 @@ use alloc::{boxed::Box, string::String, vec::Vec};
use esp_hal::{Blocking, delay::Delay}; use esp_hal::{Blocking, delay::Delay};
use crate::{ use crate::{
async_io::AsyncIO,
at_commands::ATCommands, at_commands::ATCommands,
display::{Color, Display, Position, Rgb565}, display::{Color, Display, Position, Rgb565},
font::{FontRenderer, HorizontalAlignment, VerticalAlignment}, font::{FontRenderer, HorizontalAlignment, VerticalAlignment},
@ -34,8 +35,8 @@ pub enum DrawCommands {
pub struct StateData<'a> { pub struct StateData<'a> {
display: Display<'a, Blocking, Delay>, display: Display<'a, Blocking, Delay>,
pub at_commands: ATCommands<'a, 'a>,
font_renderer: FontRenderer<'a>, font_renderer: FontRenderer<'a>,
pub io: AsyncIO,
pub delay: Delay, pub delay: Delay,
prev_commands: Vec<DrawCommands>, prev_commands: Vec<DrawCommands>,
@ -43,13 +44,10 @@ pub struct StateData<'a> {
} }
impl<'a> StateData<'a> { impl<'a> StateData<'a> {
pub fn from( pub fn from(display: Display<'a, Blocking, Delay>, io: AsyncIO) -> StateData<'a> {
display: Display<'a, Blocking, Delay>,
at_commands: ATCommands<'a, 'a>,
) -> StateData<'a> {
StateData { StateData {
display, display,
at_commands, io,
font_renderer: FontRenderer::create(30), font_renderer: FontRenderer::create(30),
delay: Delay::new(), delay: Delay::new(),

View File

@ -1,6 +1,11 @@
use alloc::{borrow::ToOwned, boxed::Box, string::String}; use alloc::{
borrow::ToOwned,
boxed::Box,
string::{String, ToString},
};
use crate::{ use crate::{
async_io::{ATCommand, SMSFormat},
display::{Position, Rgb565}, display::{Position, Rgb565},
state::{State, StateData, TextSettings}, state::{State, StateData, TextSettings},
}; };
@ -14,37 +19,45 @@ impl Default for InitATState {
fn default() -> Self { fn default() -> Self {
Self { Self {
inner_state: 0, inner_state: 0,
response: "Initializing AT..".to_owned(), response: "Initializing AT..".to_string(),
} }
} }
} }
impl State for InitATState { impl State for InitATState {
fn update(&mut self, data: &mut StateData) -> Option<Box<dyn State>> { fn update(&mut self, data: &mut StateData) -> Option<Box<dyn State>> {
if let Some(response) = data.io.poll_at_response() {
match response {
Some(response) => self.response = response,
None => {}
}
return None;
}
let res: Option<Box<dyn State>> = match self.inner_state { let res: Option<Box<dyn State>> = match self.inner_state {
0 => { 0 => {
data.at_commands.init(); data.io.send_at_command(ATCommand::ATInformation).unwrap();
self.response = data.at_commands.raw_command("ATI".to_owned());
None None
} }
1 => { 1 => {
self.response = data.at_commands.raw_command("AT+CMGF?".to_owned()); data.io
.send_at_command(ATCommand::EnterPin("1234".to_owned()))
.unwrap();
None None
} }
2 => { 2 => {
self.response = data.at_commands.raw_command("AT+CPIN=1234".to_owned()); data.io.send_at_command(ATCommand::CheckPin).unwrap();
None None
} }
3 => { 3 => {
self.response = data.at_commands.raw_command("AT+CPIN?".to_owned()); data.io
.send_at_command(ATCommand::ListTECharacterSets)
.unwrap();
None None
} }
4 => { 4 => {
self.response = data.at_commands.raw_command("AT+CMGF=1".to_owned()); data.io
None .send_at_command(ATCommand::SelectSMSFormat(SMSFormat::TextMode))
} .unwrap();
5 => {
self.response = data.at_commands.raw_command("AT+CSCS=?".to_owned());
None None
} }
_ => { _ => {