seeed_erpc/
wifi_rpcs.rs

1#[allow(dead_code)]
2use super::{codec, ids, Err};
3use generic_array::{ArrayLength, GenericArray};
4use heapless::String;
5use nom::{bytes::streaming::take, number::streaming, Input};
6
7/// Returns the mac address as a colon-separated hex string.
8pub struct GetMacAddress {}
9
10impl super::RPC for GetMacAddress {
11    type ReturnValue = String<18>;
12    type Error = i32;
13
14    fn header(&self, seq: u32) -> codec::Header {
15        codec::Header {
16            sequence: seq,
17            msg_type: ids::MsgType::Invocation,
18            service: ids::Service::Wifi,
19            request: ids::WifiRequest::GetMacAddress.into(),
20        }
21    }
22
23    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
24        let (data, hdr) = codec::Header::parse(data)?;
25        if hdr.msg_type != ids::MsgType::Reply
26            || hdr.service != ids::Service::Wifi
27            || hdr.request != ids::WifiRequest::GetMacAddress.into()
28        {
29            return Err(Err::NotOurs);
30        }
31
32        if data.input_len() < 18 {
33            return Err(Err::RPCErr(-1));
34        }
35        let mut mac: String<18> = String::new();
36        for b in data.take(17).iter_elements() {
37            mac.push(b as char).map_err(|_| Err::ResponseOverrun)?;
38        }
39
40        let (_, result) = streaming::le_u32(data.take_from(18))?;
41        if result != 0 {
42            Err(Err::RPCErr(result as i32))
43        } else {
44            Ok(mac)
45        }
46    }
47}
48
49/// Returns true if the wifi chip is currently scanning.
50pub struct IsScanning {}
51
52impl super::RPC for IsScanning {
53    type ReturnValue = bool;
54    type Error = ();
55
56    fn header(&self, seq: u32) -> codec::Header {
57        codec::Header {
58            sequence: seq,
59            msg_type: ids::MsgType::Invocation,
60            service: ids::Service::Wifi,
61            request: ids::WifiRequest::IsScanning.into(),
62        }
63    }
64
65    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
66        let (data, hdr) = codec::Header::parse(data)?;
67        if hdr.msg_type != ids::MsgType::Reply
68            || hdr.service != ids::Service::Wifi
69            || hdr.request != ids::WifiRequest::IsScanning.into()
70        {
71            return Err(Err::NotOurs);
72        }
73
74        if data.input_len() < 1 {
75            return Err(Err::RPCErr(()));
76        }
77        Ok(data.iter_elements().nth(0) != Some(0))
78    }
79}
80
81/// Describes a wifi network or station discovered via scanning.
82#[derive(Copy, Clone)]
83pub struct ScanResult {
84    /// Service Set Identification (i.e. Name of Access Point)
85    pub ssid: super::SSID,
86    /// Basic Service Set Identification (i.e. MAC address of Access Point)
87    pub bssid: super::BSSID,
88    /// Receive Signal Strength Indication in dBm. <-90=poor, >-30=Excellent
89    pub rssi: i16,
90    /// Network type
91    pub bss_type: super::BssType,
92    /// Security type
93    pub security: super::Security,
94    /// WPS type
95    pub wps: super::WPS,
96    /// Channel
97    pub chan: u32,
98    /// Radio channel that the AP beacon was received on
99    pub band: super::Band,
100}
101
102impl core::fmt::Debug for ScanResult {
103    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104        // Unused unsafe warning is erroneous: needed for safe_packed_borrows
105        #[allow(unused_unsafe)]
106        unsafe {
107            if self.ssid.len > 0 {
108                f.debug_struct("ScanResult")
109                    .field("ssid", &self.ssid)
110                    .field("bssid", &self.bssid)
111                    .field("rssi", &self.rssi)
112                    .field("type", &self.bss_type)
113                    .field("security", &self.security)
114                    .field("wps", &self.wps)
115                    .field("channel", &self.chan)
116                    .field("band", &self.band)
117                    .finish()
118            } else {
119                f.debug_struct("ScanResult")
120                    .field("bssid", &self.bssid)
121                    .field("rssi", &self.rssi)
122                    .field("type", &self.bss_type)
123                    .field("security", &self.security)
124                    .field("wps", &self.wps)
125                    .field("channel", &self.chan)
126                    .field("band", &self.band)
127                    .finish()
128            }
129        }
130    }
131}
132
133impl Default for ScanResult {
134    fn default() -> Self {
135        Self {
136            ssid: super::SSID {
137                len: 0,
138                value: [0u8; 33],
139            },
140            bssid: super::BSSID([0u8; 6]),
141            rssi: 0,
142            bss_type: super::BssType::Any,
143            security: super::Security::empty(),
144            wps: super::WPS::Default,
145            chan: 0,
146            band: super::Band::_24Ghz,
147        }
148    }
149}
150
151/// Returns N number of scan results. This RPC must only be called after starting a
152/// scan, and after IsScanning returns false.
153pub struct ScanGetAP<N: ArrayLength<ScanResult>> {
154    m: core::marker::PhantomData<N>,
155}
156
157impl<N: ArrayLength<ScanResult>> ScanGetAP<N> {
158    pub fn new() -> Self {
159        Self {
160            m: core::marker::PhantomData,
161        }
162    }
163}
164
165impl<N: ArrayLength<ScanResult>> super::RPC for ScanGetAP<N> {
166    type ReturnValue = (GenericArray<ScanResult, N>, i32);
167    type Error = usize;
168
169    fn header(&self, seq: u32) -> codec::Header {
170        codec::Header {
171            sequence: seq,
172            msg_type: ids::MsgType::Invocation,
173            service: ids::Service::Wifi,
174            request: ids::WifiRequest::ScanGetAP.into(),
175        }
176    }
177
178    fn args(&self, buff: &mut heapless::Vec<u8, 64>) {
179        let num = N::to_u16().to_le_bytes();
180        buff.extend_from_slice(&num).ok();
181    }
182
183    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
184        let (data, hdr) = codec::Header::parse(data)?;
185        if hdr.msg_type != ids::MsgType::Reply
186            || hdr.service != ids::Service::Wifi
187            || hdr.request != ids::WifiRequest::ScanGetAP.into()
188        {
189            return Err(Err::NotOurs);
190        }
191
192        let (mut data, l) = streaming::le_u32(data)?; // Binary len - returning 62 bytes per result
193        if l as usize != (62 * N::to_usize()) {
194            return Err(Err::ResponseOverrun);
195        }
196
197        use core::convert::TryInto;
198        let mut res = GenericArray::<ScanResult, N>::default();
199        for i in 0..N::to_usize() {
200            let (d, ssid_len) = streaming::le_u8(data)?;
201            let (d, ssid_data) = take(33usize)(d)?;
202            let (d, bssid) = take(6usize)(d)?;
203            let (d, rssi) = streaming::le_i16(d)?;
204            let (d, bss_type) = streaming::le_u32(d)?;
205            let (d, security) = streaming::le_u32(d)?;
206            let (d, wps) = streaming::le_u32(d)?;
207            let (d, chan) = streaming::le_u32(d)?;
208            let (d, band) = streaming::le_u32(d)?;
209
210            res[i] = ScanResult {
211                ssid: super::SSID {
212                    len: ssid_len,
213                    value: ssid_data.try_into().unwrap(),
214                },
215                bssid: super::BSSID(bssid.try_into().unwrap()),
216                rssi,
217                bss_type: bss_type.into(),
218                security: super::Security::from_bits_truncate(security),
219                wps: wps.into(),
220                chan,
221                band: band.into(),
222            };
223            data = d;
224        }
225
226        let (_, ret_val) = streaming::le_i32(data)?;
227        Ok((res, ret_val))
228    }
229}
230
231/// Returns the number of APs which were detected.
232pub struct ScanGetNumAPs {}
233
234impl super::RPC for ScanGetNumAPs {
235    type ReturnValue = u16;
236    type Error = ();
237
238    fn header(&self, seq: u32) -> codec::Header {
239        codec::Header {
240            sequence: seq,
241            msg_type: ids::MsgType::Invocation,
242            service: ids::Service::Wifi,
243            request: ids::WifiRequest::ScanGetNumAPs.into(),
244        }
245    }
246
247    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
248        let (data, hdr) = codec::Header::parse(data)?;
249        if hdr.msg_type != ids::MsgType::Reply
250            || hdr.service != ids::Service::Wifi
251            || hdr.request != ids::WifiRequest::ScanGetNumAPs.into()
252        {
253            return Err(Err::NotOurs);
254        }
255
256        if data.input_len() < 2 {
257            return Err(Err::RPCErr(()));
258        }
259        let (_, num) = streaming::le_u16(data)?;
260        Ok(num)
261    }
262}
263
264/// Initiates a network scan. A return value of 0 indicates success afaict.
265pub struct ScanStart {}
266
267impl super::RPC for ScanStart {
268    type ReturnValue = i32;
269    type Error = ();
270
271    fn header(&self, seq: u32) -> codec::Header {
272        codec::Header {
273            sequence: seq,
274            msg_type: ids::MsgType::Invocation,
275            service: ids::Service::Wifi,
276            request: ids::WifiRequest::ScanStart.into(),
277        }
278    }
279
280    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
281        let (data, hdr) = codec::Header::parse(data)?;
282        if hdr.msg_type != ids::MsgType::Reply
283            || hdr.service != ids::Service::Wifi
284            || hdr.request != ids::WifiRequest::ScanStart.into()
285        {
286            return Err(Err::NotOurs);
287        }
288
289        let (_, num) = streaming::le_i32(data)?;
290        Ok(num)
291    }
292}
293
294/// Turns on Wifi.
295pub struct WifiOn {
296    pub mode: super::WifiMode,
297}
298
299impl super::RPC for WifiOn {
300    type ReturnValue = i32;
301    type Error = ();
302
303    fn args(&self, buff: &mut heapless::Vec<u8, 64>) {
304        let mode = self.mode as u32;
305        buff.extend_from_slice(&mode.to_le_bytes()).ok();
306    }
307
308    fn header(&self, seq: u32) -> codec::Header {
309        codec::Header {
310            sequence: seq,
311            msg_type: ids::MsgType::Invocation,
312            service: ids::Service::Wifi,
313            request: ids::WifiRequest::TurnOn.into(),
314        }
315    }
316
317    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
318        let (data, hdr) = codec::Header::parse(data)?;
319        if hdr.msg_type != ids::MsgType::Reply
320            || hdr.service != ids::Service::Wifi
321            || hdr.request != ids::WifiRequest::TurnOn.into()
322        {
323            return Err(Err::NotOurs);
324        }
325
326        let (_, num) = streaming::le_i32(data)?;
327        Ok(num)
328    }
329}
330
331/// Turns off Wifi.
332pub struct WifiOff {}
333
334impl super::RPC for WifiOff {
335    type ReturnValue = i32;
336    type Error = ();
337
338    fn header(&self, seq: u32) -> codec::Header {
339        codec::Header {
340            sequence: seq,
341            msg_type: ids::MsgType::Invocation,
342            service: ids::Service::Wifi,
343            request: ids::WifiRequest::TurnOff.into(),
344        }
345    }
346
347    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
348        let (data, hdr) = codec::Header::parse(data)?;
349        if hdr.msg_type != ids::MsgType::Reply
350            || hdr.service != ids::Service::Wifi
351            || hdr.request != ids::WifiRequest::TurnOff.into()
352        {
353            return Err(Err::NotOurs);
354        }
355
356        let (_, num) = streaming::le_i32(data)?;
357        Ok(num)
358    }
359}
360
361/// Connects to the network with the provided properties.
362pub struct WifiConnect {
363    pub ssid: String<64>,
364    pub password: String<64>,
365    pub security: super::Security,
366    //key_id: u32,
367    pub semaphore: u32,
368}
369
370impl super::RPC for WifiConnect {
371    type ReturnValue = i32;
372    type Error = ();
373
374    fn args(&self, buff: &mut heapless::Vec<u8, 64>) {
375        buff.extend_from_slice(&(self.ssid.len() as u32).to_le_bytes())
376            .ok();
377        buff.extend_from_slice(self.ssid.as_ref()).ok();
378
379        // Write the nullable flag (0 = NotNull, 1 = Null)
380        buff.push(if self.password.len() > 0 { 0u8 } else { 1u8 })
381            .ok();
382        if self.password.len() > 0 {
383            buff.extend_from_slice(&(self.password.len() as u32).to_le_bytes())
384                .ok();
385            buff.extend_from_slice(self.password.as_ref()).ok();
386        }
387
388        buff.extend_from_slice(&(self.security.bits()).to_le_bytes())
389            .ok();
390        buff.extend_from_slice(&(0u32.wrapping_sub(1)).to_le_bytes())
391            .ok(); // key_id - always -1?
392        buff.extend_from_slice(&(self.semaphore).to_le_bytes()).ok();
393    }
394
395    fn header(&self, seq: u32) -> codec::Header {
396        codec::Header {
397            sequence: seq,
398            msg_type: ids::MsgType::Invocation,
399            service: ids::Service::Wifi,
400            request: ids::WifiRequest::Connect.into(),
401        }
402    }
403
404    fn parse(&mut self, data: &[u8]) -> Result<Self::ReturnValue, Err<Self::Error>> {
405        let (data, hdr) = codec::Header::parse(data)?;
406        if hdr.msg_type != ids::MsgType::Reply
407            || hdr.service != ids::Service::Wifi
408            || hdr.request != ids::WifiRequest::Connect.into()
409        {
410            return Err(Err::NotOurs);
411        }
412
413        let (_, num) = streaming::le_i32(data)?;
414        Ok(num)
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421    use crate::RPC;
422
423    fn init() {
424        let _ = env_logger::builder().is_test(true).try_init();
425    }
426
427    #[test]
428    fn mac_address() {
429        init();
430        let mut input_bytes = [42u8; 36];
431
432        input_bytes[0] = 2; // MsgType::Reply
433        input_bytes[1] = 8; // WifiRequest::GetMacAddress
434        input_bytes[2] = 14; // Service::Wifi
435
436        input_bytes[8..30].copy_from_slice(b"01:23:45:67:89:01x\0\0\0\0");
437
438        let parsed = GetMacAddress {}.parse(&input_bytes).expect("Parse failed");
439
440        assert_eq!(parsed, "01:23:45:67:89:01");
441    }
442}