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,
};
use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc, vec::Vec};
use critical_section::{CriticalSection, Mutex};
use embassy_executor::Spawner;
use alloc::{
borrow::ToOwned,
boxed::Box,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use critical_section::Mutex;
use esp_hal::{
clock::{ClockConfig, CpuClock},
clock::CpuClock,
delay::Delay,
gpio::{InputConfig, Level, Output, OutputConfig},
interrupt::software::SoftwareInterruptControl,
@ -31,6 +37,7 @@ use esp_hal::{
use esp_backtrace as _;
use crate::{
async_io::AsyncIO,
at_commands::ATCommands,
display::{Display, SetAddressMode},
state::StateManager,
@ -39,6 +46,7 @@ use crate::{
extern crate alloc;
mod async_io;
mod at_commands;
mod display;
mod font;
@ -127,15 +135,19 @@ fn main() -> ! {
.with_rx(peripherals.GPIO16)
.with_tx(peripherals.GPIO17);
let at_commands = ATCommands {
let mut at_commands = ATCommands {
rst: sim_rst,
pwr_key: sim_pwr_key,
uart,
delay: Delay::new(),
};
at_commands.init();
let async_io = AsyncIO::default();
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()),
};
@ -157,10 +169,8 @@ fn main() -> ! {
// let input_6 = Input::new(peripherals.GPIO26, 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<1024> = esp_hal::system::Stack {
mem: MaybeUninit::new([0u8; 1024]),
static mut THREAD_2_STACK: Stack<{ 30 * 1024 }> = esp_hal::system::Stack {
mem: MaybeUninit::new([0u8; 30 * 1024]),
};
let timg0 = TimerGroup::new(peripherals.TIMG0);
@ -174,35 +184,40 @@ fn main() -> ! {
&mut THREAD_2_STACK
},
{
let messages = messages.clone();
|| thread_2_main(messages)
let io = async_io.clone();
|| thread_2_main(io, at_commands)
},
);
let mut test_delay = Delay::new();
loop {
critical_section::with(|cs| {
let mut borrowed = messages.borrow(cs).borrow_mut();
for message in borrowed.iter() {
log::info!("Received: {}", message);
}
borrowed.clear();
})
// state_mgr.update();
// state_mgr.draw();
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
}
fn thread_2_main(messages: Arc<Mutex<RefCell<Vec<String>>>>) {
let delay = Delay::new();
fn thread_2_main(async_io: AsyncIO, mut at_commands: ATCommands<'static, 'static>) {
loop {
delay.delay_millis(1000);
log::info!("Sent message");
critical_section::with(|cs| {
let mut borrowed = messages.borrow(cs).borrow_mut();
borrowed.push("Message!".to_owned());
})
if let Some(command) = unsafe { async_io.check_at_command() } {
let response = match command {
async_io::ATCommand::ATInformation => at_commands.raw_command("ATI".to_string()),
async_io::ATCommand::EnterPin(pin) => {
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 crate::{
async_io::AsyncIO,
at_commands::ATCommands,
display::{Color, Display, Position, Rgb565},
font::{FontRenderer, HorizontalAlignment, VerticalAlignment},
@ -34,8 +35,8 @@ pub enum DrawCommands {
pub struct StateData<'a> {
display: Display<'a, Blocking, Delay>,
pub at_commands: ATCommands<'a, 'a>,
font_renderer: FontRenderer<'a>,
pub io: AsyncIO,
pub delay: Delay,
prev_commands: Vec<DrawCommands>,
@ -43,13 +44,10 @@ pub struct StateData<'a> {
}
impl<'a> StateData<'a> {
pub fn from(
display: Display<'a, Blocking, Delay>,
at_commands: ATCommands<'a, 'a>,
) -> StateData<'a> {
pub fn from(display: Display<'a, Blocking, Delay>, io: AsyncIO) -> StateData<'a> {
StateData {
display,
at_commands,
io,
font_renderer: FontRenderer::create(30),
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::{
async_io::{ATCommand, SMSFormat},
display::{Position, Rgb565},
state::{State, StateData, TextSettings},
};
@ -14,37 +19,45 @@ impl Default for InitATState {
fn default() -> Self {
Self {
inner_state: 0,
response: "Initializing AT..".to_owned(),
response: "Initializing AT..".to_string(),
}
}
}
impl State for InitATState {
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 {
0 => {
data.at_commands.init();
self.response = data.at_commands.raw_command("ATI".to_owned());
data.io.send_at_command(ATCommand::ATInformation).unwrap();
None
}
1 => {
self.response = data.at_commands.raw_command("AT+CMGF?".to_owned());
data.io
.send_at_command(ATCommand::EnterPin("1234".to_owned()))
.unwrap();
None
}
2 => {
self.response = data.at_commands.raw_command("AT+CPIN=1234".to_owned());
data.io.send_at_command(ATCommand::CheckPin).unwrap();
None
}
3 => {
self.response = data.at_commands.raw_command("AT+CPIN?".to_owned());
data.io
.send_at_command(ATCommand::ListTECharacterSets)
.unwrap();
None
}
4 => {
self.response = data.at_commands.raw_command("AT+CMGF=1".to_owned());
None
}
5 => {
self.response = data.at_commands.raw_command("AT+CSCS=?".to_owned());
data.io
.send_at_command(ATCommand::SelectSMSFormat(SMSFormat::TextMode))
.unwrap();
None
}
_ => {