From 1a07b38efa01d6c27b55679ba8c0f5d33f411267 Mon Sep 17 00:00:00 2001 From: Tamas Domok Date: Mon, 5 Jan 2026 19:41:01 +0100 Subject: [PATCH] pico: added sms.rs (untested on hardware) --- pico/app/src/main.rs | 12 +++- pico/pico-lib/src/call.rs | 8 +-- pico/pico-lib/src/lib.rs | 1 + pico/pico-lib/src/sms.rs | 133 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 pico/pico-lib/src/sms.rs diff --git a/pico/app/src/main.rs b/pico/app/src/main.rs index ca69bb3..ec84ae9 100644 --- a/pico/app/src/main.rs +++ b/pico/app/src/main.rs @@ -22,7 +22,7 @@ use {defmt_rtt as _, panic_probe as _}; use pico_lib::at::PicoHW; use pico_lib::poro; use pico_lib::urc; -use pico_lib::{at, call, network}; +use pico_lib::{at, call, network, sms}; extern crate alloc; @@ -143,12 +143,20 @@ async fn main(spawner: Spawner) { call::call_number( &mut client, - "+36301234567", &mut pico, + "+36301234567", Duration::from_secs(6).as_millis(), ) .await; + sms::send_sms( + &mut client, + &mut pico, + "+36301234567", + "this is a text message", + ) + .await; + let mut counter = 0u8; loop { pico.set_led_high(); diff --git a/pico/pico-lib/src/call.rs b/pico/pico-lib/src/call.rs index 67bea1d..d85cd46 100644 --- a/pico/pico-lib/src/call.rs +++ b/pico/pico-lib/src/call.rs @@ -57,8 +57,8 @@ pub struct AtHangup; pub async fn call_number( client: &mut T, - number: &'static str, pico: &mut U, + number: &'static str, duration_millis: u64, ) { { @@ -141,11 +141,11 @@ mod tests { client.results.push_back(Ok("".as_bytes())); let mut pico = crate::at::tests::PicoMock::default(); - call_number(&mut client, "+36301234567", &mut pico, 100).await; + call_number(&mut client, &mut pico, "+36301234567", 100).await; assert_eq!(3, client.sent_commands.len()); assert_eq!("AT+CHFA=1\r", client.sent_commands.get(0).unwrap()); - assert_eq!("AT+CHFA=1\r", client.sent_commands.get(0).unwrap()); - assert_eq!("AT+CHFA=1\r", client.sent_commands.get(0).unwrap()); + assert_eq!("ATD+36301234567,i;\r", client.sent_commands.get(1).unwrap()); + assert_eq!("AT+CHUP;\r", client.sent_commands.get(2).unwrap()); assert_eq!(1, pico.sleep_calls.len()); assert_eq!(100u64, *pico.sleep_calls.get(0).unwrap()); diff --git a/pico/pico-lib/src/lib.rs b/pico/pico-lib/src/lib.rs index a7f6a0b..658a84f 100644 --- a/pico/pico-lib/src/lib.rs +++ b/pico/pico-lib/src/lib.rs @@ -6,5 +6,6 @@ pub mod at; pub mod call; pub mod network; pub mod poro; +pub mod sms; pub mod urc; pub mod utils; diff --git a/pico/pico-lib/src/sms.rs b/pico/pico-lib/src/sms.rs new file mode 100644 index 0000000..973cb09 --- /dev/null +++ b/pico/pico-lib/src/sms.rs @@ -0,0 +1,133 @@ +use alloc::format; +use alloc::string::ToString; +use atat::AtatCmd; +use atat::atat_derive::AtatCmd; +use atat::atat_derive::AtatEnum; +use atat::heapless::String; + +use crate::at::NoResponse; +use crate::utils::LogBE; + +// 4.2.2 AT+CMGF Select SMS Message Format +// AT+CMGF=[] +#[derive(Clone, Debug, AtatCmd)] +#[at_cmd("+CMGF", NoResponse)] +pub struct AtSelectSMSMessageFormatWrite { + pub mode: MessageMode, +} + +#[derive(Debug, Clone, PartialEq, AtatEnum)] +pub enum MessageMode { + PDU = 0, + Text = 1, +} + +// 4.2.5 AT+CMGS Send SMS Message +// AT+CMGS=[,[]]text is entered[ctrl-Z/ESC] +#[derive(Clone, Debug)] +pub struct AtSendSMSWrite { + pub number: String<16>, + pub message: String<160>, +} + +impl<'a> AtatCmd for AtSendSMSWrite { + type Response = NoResponse; + + const MAX_LEN: usize = 16; + + fn write(&self, buf: &mut [u8]) -> usize { + let formatted = format!("AT+CMGS={}\r{}\x1a", self.number, self.message); + let cmd = formatted.as_bytes(); + let len = cmd.len(); + buf[..len].copy_from_slice(cmd); + len + } + + fn parse(&self, _: Result<&[u8], atat::InternalError>) -> Result { + Ok(NoResponse) + } +} + +pub async fn send_sms( + client: &mut T, + _pico: &mut U, // TODO: + number: &'static str, // Bytes<16> ? (same for Call) + message: &'static str, // Bytes<160> ? +) { + { + let _l = LogBE::new("AtSelectSMSMessageFormatWrite".to_string()); + let r = client + .send(&AtSelectSMSMessageFormatWrite { + mode: MessageMode::Text, + }) + .await; + match r { + Ok(_) => log::info!(" OK"), + Err(e) => log::info!(" ERROR: {:?}", e), + } + } + + { + let _l = LogBE::new("AtSendSMSWrite".to_string()); + let r = client + .send(&AtSendSMSWrite { + number: String::<16>::try_from(number).unwrap(), + message: String::<160>::try_from(message).unwrap(), + }) + .await; + match r { + Ok(_) => log::info!(" OK"), + Err(e) => log::info!(" ERROR: {:?}", e), + } + } +} + +#[cfg(test)] +mod tests { + use crate::{at, cmd_serialization_tests}; + + use super::*; + use atat::AtatCmd; + + cmd_serialization_tests! { + test_at_select_sms_message_format_write: ( + AtSelectSMSMessageFormatWrite { + mode: MessageMode::Text, + }, + 10, + "AT+CMGF=1\r", + ), + test_at_send_sms_write: ( + AtSendSMSWrite { + number: String::try_from("+361234567").unwrap(), + message: String::try_from("this is the message content").unwrap(), + }, + 47, + "AT+CMGS=+361234567\rthis is the message content\u{1a}", + ), + } + + #[tokio::test] + async fn test_send_sms() { + at::tests::init_env_logger(); + + let mut client = crate::at::tests::ClientMock::default(); + client.results.push_back(Ok("".as_bytes())); + client.results.push_back(Ok(">".as_bytes())); + + let mut pico = crate::at::tests::PicoMock::default(); + send_sms( + &mut client, + &mut pico, + "+36301234567", + "this is the text message", + ) + .await; + assert_eq!(2, client.sent_commands.len()); + assert_eq!("AT+CMGF=1\r", client.sent_commands.get(0).unwrap()); + assert_eq!( + "AT+CMGS=+36301234567\rthis is the text message\u{1a}", + client.sent_commands.get(1).unwrap() + ); + } +}