331 lines
8.6 KiB
Rust
331 lines
8.6 KiB
Rust
//! Set SSID and PASSWORD env variable before running this example.
|
|
//!
|
|
//! This gets an ip address via DHCP and advertises it with mdns
|
|
|
|
// use alloc::vec;
|
|
use alloc::vec::Vec;
|
|
use alloc::string::String;
|
|
use alloc::format;
|
|
|
|
use embassy_executor::Spawner;
|
|
use embassy_net::{
|
|
Runner,
|
|
StackResources
|
|
};
|
|
use embassy_futures::join::join;
|
|
|
|
use embassy_time::{Duration, Timer};
|
|
use esp_alloc as _;
|
|
use esp_backtrace as _;
|
|
use esp_hal::{
|
|
rng::Rng,
|
|
};
|
|
use esp_println::println;
|
|
use esp_radio::{
|
|
Controller,
|
|
wifi::{
|
|
ClientConfig,
|
|
ModeConfig,
|
|
ScanConfig,
|
|
WifiController,
|
|
WifiDevice,
|
|
WifiEvent,
|
|
WifiStaState,
|
|
},
|
|
};
|
|
use esp_hal_ota::Ota;
|
|
use esp_storage::FlashStorage;
|
|
|
|
use embassy_net::Stack;
|
|
|
|
use log::{warn,info};
|
|
|
|
use crate::mdns;
|
|
|
|
// 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
|
|
macro_rules! mk_static {
|
|
($t:ty,$val:expr) => {{
|
|
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
|
#[deny(unused_attributes)]
|
|
let x = STATIC_CELL.uninit().write(($val));
|
|
x
|
|
}};
|
|
}
|
|
|
|
const SSID: &str = env!("SSID");
|
|
const PASSWORD: &str = env!("PASSWORD");
|
|
|
|
|
|
fn dns_records(my_ip_address: [u8;4], serial: Option<usize>) -> Vec<mdns::Resource> {
|
|
use crate::mdns::*;
|
|
use crate::mdns::RData as R;
|
|
|
|
let (hostname, ptr_name) =
|
|
match serial {
|
|
None => (
|
|
String::from("eculocate.local"),
|
|
String::from("Bike._keihin._udp.local")
|
|
),
|
|
Some(number) => (
|
|
format!("eculocate{number}.local"),
|
|
format!("Bike ({number})._keihin._udp.local")
|
|
)
|
|
};
|
|
|
|
|
|
let records = [
|
|
Resource::new(&hostname, R::A(my_ip_address)),
|
|
Resource::new(
|
|
&ptr_name,
|
|
R::Srv {
|
|
target: String::from(hostname), port: 5000,
|
|
weight: 0, priority: 0
|
|
}
|
|
),
|
|
Resource::new(&ptr_name, R::Txt(["txtvers=1".into()].into())),
|
|
Resource::new("_keihin._udp.local", R::Ptr(ptr_name)),
|
|
Resource::new(DISCOVERY_NAME, R::Ptr("_keihin._udp.local".into()))
|
|
];
|
|
Vec::from(records)
|
|
}
|
|
|
|
pub async fn start_wifi(spawner: &Spawner,
|
|
wifi_peripheral: esp_hal::peripherals::WIFI<'static>,
|
|
flash_peripheral: esp_hal::peripherals::FLASH<'static>,
|
|
ecu: &mut crate::ecu::Ecu<'_>) {
|
|
|
|
|
|
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
|
|
|
|
let (controller, interfaces) =
|
|
esp_radio::wifi::new(esp_radio_ctrl, wifi_peripheral, Default::default()).unwrap();
|
|
|
|
let wifi_interface = interfaces.sta;
|
|
|
|
let config = embassy_net::Config::dhcpv4(Default::default());
|
|
|
|
let rng = Rng::new();
|
|
let seed = (rng.random() as u64) << 32 | rng.random() as u64;
|
|
|
|
// Init network stack
|
|
let (stack, runner) = embassy_net::new(
|
|
wifi_interface,
|
|
config,
|
|
mk_static!(StackResources<5>, StackResources::<5>::new()),
|
|
seed,
|
|
);
|
|
|
|
spawner.spawn(connection(controller)).ok();
|
|
spawner.spawn(net_task(runner)).ok();
|
|
|
|
let my_address;
|
|
|
|
loop {
|
|
if stack.is_link_up() {
|
|
break;
|
|
}
|
|
Timer::after(Duration::from_millis(500)).await;
|
|
}
|
|
|
|
println!("Waiting to get IP address...");
|
|
loop {
|
|
if let Some(config) = stack.config_v4() {
|
|
println!("Got IP address: {}", config.address);
|
|
my_address = config.address.address().octets();
|
|
break;
|
|
}
|
|
Timer::after(Duration::from_millis(500)).await;
|
|
}
|
|
|
|
let mut serial = None;
|
|
|
|
join(
|
|
join(
|
|
crate::streamer::loop_udp_thing(stack, ecu),
|
|
run_tcp_handler(stack, flash_peripheral),
|
|
),
|
|
async {
|
|
loop {
|
|
let mdns_thread = mdns::responder(
|
|
stack,
|
|
dns_records(my_address, serial)
|
|
);
|
|
mdns_thread.await;
|
|
serial = match serial { None => Some(2), Some(n) => Some(n+1) };
|
|
info!("probe failed, restarting with incremented serial {:?}", serial);
|
|
Timer::after(Duration::from_millis(5000)).await;
|
|
};
|
|
}
|
|
).await;
|
|
}
|
|
|
|
|
|
#[embassy_executor::task]
|
|
async fn connection(mut controller: WifiController<'static>) {
|
|
println!("start connection task");
|
|
println!("Device capabilities: {:?}", controller.capabilities());
|
|
loop {
|
|
match esp_radio::wifi::sta_state() {
|
|
WifiStaState::Connected => {
|
|
// wait until we're no longer connected
|
|
controller
|
|
.wait_for_event(WifiEvent::StaDisconnected)
|
|
.await;
|
|
Timer::after(Duration::from_millis(5000)).await
|
|
}
|
|
_ => {}
|
|
}
|
|
if !matches!(controller.is_started(), Ok(true)) {
|
|
let station_config = ModeConfig::Client(
|
|
ClientConfig::default()
|
|
.with_ssid(SSID.into())
|
|
.with_password(PASSWORD.into()),
|
|
);
|
|
controller.set_config(&station_config).unwrap();
|
|
println!("Starting wifi");
|
|
controller.start_async().await.unwrap();
|
|
println!("Wifi started!");
|
|
|
|
println!("Scan");
|
|
let scan_config = ScanConfig::default().with_max(10);
|
|
let result = controller
|
|
.scan_with_config_async(scan_config)
|
|
.await
|
|
.unwrap();
|
|
for ap in result {
|
|
println!("{:?}", ap);
|
|
}
|
|
}
|
|
println!("About to connect...");
|
|
|
|
match controller.connect_async().await {
|
|
Ok(_) => println!("Wifi connected!"),
|
|
Err(e) => {
|
|
println!("Failed to connect to wifi: {e:?}");
|
|
Timer::after(Duration::from_millis(5000)).await
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[embassy_executor::task]
|
|
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
|
|
runner.run().await
|
|
}
|
|
|
|
async fn read_all(socket: &mut embassy_net::tcp::TcpSocket<'_>,
|
|
buf: &mut [u8],
|
|
len: usize) -> usize {
|
|
let mut n = 0;
|
|
while n < len {
|
|
match socket.read(&mut buf[n..]).await {
|
|
Ok(0) => {
|
|
warn!("read EOF");
|
|
break;
|
|
},
|
|
Ok(count) => {
|
|
n = n + count;
|
|
}
|
|
Err(e) => {
|
|
warn!("read err {:?}", e);
|
|
return 0; // ew
|
|
}
|
|
}
|
|
}
|
|
n
|
|
}
|
|
|
|
async fn write_ota(ota : &mut Ota<FlashStorage<'_>>,
|
|
sock: &mut embassy_net::tcp::TcpSocket<'_>,
|
|
len: u32) {
|
|
let mut buf = [0u8; 4096];
|
|
|
|
ota.ota_begin(len, 0);
|
|
|
|
let mut len = len;
|
|
while len > 0 {
|
|
match sock.read(&mut buf).await {
|
|
Ok(chunksize) => {
|
|
info!("writing {} bytes, {} remaining", chunksize, len);
|
|
let res = ota.ota_write_chunk(&buf[0..chunksize]);
|
|
if res == Ok(true) {
|
|
info!("eof on write, remaining bytes = {} ", len);
|
|
|
|
if ota.ota_flush(false, false).is_ok() { // ignore crc, no rollback
|
|
Timer::after_millis(1000).await;
|
|
esp_hal::system::software_reset();
|
|
}
|
|
}
|
|
// let progress = (ota.get_ota_progress() * 100.0) as u8;
|
|
// log::info!("progress: {}%", progress);
|
|
len = len - (chunksize as u32);
|
|
},
|
|
Err(x) => {
|
|
warn!("error reading ota image: {:?}", x);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if len == 0 {
|
|
info!("wrote all bytes");
|
|
} else {
|
|
warn!("stream ended early, was expecting another {} bytes", len);
|
|
}
|
|
}
|
|
|
|
fn register_session_key(key : &[u8]) {
|
|
info!("got session key{:?}", key);
|
|
}
|
|
|
|
|
|
async fn run_tcp_handler(stack : Stack<'_>, flash_peripheral: esp_hal::peripherals::FLASH<'static>) {
|
|
let mut rx_buffer = [0; 4096];
|
|
let mut tx_buffer = [0; 4096];
|
|
let mut buf = [0u8; 68];
|
|
let flash = FlashStorage::new(flash_peripheral);
|
|
let mut ota = Ota::new(flash).unwrap();
|
|
|
|
loop {
|
|
let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
|
socket.set_timeout(Some(Duration::from_secs(10)));
|
|
|
|
if let Err(e) = socket.accept(5000).await {
|
|
warn!("error accepting connection: {:?}", e);
|
|
continue;
|
|
}
|
|
|
|
info!("Received connection from {:?}", socket.remote_endpoint());
|
|
|
|
let n = read_all(&mut socket, &mut buf, 68).await;
|
|
|
|
if n == 68 {
|
|
info!("got a thing");
|
|
match &buf[0..4] {
|
|
b"ROM0" => {
|
|
info!("OTA request");
|
|
let mut len_bytes = [0u8; 4];
|
|
let _n = read_all(&mut socket, &mut len_bytes[..], 4).await;
|
|
let len = u32::from_be_bytes(len_bytes);
|
|
info!("reading {} bytes", len);
|
|
write_ota(&mut ota, &mut socket, len).await;
|
|
info!("write {:?}", socket.write(b"BYE\n").await);
|
|
socket.flush().await;
|
|
socket.close();
|
|
},
|
|
b"KEY0" => {
|
|
info!("sessionkey");
|
|
let _sig = &buf[4..68];
|
|
let mut key = [0u8; 32]; // 256 bits
|
|
if read_all(&mut socket, &mut key, 32).await == 32 {
|
|
register_session_key(&key);
|
|
}
|
|
},
|
|
_ => {
|
|
warn!("unrecognised command");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|