use core::{
array,
cell::{Cell, UnsafeCell},
str::FromStr,
};
use heapless::{String, format};
use crate::{
Config,
at_commands::{
ANSWER_CALL, AT, CHECK_PIN_PROTECTED, CTRL_Z, DELETE_MESSAGE, ENABLE_GSM_CHARSET,
HANG_UP_CALL, LIST_ALL_MESSAGES, LIST_CURRENT_CALLS,
},
buffer::RingBuffer,
call_state::CallState,
errors::SimError,
log::{debug, error, info, trace},
state_machine::{self, State},
};
const ASCII_CR: u8 = '\r' as u8;
const ASCII_LF: u8 = '\n' as u8;
const SIM800L_READ_BUFFER_LENGTH: usize = 1024;
const PHONE_NUMBER_MAX_LEN: usize = 24;
const MESSAGE_MAX_LEN: usize = 1024;
pub struct SimX00X<
'a,
U: embedded_io::Write + embedded_io::WriteReady + embedded_io::Read + embedded_io::ReadReady,
const QUEUE_LEN: usize,
> {
pub(crate) uart: UnsafeCell<U>,
pub(crate) uart_buffer: [u8; 1024],
pub(crate) uart_buffer_idx: usize,
pub(crate) state: State,
pub(crate) config: Config<'a, QUEUE_LEN>,
pub(crate) pin_locked: bool,
pub(crate) registered: bool,
pub(crate) expect_ack: bool,
pub(crate) watchdog: Cell<u8>,
pub(crate) parse_index: u8,
pub(crate) send_ussd_pending: bool,
pub(crate) ussd: String<64>,
pub(crate) connect_pending: bool,
pub(crate) disconnect_pending: bool,
pub(crate) sender: String<PHONE_NUMBER_MAX_LEN>,
pub(crate) recipient: String<PHONE_NUMBER_MAX_LEN>,
pub(crate) dial_pending: bool,
pub(crate) call_state: CallState,
pub(crate) send_pending: bool,
pub(crate) message: String<MESSAGE_MAX_LEN>,
pub(crate) outgoing_smses:
RingBuffer<(String<PHONE_NUMBER_MAX_LEN>, String<MESSAGE_MAX_LEN>), QUEUE_LEN>,
}
impl<
'a,
U: embedded_io::Write + embedded_io::WriteReady + embedded_io::Read + embedded_io::ReadReady,
const QUEUE_LEN: usize,
> SimX00X<'a, U, QUEUE_LEN>
{
pub fn reset(&mut self) {
self.uart_buffer_idx = 0;
self.state = State::StateIdle;
self.pin_locked = true;
self.registered = false;
self.send_ussd_pending = false;
self.expect_ack = false;
self.watchdog.set(0);
self.parse_index = 0;
self.ussd = String::new();
self.connect_pending = false;
self.disconnect_pending = false;
self.sender = String::new();
self.recipient = String::new();
self.dial_pending = false;
self.call_state = CallState::Disconnect;
self.send_pending = false;
self.message = String::new();
self.outgoing_smses.reset();
let uart = unsafe { &mut *self.uart.get() };
let _ = uart.flush();
if uart.read_ready().is_ok_and(|x| x) {
let _ = uart.read(&mut self.uart_buffer);
}
}
pub fn new(uart: U, config: Config<'a, QUEUE_LEN>) -> Self {
Self {
uart: UnsafeCell::new(uart),
uart_buffer: [0; 1024],
uart_buffer_idx: 0,
state: State::StateIdle,
config,
pin_locked: true,
registered: false,
send_ussd_pending: false,
expect_ack: false,
watchdog: Cell::new(0),
parse_index: 0,
ussd: String::new(),
connect_pending: false,
disconnect_pending: false,
sender: String::new(),
recipient: String::new(),
dial_pending: false,
call_state: CallState::Disconnect,
send_pending: false,
message: String::new(),
outgoing_smses: RingBuffer::new(array::repeat((String::new(), String::new()))),
}
}
pub(crate) fn push_cmd(&self, cmd: &str) -> Result<(), SimError<U>> {
self.watchdog.set(0);
if self.state == State::StatePoweringDown {
return Err(SimError::PoweringDown);
}
unsafe { &mut *self.uart.get() }
.write_all(cmd.as_bytes())
.map_err(SimError::IOError)
}
pub(crate) fn confirm_cmd(&self) -> Result<(), SimError<U>> {
self.watchdog.set(0);
if self.state == State::StatePoweringDown {
return Err(SimError::PoweringDown);
}
let uart = unsafe { &mut *self.uart.get() };
uart.write_all(&[ASCII_CR]).map_err(SimError::IOError)?;
uart.write_all(&[ASCII_LF]).map_err(SimError::IOError)
}
pub(crate) fn send_cmd(&self, cmd: &str) -> Result<(), SimError<U>> {
self.watchdog.set(0);
if self.state == State::StatePoweringDown {
return Err(SimError::PoweringDown);
}
let uart = unsafe { &mut *self.uart.get() };
uart.write_all(cmd.as_bytes()).map_err(SimError::IOError)?;
uart.write_all(&[ASCII_CR]).map_err(SimError::IOError)?;
uart.write_all(&[ASCII_LF]).map_err(SimError::IOError)
}
pub(crate) fn check_sms(&mut self) -> Result<(), SimError<U>> {
self.send_cmd(LIST_ALL_MESSAGES)?;
self.state = State::StateParseSmsResponse;
self.parse_index = 0;
Ok(())
}
pub fn update(&mut self) -> Result<(), SimError<U>> {
if self.state == State::StatePoweredDown {
return Err(SimError::PoweredDown);
}
self.watchdog.update(|x| x + 1);
info!("WATCHDOG: {}", self.watchdog.get()); if self.watchdog.get() >= 10 {
if let Some(f) = self.config.module_stalled_callback.as_mut() {
if f() {
error!("[Sim800L] Module stalled. Resetting...");
self.reset();
debug!("[Sim800L] Reset OK.");
} else {
error!(
"[Sim800L] Module stalled but the callback returned false. Not resetting."
);
}
} else {
error!("[Sim800L] Module stalled but the callback is not set. Not resetting.");
}
return Err(SimError::ModuleStalled);
}
if self.watchdog.get() == 3 {
self.state = State::StateInit;
unsafe { &mut *self.uart.get() }
.write_all(&[CTRL_Z])
.map_err(SimError::IOError)?;
}
if self.expect_ack {
return Ok(());
}
if self.state == State::StateInit {
if self.registered && self.send_pending && self.outgoing_smses.cons_avail() {
self.send_cmd(ENABLE_GSM_CHARSET)?;
self.state = State::StateSendingSms1;
} else if self.registered && self.dial_pending {
self.send_cmd(ENABLE_GSM_CHARSET)?;
self.state = State::StateDialing1;
} else if self.registered && self.connect_pending {
self.connect_pending = false;
info!("[Sim800L] Answering call...");
self.send_cmd(ANSWER_CALL)?;
self.state = State::StateAtaSent;
} else if self.registered && self.send_ussd_pending {
self.send_cmd(ENABLE_GSM_CHARSET)?;
self.state = State::StateSendUssd1;
} else if self.registered && self.disconnect_pending {
self.disconnect_pending = false;
info!("[Sim800L] Disconnecting");
self.send_cmd(HANG_UP_CALL)?;
} else if self.registered && self.call_state != CallState::Disconnect {
self.send_cmd(LIST_CURRENT_CALLS)?;
self.state = State::StateCheckCall;
return Ok(());
} else if self.pin_locked {
self.send_cmd(CHECK_PIN_PROTECTED)?;
self.state = State::StateCheckPin;
return Ok(());
} else {
self.send_cmd(AT)?;
self.state = State::StateSetupCmgf;
}
self.expect_ack = true;
} else if self.state == State::StateSmsReceived {
self.push_cmd(DELETE_MESSAGE)?;
self.push_cmd(format!(12; "{}", self.parse_index)?.as_str())?;
self.confirm_cmd()?;
self.state = State::StateCheckSms;
self.expect_ack = true;
}
Ok(())
}
pub fn tick(&mut self) -> Result<(), SimError<U>> {
if self.state == State::StatePoweredDown {
return Err(SimError::PoweredDown);
}
let mut byte = [0];
let uart = unsafe { &mut *self.uart.get() };
while uart.read_ready().is_ok_and(|r| r)
&& uart.read(&mut byte).map_err(SimError::IOError)? > 0
{
let mut byte = byte[0];
if self.uart_buffer_idx == SIM800L_READ_BUFFER_LENGTH {
self.uart_buffer_idx = 0;
}
if byte == ASCII_CR as u8 {
continue;
}
if byte >= 0x7F {
byte = '?' as u8; }
self.uart_buffer[self.uart_buffer_idx] = byte;
if self.state == State::StateSendingSms2
&& self.uart_buffer_idx == 0
&& byte == '>' as u8
{
self.uart_buffer_idx += 1;
self.uart_buffer[self.uart_buffer_idx] = ASCII_LF as u8;
}
if self.uart_buffer[self.uart_buffer_idx] == ASCII_LF as u8 {
trace!(
"End of response: {} - {}",
&self.uart_buffer[..self.uart_buffer_idx],
unsafe { str::from_utf8_unchecked(&self.uart_buffer[..self.uart_buffer_idx]) }
);
state_machine::parse_cmd(self)?;
self.uart_buffer_idx = 0;
} else {
self.uart_buffer_idx += 1;
}
}
if self.state == State::StateInit
&& self.registered
&& (self.call_state != CallState::Disconnect || self.send_pending || self.dial_pending || self.connect_pending || self.disconnect_pending)
{
self.update()?;
}
Ok(())
}
pub fn power_off_normally(&mut self) -> Result<(), SimError<U>> {
self.send_cmd("AT+CPOWD=1")?;
self.state = State::StatePoweringDown;
Ok(())
}
pub unsafe fn power_off_urgently(&mut self) -> Result<(), SimError<U>> {
self.send_cmd("AT+CPOWD=0")?;
self.reset();
Ok(())
}
pub fn enable_full_functionality_mode(&mut self) -> Result<(), SimError<U>> {
self.send_cmd("AT+CFUN=1")
}
pub fn enable_minimum_functionality_mode(&mut self) -> Result<(), SimError<U>> {
self.send_cmd("AT+CFUN=0")
}
pub fn enable_flight_mode(&mut self) -> Result<(), SimError<U>> {
self.send_cmd("AT+CFUN=4")
}
pub fn enqueue_sms(&mut self, recipient: &str, sms: &'a str) -> Result<(), SimError<U>> {
if !self.outgoing_smses.push((
String::from_str(recipient).unwrap(),
String::from_str(sms).unwrap(),
)) {
return Err(SimError::SmsQueueLimitReached);
}
self.send_pending = true;
Ok(())
}
pub fn send_ussd(&mut self, ussd: &str) -> Result<(), SimError<U>> {
self.ussd.clear();
self.ussd.push_str(ussd)?;
self.send_ussd_pending = true;
self.update()
}
pub fn dial(&mut self, number: &str) -> Result<(), SimError<U>> {
self.recipient.clear();
self.recipient.push_str(number)?;
self.dial_pending = true;
Ok(())
}
pub(crate) fn answer_call(&mut self) {
self.connect_pending = true;
}
pub fn hang_up_call(&mut self) {
self.disconnect_pending = true;
}
}