From 4235b5495902db8b7ad9ede0a86798fa39728db0 Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sun, 7 Dec 2025 13:14:07 +0100 Subject: [PATCH] wifi: rewrite wifi task --- Cargo.toml | 5 +- src/bin/main.rs | 75 ++++++++++------- src/tasks/wifi.rs | 203 ++++++++++++++++++++++------------------------ 3 files changed, 143 insertions(+), 140 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9bf309e..8b2c72f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ real-output = [] simulation = ["dep:rmp"] radio = [ "dep:bleps", - "dep:esp-wifi" + "dep:esp-wifi", + "dep:reqwless" ] motion = ["mpu", "gps"] max-usb-power = [] @@ -102,6 +103,8 @@ esp-storage = { version = "0.7.0", features = ["esp32s3"] } embedded-storage = "0.3.1" rmp = { path = "../msgpack-rust/rmp/", optional = true, default-features = false } display-interface = "0.5.0" +embassy-net = { version = "0.7.1", features = ["alloc", "dns", "medium-ethernet", "proto-ipv4", "tcp", "udp", "dhcpv4"] } +reqwless = { version = "0.13.0", optional = true } [profile.dev] # Rust debug is too slow. diff --git a/src/bin/main.rs b/src/bin/main.rs index a19ab39..665defe 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -41,7 +41,7 @@ esp_bootloader_esp_idf::esp_app_desc!(); static STATIC_HI_EXEC: StaticCell> = StaticCell::new(); static BUS_GARAGE: StaticCell = StaticCell::new(); -static mut CORE2_STACK: Stack<8192> = Stack::new(); +static mut CORE2_STACK: Stack<16384> = Stack::new(); static CORE_HANDLE: StaticCell = StaticCell::new(); #[esp_hal_embassy::main] @@ -145,39 +145,52 @@ async fn main(spawner: Spawner) { info!("Launching motion engine"); spawner.must_spawn(motion_task(garage.motion.dyn_receiver(), garage.notify.dyn_publisher().unwrap(), garage.predict.dyn_sender())); + #[cfg(feature="radio")] + let wifi_init = { + info!("Configuring wifi"); + + static WIFI_INIT: StaticCell> = StaticCell::new(); + let rng = esp_hal::rng::Rng::new(peripherals.RNG); + WIFI_INIT.init_with(|| {esp_wifi::init(timer0.timer0, rng).expect("Failed to initialize radio controller")}) + }; + info!("Starting core 2"); let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL); - CORE_HANDLE.init(cpu_control.start_app_core(unsafe { &mut *addr_of_mut!(CORE2_STACK) }, || { - static STATIC_EXEC: StaticCell = StaticCell::new(); - let exec = STATIC_EXEC.init(Executor::new()); - exec.run(|spawner| { - #[cfg(feature="radio")] - { - info!("Launching wifi"); - //spawner.must_spawn(renderbug_embassy::tasks::wifi::wireless_task(garage.notify.dyn_receiver().unwrap(), timer0.timer0.into(), peripherals.RNG, peripherals.WIFI, peripherals.BT)); - } - info!("Launching Safety UI"); - spawner.must_spawn(safety_ui_main(garage.notify.dyn_subscriber().unwrap(), safety_ui)); - info!("Launching UI"); - spawner.must_spawn(ui_main(garage.notify.dyn_subscriber().unwrap(), garage.telemetry.dyn_publisher().unwrap(), ui)); - info!("Launching OLED UI"); - spawner.must_spawn(oled_ui(garage.telemetry.dyn_subscriber().unwrap(), oledui)); - #[cfg(feature="demo")] - { - warn!("Launching with demo sequencer"); - spawner.must_spawn(renderbug_embassy::tasks::demo::demo_task(garage.notify.dyn_publisher().unwrap())); - } - #[cfg(not(feature="demo"))] - { - info!("Launching prediction engine"); - spawner.must_spawn(renderbug_embassy::tasks::predict::prediction_task(garage.predict.dyn_receiver(), garage.notify.dyn_publisher().unwrap(), garage.telemetry.dyn_publisher().unwrap())); - } - info!("Launching core 2 watchdog"); - spawner.must_spawn(wdt_task(ui_wdt)); + CORE_HANDLE.init_with(|| { + cpu_control.start_app_core(unsafe { &mut *addr_of_mut!(CORE2_STACK) }, || { + let exec = CORE2_EXEC.init_with(|| { Executor::new() }); + exec.run(|spawner| { + info!("Launching Safety UI"); + spawner.must_spawn(safety_ui_main(garage.notify.dyn_subscriber().unwrap(), safety_ui)); + info!("Launching UI"); + spawner.must_spawn(ui_main(garage.notify.dyn_subscriber().unwrap(), garage.telemetry.dyn_publisher().unwrap(), ui)); + info!("Launching OLED UI"); + spawner.must_spawn(oled_ui(garage.telemetry.dyn_subscriber().unwrap(), oledui)); - info!("System is ready in {}ms", Instant::now().as_millis()); - }); - }).unwrap()); + #[cfg(feature="radio")] + { + info!("Launching networking stack"); + spawner.must_spawn(renderbug_embassy::tasks::wifi::wireless_task(garage.telemetry.dyn_subscriber().unwrap(), wifi_init, peripherals.WIFI)); + } + + #[cfg(feature="demo")] + { + warn!("Launching with demo sequencer"); + spawner.must_spawn(renderbug_embassy::tasks::demo::demo_task(garage.notify.dyn_publisher().unwrap())); + } + #[cfg(not(feature="demo"))] + { + info!("Launching prediction engine"); + spawner.must_spawn(renderbug_embassy::tasks::predict::prediction_task(garage.predict.dyn_receiver(), garage.notify.dyn_publisher().unwrap(), garage.telemetry.dyn_publisher().unwrap())); + } + + info!("Launching core 2 watchdog"); + spawner.must_spawn(wdt_task(ui_wdt)); + + info!("System is ready in {}ms", Instant::now().as_millis()); + }); + }).unwrap() + }); } #[embassy_executor::task] diff --git a/src/tasks/wifi.rs b/src/tasks/wifi.rs index 8c56298..0474c38 100644 --- a/src/tasks/wifi.rs +++ b/src/tasks/wifi.rs @@ -1,127 +1,114 @@ -use bleps::{ad_structure::{create_advertising_data, AdStructure, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}, attribute_server::AttributeServer, gatt, Ble, HciConnector}; -use embassy_sync::channel::DynamicReceiver; +use alloc::string::ToString; +use embassy_executor::Spawner; use embassy_sync::pubsub::DynSubscriber; -use esp_hal::timer::AnyTimer; -use esp_wifi::ble::controller::BleConnector; +use esp_wifi::{EspWifiController, wifi::{ClientConfiguration, WifiDevice}}; use log::*; +use alloc::format; -use crate::events::Notification; +use embassy_net::dns::DnsSocket; +use embassy_net::tcp::client::{TcpClient, TcpClientState}; +use embassy_net::{Config, StackResources}; +use nalgebra::Vector2; +use reqwless::client::{HttpClient, TlsConfig}; +use static_cell::StaticCell; -pub async fn ble_task(_notify: DynSubscriber<'static, Notification>, wifi_init: &esp_wifi::EspWifiController<'_>, bluetooth_device: esp_hal::peripherals::BT<'static>) { - info!("Setting up BLE stack"); - let connector = BleConnector::new(wifi_init, bluetooth_device); - let get_millis = || esp_hal::time::Instant::now().duration_since_epoch().as_millis(); - let hci = HciConnector::new(connector, get_millis); - let mut ble = Ble::new(&hci); - ble.init().unwrap(); - ble.cmd_set_le_advertising_parameters().unwrap(); - ble.cmd_set_le_advertising_data( - create_advertising_data(&[ - AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), - AdStructure::ServiceUuids16(&[Uuid::Uuid16(0x0001)]), - AdStructure::CompleteLocalName("Renderbug!") - ]) - .unwrap() - ).unwrap(); - ble.cmd_set_le_advertise_enable(true).unwrap(); +use crate::{backoff::Backoff, events::{Prediction, Telemetry}}; - let mut wf1 = |_offset: usize, data: &[u8]| { - info!("Read serial data! {data:?}"); - }; - - // https://nextcloud.malloc.hackerbots.net/nextcloud/index.php/apps/phonetrack/logGet/cc668656ef51680c99b0eb6e5323a459/renderbug?lat=LAT&lon=LON&alt=ALT&acc=ACC&bat=BAT&sat=SAT&speed=SPD&bearing=DIR×tamp=TIME - - // Other useful characteristics: - // 0x2A67 - Location and speed - // 0x2A00 - Device name - // 0x2B90 - Device time - // Permitted characteristics: - // Acceleration - // Force - // Length - // Linear position - // Rotational speed - // Temperature - // Torque - // Useful app that logs data: https://github.com/a2ruan/ArduNetApp?tab=readme-ov-file - // Requires service 4fafc201-1fb5-459e-8fcc-c5c9c331914b, characteristic beb5483e-36e1-4688-b7f5-ea07361b26a8 - let s = &b""[..]; - gatt!([service { - uuid: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E", // Nordic UART - characteristics: [ - characteristic { - uuid: "6E400003-B5A3-F393-E0A9-E50E24DCCA9E", // TX from device, everything is sent as notifications - notify: true, - name: "tx", - value: s - }, - characteristic { - uuid: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E", // RX from phone - write: wf1 - }, - ] - }]); - let mut rng = bleps::no_rng::NoRng; - let mut _srv = AttributeServer::new(&mut ble, &mut gatt_attributes, &mut rng); - - info!("BLE running!"); - // TODO: Somehow need to recreate the attributeserver after disconnecting? - /*loop { - let notification = match notify.try_receive() { - Err(_) => None, - Ok(Notification::Beat) => Some("beat"), - _ => None - }; - - match notification { - None => { - srv.do_work().unwrap(); - Timer::after_millis(5).await; - }, - Some(serial_data) => { - for chunk in serial_data.as_bytes().chunks(20) { - srv.do_work_with_notification(Some(NotificationData::new(tx_handle, chunk))).unwrap(); - } - srv.do_work_with_notification(Some(NotificationData::new(tx_handle, &b"\n"[..]))).unwrap(); - } - } - }*/ +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, WifiDevice<'static>>) { + info!("Network stack is running"); + runner.run().await } +static RESOURCES: StaticCell> = StaticCell::new(); + // TODO: Wifi task needs to know when there is data to upload, so it only connects when needed. #[embassy_executor::task] -pub async fn wireless_task(_notify: DynamicReceiver<'static, Notification>, timer: AnyTimer<'static>, rng: esp_hal::peripherals::RNG<'static>, _wifi_device: esp_hal::peripherals::WIFI<'static>, _bluetooth_device: esp_hal::peripherals::BT<'static>) { - let rng = esp_hal::rng::Rng::new(rng); - let _wifi_init = - esp_wifi::init(timer, rng).expect("Failed to initialize WIFI/BLE controller"); +pub async fn wireless_task(mut telemetry: DynSubscriber<'static, Telemetry>, wifi_init: &'static mut EspWifiController<'static>, wifi_device: esp_hal::peripherals::WIFI<'static>) { + let (mut wifi, interfaces) = esp_wifi::wifi::new(wifi_init, wifi_device) + .expect("Failed to initialize WIFI!"); + wifi.set_configuration(&esp_wifi::wifi::Configuration::Client( + ClientConfiguration { + ssid: "The Frequency".to_string(), + auth_method: esp_wifi::wifi::AuthMethod::WPA2Personal, + password: "thepasswordkenneth".to_string(), + ..Default::default() + } + )).unwrap(); + wifi.set_mode(esp_wifi::wifi::WifiMode::Sta).unwrap(); + wifi.set_power_saving(esp_wifi::config::PowerSaveMode::Maximum).unwrap(); + wifi.start_async().await.unwrap(); - //ble_task(notify, &wifi_init, bluetooth_device).await; + let device = interfaces.sta; + // TODO: Somehow grab a real random seed from main() + let seed = 0; + + let config = Config::dhcpv4(Default::default()); + let (stack, runner) = embassy_net::new(device, config, RESOURCES.init_with(|| { StackResources::new() }), seed as u64); + info!("Launching network task"); + Spawner::for_current_executor().await.must_spawn(net_task(runner)); - /* loop { - let (mut wifi, _interfaces) = esp_wifi::wifi::new(&wifi_init, wifi_device) - .expect("Failed to initialize WIFI controller"); } + Backoff::from_secs(3).forever().attempt(async || { + info!("Connecting to wifi..."); + match wifi.connect_async().await { + Ok(_) => Ok(()), + Err(e) => { + error!("Wifi error: {e:?}"); + Err(()) + } + } + }).await.unwrap(); + + info!("Waiting for DHCP"); + stack.wait_config_up().await; + + info!("Online!"); + let ip_cfg = stack.config_v4().unwrap(); + info!("ip={ip_cfg:?}"); + + let mut rx_buf = [0; 4096]; + let mut tx_buf = [0; 4096]; + let dns = DnsSocket::new(stack); + let tcp_state = TcpClientState::<1, 4096, 4096>::new(); + let tcp = TcpClient::new(stack, &tcp_state); + let tls = TlsConfig::new( + seed as u64, + &mut rx_buf, + &mut tx_buf, + reqwless::client::TlsVerify::None, + ); + + let mut client = HttpClient::new_with_tls(&tcp, &dns, tls); loop { - //let results = wifi.scan_n_async(16).await.unwrap(); - wifi.set_configuration(&esp_wifi::wifi::Configuration::Client( - ClientConfiguration { - ssid: "The Frequency".to_string(), - auth_method: esp_wifi::wifi::AuthMethod::WPA2Personal, - password: "thepasswordkenneth".to_string(), - ..Default::default() + if let Telemetry::Prediction(Prediction::Location(coords)) = telemetry.next_message_pure().await { + if let Err(e) = push_location(&mut client, coords).await { + error!("HTTP error in publishing location: {e:?}"); + break } - )).unwrap(); - - if wifi.connect_async().await.is_ok() { - info!("Connected to wifi!"); - while wifi.is_connected().unwrap() { - Timer::after_secs(60).await; - } - info!("Disconnected."); } - Timer::after_secs(30).await; } } - */ +} + +async fn push_location(client: &mut HttpClient<'_, TcpClient<'_, 1, 4096, 4096>, DnsSocket<'_>>, location: Vector2) -> Result<(), reqwless::Error> { + let mut buffer = [0u8; 4096]; + let base = "https://nextcloud.malloc.hackerbots.net/nextcloud/index.php/apps/phonetrack/logGet/a062000067304e9dee6590f1b8f9e0db/renderbug"; + let url = format!("{base}?lat={}&lon={}", location.y, location.x); + info!("Pushing to {url}"); + let mut http_req = client + .request( + reqwless::request::Method::GET, + &url, + ) + .await?; + let response = http_req.send(&mut buffer).await?; + + info!("Got response"); + let res = response.body().read_to_end().await?; + + let content = core::str::from_utf8(res).unwrap(); + info!("{content}"); + Ok(()) } \ No newline at end of file