1use core::fmt;
2use core::fmt::{Debug, Write};
3use drogue_network::addr::{IpAddr, Ipv4Addr, SocketAddr};
4use heapless::{
5 String,
6 consts::{
7 U128,
8 }
9};
10
11#[derive(Debug)]
12pub struct ResolverAddresses {
13 pub resolver1: Ipv4Addr,
14 pub resolver2: Option<Ipv4Addr>,
15}
16
17#[derive(Debug)]
19pub enum ConnectionType {
20 TCP,
21 UDP,
22}
23
24#[derive(Debug)]
26pub enum WiFiMode {
27 Station,
29 SoftAccessPoint,
31 SoftAccessPointAndStation,
33}
34
35#[derive(Debug)]
37pub enum Command<'a> {
38 QueryFirmwareInfo,
39 SetMode(WiFiMode),
40 JoinAp { ssid: &'a str, password: &'a str },
41 QueryIpAddress,
42 StartConnection(usize, ConnectionType, SocketAddr),
43 CloseConnection(usize),
44 Send { link_id: usize, len: usize },
45 Receive { link_id: usize, len: usize },
46 QueryDnsResolvers,
47 SetDnsResolvers(ResolverAddresses),
48 GetHostByName{ hostname: &'a str},
49}
50
51impl<'a> Command<'a> {
52 pub fn as_bytes(&self) -> String<U128> {
53 match self {
54 Command::QueryFirmwareInfo => String::from("AT+GMR"),
55 Command::QueryIpAddress => String::from("AT+CIPSTA_CUR?"),
56 Command::SetMode(mode)=> match mode {
57 WiFiMode::Station => String::from("AT+CWMODE_CUR=1"),
58 WiFiMode::SoftAccessPoint => String::from("AT+CWMODE_CUR=2"),
59 WiFiMode::SoftAccessPointAndStation => String::from("AT+CWMODE_CUR=3"),
60 }
61 Command::JoinAp { ssid, password } => {
62 let mut s = String::from("AT+CWJAP_CUR=\"");
63 s.push_str(ssid).unwrap();
64 s.push_str("\",\"").unwrap();
65 s.push_str(password).unwrap();
66 s.push_str("\"").unwrap();
67 s
68 }
69 Command::StartConnection(link_id, connection_type, socket_addr) => {
70 let mut s = String::from("AT+CIPSTART=");
71 write!(s, "{},", link_id).unwrap();
72 match connection_type {
73 ConnectionType::TCP => {
74 write!(s, "\"TCP\"").unwrap();
75 }
76 ConnectionType::UDP => {
77 write!(s, "\"UDP\"").unwrap();
78 }
79 }
80 write!(s, ",").unwrap();
81 match socket_addr.ip() {
82 IpAddr::V4(ip) => {
83 let octets = ip.octets();
84 write!(
85 s,
86 "\"{}.{}.{}.{}\",{}",
87 octets[0],
88 octets[1],
89 octets[2],
90 octets[3],
91 socket_addr.port()
92 )
93 .unwrap();
94 }
95 IpAddr::V6(_) => panic!("IPv6 not supported"),
96 }
97 s as String<U128>
98 }
99 Command::CloseConnection(link_id) => {
100 let mut s = String::from("AT+CIPCLOSE=");
101 write!(s, "{}", link_id).unwrap();
102 s
103 }
104 Command::Send { link_id, len } => {
105 let mut s = String::from("AT+CIPSEND=");
106 write!(s, "{},{}", link_id, len).unwrap();
107 s
108 }
109 Command::Receive { link_id, len } => {
110 let mut s = String::from("AT+CIPRECVDATA=");
111 write!(s, "{},{}", link_id, len).unwrap();
112 s
113 }
114 Command::QueryDnsResolvers => {
115 String::from("AT+CIPDNS_CUR?")
116 }
117 Command::SetDnsResolvers(addr) => {
118 let mut s = String::from("AT+CIPDNS_CUR=1,");
119 write!(s, "\"{}\"", addr.resolver1).unwrap();
120 if let Some(resolver2) = addr.resolver2 {
121 write!(s, ",\"{}\"", resolver2 ).unwrap()
122 }
123 s
124 }
125 Command::GetHostByName { hostname } => {
126 let mut s = String::from("AT+CIPDOMAIN=");
127 write!(s, "\"{}\"", hostname).unwrap();
128 s
129 }
130 }
131 }
132}
133
134#[allow(clippy::large_enum_variant)]
136pub enum Response {
137 None,
138 Ok,
139 Error,
140 FirmwareInfo(FirmwareInfo),
141 ReadyForData,
142 ReceivedDataToSend(usize),
143 SendOk,
144 SendFail,
145 DataAvailable { link_id: usize, len: usize },
146 DataReceived([u8; crate::BUFFER_LEN], usize),
147 WifiConnected,
148 WifiConnectionFailure(WifiConnectionFailure),
149 WifiDisconnect,
150 GotIp,
151 IpAddresses(IpAddresses),
152 Connect(usize),
153 Closed(usize),
154 Resolvers(ResolverAddresses),
155 IpAddress(IpAddr),
156 DnsFail,
157 UnlinkFail,
158}
159
160impl Debug for Response {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 match self {
163 Response::None => f.write_str("None"),
164 Response::Ok => f.write_str("Ok"),
165 Response::Error => f.write_str("Error"),
166 Response::FirmwareInfo(v) => f.debug_tuple("FirmwareInfo").field(v).finish(),
167 Response::ReadyForData => f.write_str("ReadyForData"),
168 Response::ReceivedDataToSend(len) => f.debug_tuple("ReceivedDataToSend").field(len).finish(),
169 Response::SendOk =>f.write_str("SendOk"),
170 Response::SendFail => f.write_str("SendFail"),
171 Response::DataAvailable { link_id, len } => f
172 .debug_struct("DataAvailable")
173 .field("link_id", link_id)
174 .field("len", len)
175 .finish(),
176 Response::DataReceived(_, _) => f.write_str("DataReceived"),
178 Response::WifiConnected => f.write_str("WifiConnected"),
179 Response::WifiConnectionFailure(v) => {
180 f.debug_tuple("WifiConnectionFailure").field(v).finish()
181 }
182 Response::WifiDisconnect => f.write_str("WifiDisconnect"),
183 Response::GotIp => f.write_str("GotIp"),
184 Response::IpAddresses(v) => f.debug_tuple("IpAddresses").field(v).finish(),
185 Response::Connect(v) => f.debug_tuple("Connect").field(v).finish(),
186 Response::Closed(v) => f.debug_tuple("Closed").field(v).finish(),
187 Response::IpAddress( v) => f.debug_tuple( "IpAddress").field(v).finish(),
188 Response::Resolvers(v) => f.debug_tuple( "Resolvers").field(v).finish(),
189 Response::DnsFail => f.write_str("DNS Fail"),
190 Response::UnlinkFail => f.write_str("UnlinkFail"),
191 }
192 }
193}
194
195#[derive(Debug)]
197pub struct IpAddresses {
198 pub ip: Ipv4Addr,
199 pub gateway: Ipv4Addr,
200 pub netmask: Ipv4Addr,
201}
202
203#[derive(Debug)]
205pub struct FirmwareInfo {
206 pub major: u8,
207 pub minor: u8,
208 pub patch: u8,
209 pub build: u8,
210}
211
212#[derive(Debug)]
214pub enum WifiConnectionFailure {
215 Timeout,
216 WrongPassword,
217 CannotFindTargetAp,
218 ConnectionFailed,
219}
220
221
222
223impl From<u8> for WifiConnectionFailure {
224 fn from(code: u8) -> Self {
225 match code {
226 1 => WifiConnectionFailure::Timeout,
227 2 => WifiConnectionFailure::WrongPassword,
228 3 => WifiConnectionFailure::CannotFindTargetAp,
229 _ => WifiConnectionFailure::ConnectionFailed,
230 }
231 }
232}
233
234#[allow(dead_code)]
239fn dump_data(name: &str, data: &[u8], len: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240 let data = &data[0..len];
241
242 f.write_str(name)?;
243 f.write_char('(')?;
244
245 f.write_fmt(format_args!("{}; '", len))?;
246
247 for d in data {
248 if *d == 0 {
249 f.write_str("\\0")?;
250 } else if *d <= 0x7F {
251 f.write_char(*d as char)?;
252 } else {
253 f.write_char('\u{FFFD}')?;
254 }
255 }
256
257 f.write_str("'; ")?;
258 f.write_fmt(format_args!("{:X?}", data))?;
259 f.write_char(')')?;
260
261 Ok(())
262}
263
264#[cfg(test)]
265mod test {
266 use super::*;
267 use arrayvec::ArrayString;
268 use core::fmt::Write;
269
270 #[test]
271 fn test_debug_no_value() {
272 let mut buf = ArrayString::<[u8; 20]>::new();
273
274 write!(&mut buf, "{:?}", Response::Ok).expect("Can't write");
275 assert_eq!(&buf, "Ok");
276 }
277
278 #[test]
279 fn test_debug_simple_value() {
280 let mut buf = ArrayString::<[u8; 20]>::new();
281
282 write!(&mut buf, "{:?}", Response::Connect(1)).expect("Can't write");
283 assert_eq!(&buf, "Connect(1)");
284 }
285
286 #[test]
287 fn test_debug_data() {
288 let mut buf = ArrayString::<[u8; 256]>::new();
289 let data = b"FOO\0BAR";
290
291 let mut array = [0u8; 128];
292 for (&x, p) in data.iter().zip(array.iter_mut()) {
293 *p = x;
294 }
295
296 write!(&mut buf, "{:?}", Response::DataReceived(array, data.len())).expect("Can't write");
297 assert_eq!(
298 &buf,
299 "DataReceived(7; 'FOO\\0BAR'; [46, 4F, 4F, 0, 42, 41, 52])"
300 );
301 }
302}