diff --git a/src/async_io.rs b/src/async_io.rs new file mode 100644 index 0000000..6998d2a --- /dev/null +++ b/src/async_io.rs @@ -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>>>, + at_response: Rc>>>, +} + +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 { + 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> { + 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 + }) + } +} diff --git a/src/main.rs b/src/main.rs index 2679ae9..d40c9bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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::::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>>>) { - 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) }; + } } } diff --git a/src/state.rs b/src/state.rs index 809cef5..2f42725 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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, @@ -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(), diff --git a/src/states.rs b/src/states.rs index 1907b17..94ca599 100644 --- a/src/states.rs +++ b/src/states.rs @@ -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> { + if let Some(response) = data.io.poll_at_response() { + match response { + Some(response) => self.response = response, + None => {} + } + return None; + } let res: Option> = 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 } _ => {