1use crate::{error::Error, send_at, LteLink};
2use arrayvec::{ArrayString, ArrayVec};
3use core::{fmt::Write, write};
4
5const ASCII_TO_7BIT_TABLE: [u8; 256] = [
9 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0A, 0x20, 0x20, 0x0D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26,
14 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xBC, 0xAF, 0xBE, 0x94, 0x11, 0x27, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA8, 0xC0, 0xA9, 0xBD, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
31 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x63, 0x01, 0xE5, 0x03, 0x53,
32 0x5F, 0x73, 0x63, 0x20, 0x20, 0x20, 0x2D, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5A, 0x75, 0x0A, 0x20, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x59, 0x60, 0x41, 0x41, 0x41, 0x41, 0x5B, 0x0E, 0x1C, 0x09, 0x45, 0x1F, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, 0x44, 0x5D, 0x4F, 0x4F, 0x4F, 0x4F, 0x5C, 0x2A, 0x0B, 0x55, 0x55, 0x55, 0x5E, 0x59, 0x20, 0x1E, 0x7F, 0x61, 0x61, 0x61, 0x7B, 0x0F, 0x1D, 0x63, 0x04, 0x05, 0x65, 0x65, 0x07, 0x69, 0x69, 0x69, 0x20, 0x7D, 0x08, 0x6F, 0x6F, 0x6F, 0x7C, 0x2F, 0x0C, 0x06, 0x75, 0x75, 0x7E, 0x79, 0x20, 0x79, ];
45
46const STR_7BIT_ESCAPE_IND: u8 = 0x80;
48const STR_7BIT_CODE_MASK: u8 = 0x7F;
49const STR_7BIT_ESCAPE_CODE: u8 = 0x1B;
50
51pub struct Sms<'a> {
53 number: &'a str,
54 message: &'a str,
55}
56
57impl<'a> Sms<'a> {
58 pub fn new(number: &'a str, message: &'a str) -> Self {
62 Self { number, message }
63 }
64 fn encode_number(number: &str) -> Result<ArrayString<15>, Error> {
67 let mut number: ArrayString<15> = ArrayString::from(number.trim_start_matches('+'))
68 .map_err(|_| Error::BufferTooSmall(None))?;
69
70 if number.len() % 2 != 0 {
71 number
72 .try_push('F')
73 .map_err(|_| Error::BufferTooSmall(None))?;
74 }
75
76 if number.is_ascii() {
77 let mut swapped_number = ArrayString::from_byte_string(
78 &number
79 .as_bytes()
80 .chunks(2)
81 .flat_map(|c| [c[1], c[0]])
82 .chain((0..15 - number.len()).map(|_| 0))
83 .collect::<ArrayVec<u8, 15>>()
84 .into_inner()
85 .unwrap(),
86 )
87 .unwrap();
88 swapped_number.truncate(number.len());
89
90 Ok(swapped_number)
91 } else {
92 Err(Error::SmsNumberNotAscii)
93 }
94 }
95 fn ascii_to_gsm7bit<const N: usize>(text: &str) -> Result<ArrayString<N>, Error> {
98 let mut encoded_message = ArrayString::new();
99
100 for c in text.chars() {
101 if c.is_ascii() {
102 let char_7bit = ASCII_TO_7BIT_TABLE[c as usize];
103 if char_7bit & STR_7BIT_ESCAPE_IND == 0 {
104 encoded_message
105 .try_push(char_7bit as char)
106 .map_err(|_| Error::BufferTooSmall(None))?;
107 } else {
108 encoded_message
109 .try_push(STR_7BIT_ESCAPE_CODE as char)
110 .map_err(|_| Error::BufferTooSmall(None))?;
111 encoded_message
112 .try_push((char_7bit & STR_7BIT_CODE_MASK) as char)
113 .map_err(|_| Error::BufferTooSmall(None))?;
114 }
115 }
116 }
117
118 Ok(encoded_message)
119 }
120 fn pack_gsm7bit<const N: usize>(text: ArrayString<N>) -> ArrayVec<u8, N> {
123 let mut src: usize = 0;
124 let mut dst: usize = 0;
125 let mut shift: usize = 0;
126 let len = text.len();
127 let mut bytes: ArrayVec<u8, N> = ArrayVec::new();
128 bytes.try_extend_from_slice(text.as_bytes()).unwrap();
129
130 while src < len {
131 bytes[dst] = bytes[src] >> shift;
132 src += 1;
133 if src < len {
134 bytes[dst] |= bytes[src] << (7 - shift);
135 shift += 1;
136 if shift == 7 {
137 shift = 0;
138 src += 1;
139 }
140 }
141 dst += 1;
142 }
143 bytes.truncate(dst);
144 bytes
145 }
146 pub async fn send<const N: usize>(self) -> Result<(), Error> {
150 let encoded_number = Self::encode_number(self.number).unwrap();
151
152 #[cfg(feature = "defmt")]
153 defmt::trace!("encoded_number: {}", encoded_number.as_str());
154
155 let encoded_message = Self::pack_gsm7bit(Self::ascii_to_gsm7bit::<N>(self.message)?);
156
157 let size = 2 + 1 + 1 + encoded_number.len()/2 +
161 2 + 1 + encoded_message.len();
164
165 let mut at_cmgs: ArrayString<N> = ArrayString::new();
166 let mut encoded_number_len = encoded_number.len();
167 if self.number.trim_start_matches('+').len() % 2 != 0 {
168 encoded_number_len -= 1;
169 }
170 write!(
172 &mut at_cmgs,
173 "AT+CMGS={}\r{:04X}{:04X}91{}",
174 size, 0x01, encoded_number_len, encoded_number
175 )
176 .map_err(|_| Error::BufferTooSmall(None))?;
177 write!(&mut at_cmgs, "00{:04X}", self.message.len())
179 .map_err(|_| Error::BufferTooSmall(None))?;
180 for c in &encoded_message {
182 write!(&mut at_cmgs, "{c:02X}").map_err(|_| Error::BufferTooSmall(None))?;
183 }
184 write!(&mut at_cmgs, "\x1A").map_err(|_| Error::BufferTooSmall(None))?;
186
187 #[cfg(feature = "defmt")]
188 defmt::trace!("at_cmgs: {:?}", at_cmgs.as_str());
189
190 let lte_link = LteLink::new().await?;
192 lte_link.wait_for_link().await?;
193
194 #[cfg(feature = "defmt")]
195 defmt::trace!("link found");
196
197 if send_at::<6>("AT+CNMI=3,2,0,1").await?.as_str() != "OK\r\n" {
200 return Err(Error::UnexpectedAtResponse);
201 }
202
203 let result = send_at::<18>(&at_cmgs).await?;
205
206 #[cfg(feature = "defmt")]
207 defmt::trace!("result: {}", result.as_str());
208
209 lte_link.deactivate().await?;
210 if result.ends_with("OK\r\n") {
211 Ok(())
212 } else {
213 Err(Error::UnexpectedAtResponse)
214 }
215 }
216}