eculocate

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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 }