wifi.rs (8685B)
1 //! This gets an ip address via DHCP and advertises it with mdns 2 3 use alloc::vec::Vec; 4 use alloc::string::String; 5 use alloc::format; 6 7 use hashbrown::HashMap; 8 9 use ed25519_dalek::{VerifyingKey, Verifier, Signature}; 10 11 use embassy_executor::Spawner; 12 use embassy_net::{ 13 Runner, 14 StackResources 15 }; 16 use embassy_futures::join::join3; 17 use embassy_sync::mutex::Mutex; 18 use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 19 20 use embassy_time::{Duration, Timer, Instant}; 21 use esp_alloc as _; 22 use esp_backtrace as _; 23 use esp_hal::{ 24 rng::Rng, 25 }; 26 use esp_println::println; 27 use esp_radio::wifi::{ 28 ScanConfig, 29 WifiController, 30 WifiDevice, 31 WifiEvent, 32 WifiStaState, 33 WifiError, 34 AccessPointInfo 35 }; 36 37 use embassy_net::Stack; 38 use embedded_io_async::Read; 39 40 use log::{warn, info, error}; 41 42 use crate::mdns; 43 44 // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html 45 macro_rules! mk_static { 46 ($t:ty,$val:expr) => {{ 47 static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); 48 #[deny(unused_attributes)] 49 let x = STATIC_CELL.uninit().write(($val)); 50 x 51 }}; 52 } 53 54 fn dns_records(my_ip_address: [u8;4], serial: Option<usize>) -> Vec<mdns::Resource> { 55 use crate::mdns::*; 56 use crate::mdns::RData as R; 57 58 let (hostname, ptr_name) = 59 match serial { 60 None => ( 61 String::from("eculocate.local"), 62 String::from("Bike._keihin._udp.local") 63 ), 64 Some(number) => ( 65 format!("eculocate{number}.local"), 66 format!("Bike ({number})._keihin._udp.local") 67 ) 68 }; 69 70 71 let records = [ 72 Resource::new(&hostname, R::A(my_ip_address)), 73 Resource::new( 74 &ptr_name, 75 R::Srv { 76 target: String::from(hostname), port: 5000, 77 weight: 0, priority: 0 78 } 79 ), 80 Resource::new(&ptr_name, R::Txt(["txtvers=1".into()].into())), 81 Resource::new("_keihin._udp.local", R::Ptr(ptr_name)), 82 Resource::new(DISCOVERY_NAME, R::Ptr("_keihin._udp.local".into())) 83 ]; 84 Vec::from(records) 85 } 86 87 pub struct SessionStore { 88 map : Mutex<CriticalSectionRawMutex, HashMap<[u8; 32], Instant>>, 89 public_key: VerifyingKey 90 } 91 92 impl SessionStore { 93 pub fn new() -> Self { 94 let m = HashMap::new(); 95 let verifying_key_bytes = include_bytes!("../../pubkey.bin"); 96 let public_key: VerifyingKey = VerifyingKey::from_bytes(verifying_key_bytes).expect("verify"); 97 98 Self { map : Mutex::new(m), public_key } 99 } 100 101 pub async fn register(&self, key: &[u8], signature: &[u8]) -> bool { 102 info!("pretending to register {:?}", key); 103 104 if self.public_key.verify(key, &Signature::try_from(signature).expect("64 bytes")).is_ok() { 105 let mut m = self.map.lock().await; 106 m.insert(key.try_into().unwrap(), Instant::now() + Duration::from_secs(86400)); 107 true 108 } else { 109 warn!("wrong key"); 110 false 111 } 112 } 113 114 pub async fn is_registered(&self, key: &[u8]) -> bool { 115 let m = self.map.lock().await; 116 match m.get(key) { 117 Some(expiry) => *expiry > Instant::now(), 118 None => false 119 } 120 } 121 } 122 123 pub async fn scan_networks(controller: &mut WifiController<'_>) -> Vec<AccessPointInfo> { 124 println!("Scan"); 125 let scan_config = ScanConfig::default().with_max(10); 126 let result = controller 127 .scan_with_config_async(scan_config) 128 .await 129 .expect("scan failed"); 130 result 131 } 132 133 pub async fn start_wifi(spawner: &Spawner, 134 mut controller: WifiController<'static>, 135 wifi_interface: WifiDevice<'static>, 136 mut flasher: crate::ota::Flasher<'static>, 137 ecu: &mut crate::ecu::Ecu<'_>) { 138 139 let config = embassy_net::Config::dhcpv4(Default::default()); 140 141 let rng = Rng::new(); 142 let seed = (rng.random() as u64) << 32 | rng.random() as u64; 143 144 // Init network stack 145 let (stack, runner) = embassy_net::new( 146 wifi_interface, 147 config, 148 mk_static!(StackResources<5>, StackResources::<5>::new()), 149 seed, 150 ); 151 152 if !connect(&mut controller).await.is_ok() { 153 flasher.set_var("ssid", None); 154 flasher.set_var("secret", None); 155 info!("[gatt] deep breath then reboot"); 156 Timer::after(Duration::from_millis(2000)).await; 157 esp_hal::system::software_reset(); 158 } 159 160 spawner.spawn(maintain_connection(controller)).ok(); 161 spawner.spawn(net_task(runner)).ok(); 162 163 let my_address; 164 165 loop { 166 if stack.is_link_up() { 167 break; 168 } 169 Timer::after(Duration::from_millis(500)).await; 170 } 171 172 println!("Waiting to get IP address..."); 173 loop { 174 if let Some(config) = stack.config_v4() { 175 println!("Got IP address: {}", config.address); 176 my_address = config.address.address().octets(); 177 break; 178 } 179 Timer::after(Duration::from_millis(500)).await; 180 } 181 182 let mut serial = None; 183 184 let session_store = SessionStore::new(); 185 186 join3( 187 crate::streamer::loop_udp_thing(stack, ecu, &session_store), 188 run_tcp_handler(stack, &session_store, flasher), 189 async { 190 loop { 191 let mdns_thread = mdns::responder( 192 stack, 193 dns_records(my_address, serial) 194 ); 195 mdns_thread.await; 196 serial = match serial { None => Some(2), Some(n) => Some(n+1) }; 197 info!("probe failed, restarting with incremented serial {:?}", serial); 198 Timer::after(Duration::from_millis(5000)).await; 199 }; 200 } 201 ).await; 202 } 203 204 async fn connect(controller: &mut WifiController<'static>) -> Result<(), WifiError> { 205 let mut attempts = 0; 206 let mut retval = Ok(()); 207 println!("start connecting"); 208 while attempts < 5 { 209 println!("About to connect..."); 210 211 match controller.connect_async().await { 212 Ok(_) => { 213 println!("Wifi connected!"); 214 return retval; 215 }, 216 Err(e) => { 217 println!("Failed to connect to wifi, attempt {attempts}: {e:?}"); 218 attempts += 1; 219 Timer::after(Duration::from_millis(1000)).await; 220 retval = Err(e); 221 } 222 } 223 } 224 retval 225 } 226 227 #[embassy_executor::task] 228 async fn maintain_connection(mut controller: WifiController<'static>) { 229 println!("start connection task"); 230 println!("Device capabilities: {:?}", controller.capabilities()); 231 loop { 232 match esp_radio::wifi::sta_state() { 233 WifiStaState::Connected => { 234 // wait until we're no longer connected 235 controller 236 .wait_for_event(WifiEvent::StaDisconnected) 237 .await; 238 Timer::after(Duration::from_millis(5000)).await 239 } 240 _ => {} 241 } 242 let _ = connect(&mut controller); 243 } 244 } 245 246 #[embassy_executor::task] 247 async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { 248 runner.run().await 249 } 250 251 async fn run_tcp_handler(stack : Stack<'_>, 252 sessions : &SessionStore, 253 mut flasher : crate::ota::Flasher<'_>) { 254 let mut rx_buffer = [0; 4096]; 255 let mut tx_buffer = [0; 4096]; 256 let mut buf = [0u8; 96]; 257 258 loop { 259 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); 260 socket.set_timeout(Some(Duration::from_secs(10))); 261 262 if let Err(e) = socket.accept(5000).await { 263 warn!("error accepting connection: {:?}", e); 264 continue; 265 } 266 267 info!("Received connection from {:?}", socket.remote_endpoint()); 268 269 if let Ok(_) = socket.read_exact(&mut buf[0..4]).await { 270 info!("got a thing"); 271 match &buf[0..4] { 272 b"ROM0" => { 273 info!("OTA request"); 274 if let Err(e) = flasher.write(&mut socket).await { 275 error!("ota flash failed: {:?}", e); 276 } 277 }, 278 b"KEY0" => { 279 info!("sessionkey"); 280 if let Ok(_) = socket.read_exact(&mut buf).await { 281 let (key, sig) = buf.split_at(32); 282 if sessions.register(key, sig).await { 283 info!("registered? {}", sessions.is_registered(key).await); 284 info!("write {:?}", socket.write(b"BYE\n").await); 285 let _ = socket.flush().await; 286 } else { 287 warn!("couldn't register key") 288 } 289 socket.close(); 290 } 291 }, 292 _ => { 293 warn!("unrecognised command"); 294 } 295 296 } 297 } 298 } 299 }