radio: rewrite the networking stack, and implement some BLE magic
This commit is contained in:
@@ -1,100 +1,124 @@
|
||||
use alloc::string::ToString;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::channel::DynamicSender;
|
||||
use embassy_sync::pubsub::DynSubscriber;
|
||||
use esp_radio::Controller;
|
||||
use esp_radio::wifi::{ClientConfig, WifiDevice};
|
||||
use embassy_time::{Duration, Instant, WithTimeout};
|
||||
use esp_hal::rng::Rng;
|
||||
use esp_radio::wifi::{ClientConfig, ScanConfig, WifiController, WifiDevice, WifiEvent};
|
||||
use log::*;
|
||||
use alloc::format;
|
||||
|
||||
use embassy_net::dns::DnsSocket;
|
||||
use embassy_net::tcp::client::{TcpClient, TcpClientState};
|
||||
use embassy_net::{Config, StackResources};
|
||||
use embassy_net::Stack;
|
||||
use nalgebra::Vector2;
|
||||
use reqwless::client::{HttpClient, TlsConfig};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
use crate::ego::engine::gps_to_local_meters_haversine;
|
||||
use crate::events::{Measurement, SensorSource, SensorState};
|
||||
use crate::{backoff::Backoff, events::{Prediction}};
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn net_task(mut runner: embassy_net::Runner<'static, WifiDevice<'static>>) {
|
||||
info!("Network stack is running");
|
||||
pub async fn net_task(mut runner: embassy_net::Runner<'static, WifiDevice<'static>>) {
|
||||
info!("Starting network stack");
|
||||
runner.run().await
|
||||
}
|
||||
|
||||
static RESOURCES: StaticCell<StackResources<5>> = 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(mut predictions: DynSubscriber<'static, Prediction>, wifi_init: &'static mut Controller<'static>, wifi_device: esp_hal::peripherals::WIFI<'static>) {
|
||||
let (mut wifi, interfaces) = esp_radio::wifi::new(wifi_init, wifi_device, esp_radio::wifi::Config::default())
|
||||
.expect("Failed to initialize WIFI!");
|
||||
pub async fn wifi_connect_task(mut wifi: WifiController<'static>, stack: Stack<'static>, motion: DynamicSender<'static, Measurement>) {
|
||||
wifi.set_config(&esp_radio::wifi::ModeConfig::Client(
|
||||
ClientConfig::default()
|
||||
.with_ssid("The Frequencey".to_string())
|
||||
.with_ssid("The Frequency".to_string())
|
||||
.with_auth_method(esp_radio::wifi::AuthMethod::Wpa2Personal)
|
||||
.with_password("thepasswordkenneth".to_string())
|
||||
)).unwrap();
|
||||
wifi.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
|
||||
wifi.set_power_saving(esp_radio::wifi::PowerSaveMode::Maximum).unwrap();
|
||||
wifi.start_async().await.unwrap();
|
||||
|
||||
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");
|
||||
unsafe { Spawner::for_current_executor().await }.must_spawn(net_task(runner));
|
||||
|
||||
loop {
|
||||
Backoff::from_secs(3).forever().attempt(async || {
|
||||
info!("Connecting to wifi...");
|
||||
wifi.start_async().await.unwrap();
|
||||
motion.send(Measurement::SensorHardwareStatus(SensorSource::Wifi, crate::events::SensorState::AcquiringFix)).await;
|
||||
let networks = wifi.scan_with_config_async(ScanConfig::default().with_show_hidden(true)).await.unwrap();
|
||||
for network in networks {
|
||||
info!("wifi: {} @ {}db", network.ssid, network.signal_strength);
|
||||
}
|
||||
match wifi.connect_async().await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Wifi error: {e:?}");
|
||||
error!("Unable to connect to wifi: {e:?}");
|
||||
wifi.stop_async().await.unwrap();
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}).await.unwrap();
|
||||
|
||||
info!("Waiting for DHCP");
|
||||
stack.wait_config_up().await;
|
||||
info!("Waiting for DHCP...");
|
||||
motion.send(Measurement::SensorHardwareStatus(SensorSource::Wifi, SensorState::Degraded)).await;
|
||||
if stack.wait_config_up().with_timeout(Duration::from_secs(5)).await.is_ok() {
|
||||
info!("Online!");
|
||||
motion.send(Measurement::SensorHardwareStatus(SensorSource::Wifi, SensorState::Online)).await;
|
||||
let ip_cfg = stack.config_v4().unwrap();
|
||||
info!("ip={ip_cfg:?}");
|
||||
wifi.wait_for_event(WifiEvent::ApStaDisconnected).await;
|
||||
info!("Wifi disconnected!");
|
||||
} else {
|
||||
warn!("DHCP timed out after 5 seconds. Disconnecting wifi");
|
||||
wifi.disconnect_async().await.unwrap();
|
||||
}
|
||||
warn!("Stopping wifi device");
|
||||
wifi.stop_async().await.unwrap();
|
||||
motion.send(Measurement::SensorHardwareStatus(SensorSource::Wifi, SensorState::Offline)).await;
|
||||
}
|
||||
}
|
||||
|
||||
info!("Online!");
|
||||
let ip_cfg = stack.config_v4().unwrap();
|
||||
info!("ip={ip_cfg:?}");
|
||||
// TODO: Wifi task needs to know when there is data to upload, so it only connects when needed.
|
||||
#[embassy_executor::task]
|
||||
pub async fn http_telemetry_task(mut predictions: DynSubscriber<'static, Prediction>, stack: Stack<'static>) {
|
||||
// TODO: should wait for wifi disconnect event somehow and use that to restart sending the wifi around
|
||||
|
||||
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 seed = Rng::new().random() as i32;
|
||||
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);
|
||||
let mut client = HttpClient::new_with_tls(&tcp, &dns, tls);
|
||||
|
||||
loop {
|
||||
if let Prediction::Location(coords) = predictions.next_message_pure().await {
|
||||
if let Err(e) = push_location(&mut client, coords).await {
|
||||
error!("HTTP error in publishing location: {e:?}");
|
||||
break
|
||||
// TODO: Only should upload a data point after some distance has occurred
|
||||
let mut last_location = Vector2::default();
|
||||
let mut last_push = Instant::from_ticks(0);
|
||||
|
||||
loop {
|
||||
if let Prediction::Location(coords) = predictions.next_message_pure().await {
|
||||
|
||||
if stack.is_config_up() {
|
||||
// Only push to HTTP if we have an ip config etc
|
||||
if last_push.elapsed().as_secs() >= 5 || gps_to_local_meters_haversine(&last_location, &coords).norm() >= 10.0 {
|
||||
last_location = coords;
|
||||
last_push = Instant::now();
|
||||
if let Err(e) = Backoff::from_secs(3).attempt(async || {
|
||||
push_location(&mut client, coords, Instant::now().as_millis()).await
|
||||
}).await {
|
||||
warn!("Could not submit location! {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn push_location(client: &mut HttpClient<'_, TcpClient<'_, 1, 4096, 4096>, DnsSocket<'_>>, location: Vector2<f64>) -> Result<(), reqwless::Error> {
|
||||
async fn push_location(client: &mut HttpClient<'_, TcpClient<'_, 1, 4096, 4096>, DnsSocket<'_>>, location: Vector2<f64>, timestamp: u64) -> 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);
|
||||
let url = format!("{base}?lon={}&lat={}×tamp={}", location.y, location.x, timestamp);
|
||||
info!("Pushing to {url}");
|
||||
let mut http_req = client
|
||||
.request(
|
||||
@@ -104,10 +128,9 @@ async fn push_location(client: &mut HttpClient<'_, TcpClient<'_, 1, 4096, 4096>,
|
||||
.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}");
|
||||
debug!("HTTP response: {content}");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user