eculocate

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

commit 63022bcfd22f19f49b04cfc1bd7e0d922f887551
parent dbd276d200cd0c4b1a28c5dd5df7a414e4706d63
Author: Daniel Barlow <dan@telent.net>
Date:   Sat,  6 Dec 2025 19:48:00 +0000

add wifi "hello world"

Diffstat:
MCargo.lock | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
MCargo.toml | 13+++++++++++++
Mnotes.md | 89+++++++++++++++----------------------------------------------------------------
Msrc/main.rs | 35+++++++++++++++++++----------------
Asrc/wifi.rs | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 308 insertions(+), 90 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -54,6 +54,12 @@ dependencies = [ [[package]] name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" @@ -282,6 +288,7 @@ dependencies = [ "bt-hci", "embassy-executor", "embassy-futures", + "embassy-net", "embassy-sync 0.7.2", "embassy-time", "embedded-io 0.7.1", @@ -294,6 +301,7 @@ dependencies = [ "esp-radio", "esp-rtos", "log", + "smoltcp", "static_cell", "trouble-host", ] @@ -361,6 +369,29 @@ dependencies = [ ] [[package]] +name = "embassy-net" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558a231a47e7d4a06a28b5278c92e860f1200f24821d2f365a2f40fe3f3c7b2" +dependencies = [ + "document-features", + "embassy-net-driver", + "embassy-sync 0.7.2", + "embassy-time", + "embedded-io-async 0.6.1", + "embedded-nal-async", + "heapless 0.8.0", + "managed", + "smoltcp", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] name = "embassy-sync" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -488,6 +519,25 @@ dependencies = [ ] [[package]] +name = "embedded-nal" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56a28be191a992f28f178ec338a0bf02f63d7803244add736d026a471e6ed77" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "embedded-nal-async" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76959917cd2b86f40a98c28dd5624eddd1fa69d746241c8257eac428d83cb211" +dependencies = [ + "embedded-io-async 0.6.1", + "embedded-nal", +] + +[[package]] name = "embedded-storage" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -599,7 +649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ "bitfield 0.19.3", - "bitflags", + "bitflags 2.10.0", "bytemuck", "cfg-if", "critical-section", @@ -704,10 +754,12 @@ dependencies = [ "bt-hci", "cfg-if", "document-features", + "embassy-net-driver", "embedded-io 0.6.1", "embedded-io 0.7.1", "embedded-io-async 0.6.1", "embedded-io-async 0.7.0", + "enumset", "esp-alloc", "esp-config", "esp-hal", @@ -724,6 +776,7 @@ dependencies = [ "num-traits", "portable-atomic", "portable_atomic_enum", + "smoltcp", ] [[package]] @@ -1137,6 +1190,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1433,6 +1492,19 @@ dependencies = [ ] [[package]] +name = "smoltcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "heapless 0.8.0", + "managed", +] + +[[package]] name = "somni-expr" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -41,6 +41,15 @@ esp-radio = { version = "0.17.0", features = [ "esp32c3", "log-04", "unstable", + "wifi", + "smoltcp" +] } + +smoltcp = { version = "0.12.0", default-features = false, features = [ + "medium-ethernet", + "socket-raw", + "proto-dhcpv4", + "proto-ipv4" ] } @@ -52,6 +61,10 @@ trouble-host = { version = "0.5.0", features = [ "peripheral", "gatt", "default-packet-pool-mtu-255", "derive", "scan", ] } +embassy-net = { version = "0.7.1", features = [ +"medium-ethernet", "proto-ipv4", "tcp", "dhcpv4", +] } + [profile.dev] # Rust debug is too slow. diff --git a/notes.md b/notes.md @@ -127,76 +127,19 @@ table 0 is "ecmid" ## exporting -What BLE shall we publish? - -From https://gonzos.net/projects/wp-content/uploads/2015/09/Honda-data-tables.pdf - -Not all attributes are present for all bikes. - -RPM -TPS volt/% -ECT volt/deg C -IAT volt/deg C -MAP volt/kPa -Batt volt -Speed -Fuel inj (?) -Engine on/off -[Neutral/clutch, in gear, kickstand] - -Should we have one service for all the things, or separate services -for each? I assume that separate services make it easier to add more -later without having to change clients - - -+ rpm -+ tps - - voltage - - percentage -+ ect - - voltage - - degrees - -... etc - - -We could independently source bank angle and acceleration/deceleration -if we use a board with an IMU, so consider adding them to the profile -(or have two profiles). - -there don't appear to be services for this kind of thing -some characteristics that exist: - - uuid: 0x2C09 - name: Rotational Speed - id: org.bluetooth.characteristic.rotational_speed - (32 bits, signed => -2^31 < rpm < 2^31) - - uuid: 0x2A6E - name: Temperature - id: org.bluetooth.characteristic.temperature - (16 bits, 2dp => 0 <= t < 655.36) - - uuid: 0x2B18 - name: Voltage - id: org.bluetooth.characteristic.voltage - (16 bits, V/64 => 0 < v < 1022 - - uuid: 0x2A6D - name: Pressure - id: org.bluetooth.characteristic.pressure - - id: org.bluetooth.characteristic.percentage_8 - (8 bits, 0.5% resolution) - - id: org.bluetooth.characteristic.boolean - 0 = false, 1 = true - -specs of each characteristic are at -https://bitbucket.org/bluetooth-SIG/public/src/main/gss/ - -> A common practice is to choose a base UUID for the custom service and then increment the 3rd and 4th Most Significant Bytes (MSB) within the UUID of each included characteristic - - - -# System architecture - -So far there is none. This is my first Rust program - -We would like to make a stream that gets a value from the ecu each -time it is called, and writes that value to the appropriate ble -characteristic +we're going to use wifi instead of ble, it's a lot faster and +it makes remote dev simpler + +https://docs.espressif.com/projects/rust/no_std-training/03_6_http_client.html + +* scan for networks and then pick whichever of dandroid,telent +is available + +* use mdns to notify the peer of where to connect to + +https://lib.rs/crates/esp-hal-mdns + +* start a udp server, and stream the tables to anyone who sends a message to it + +* udp is connectionless. if we get a second message from a different address, +switch to straming the tables there instead diff --git a/src/main.rs b/src/main.rs @@ -11,6 +11,7 @@ use esp_println as _; pub mod wibble; pub mod ecu; +pub mod wifi; use esp_hal::{ timer::timg::TimerGroup, @@ -36,7 +37,7 @@ esp_bootloader_esp_idf::esp_app_desc!(); // #[main] #[esp_rtos::main] -async fn main(_s : Spawner) { +async fn main(s : Spawner) { esp_println::logger::init_logger_from_env(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); @@ -62,22 +63,24 @@ async fn main(_s : Spawner) { ); // wibble::run_ble(peripherals.BT, ecu).await; - async { - loop { - let c = async |measure| { - match measure { - Measure::Rpm(v) => { - info!("got rpm {v}"); - }, - Measure::Tps(mv, percentage) => { - info!("got tps {mv} {percentage}"); + join( + crate::wifi::start_wifi(&s, peripherals.WIFI), + async { + loop { + let c = async |measure| { + match measure { + Measure::Rpm(v) => { + info!("got rpm {v}"); + }, + Measure::Tps(mv, percentage) => { + info!("got tps {mv} {percentage}"); + } } - } - }; - let _ = &ecu.poll(c).await; - Timer::after_millis(1000).await; - } - }.await; + }; + let _ = &ecu.poll(c).await; + Timer::after_millis(1000).await; + } + }).await; } diff --git a/src/wifi.rs b/src/wifi.rs @@ -0,0 +1,187 @@ +//! Set SSID and PASSWORD env variable before running this example. +//! +//! This gets an ip address via DHCP then performs an HTTP get request to some +//! "random" server + + +use core::net::Ipv4Addr; + +use embassy_executor::Spawner; +use embassy_net::{Runner, StackResources, tcp::TcpSocket}; +use embassy_time::{Duration, Timer}; +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{ + // clock::CpuClock, + // interrupt::software::SoftwareInterruptControl, + // ram, + rng::Rng, + // timer::timg::TimerGroup, +}; +use esp_println::println; +use esp_radio::{ + Controller, + wifi::{ + ClientConfig, + ModeConfig, + ScanConfig, + WifiController, + WifiDevice, + WifiEvent, + WifiStaState, + }, +}; + +// 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"); + +pub async fn start_wifi(spawner: &Spawner, + wifi_peripheral: esp_hal::peripherals::WIFI<'static>) -> ! { + + + 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<3>, StackResources::<3>::new()), + seed, + ); + + spawner.spawn(connection(controller)).ok(); + spawner.spawn(net_task(runner)).ok(); + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + 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: {}", config.address); + break; + } + Timer::after(Duration::from_millis(500)).await; + } + + loop { + Timer::after(Duration::from_millis(1_000)).await; + + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + let remote_endpoint = (Ipv4Addr::new(142, 250, 185, 115), 80); + println!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + println!("connect error: {:?}", e); + continue; + } + println!("connected!"); + let mut buf = [0; 1024]; + loop { + use embedded_io_async::Write; + let r = socket + .write(b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n") + .await; + if let Err(e) = r { + println!("write error: {:?}", e); + break; + } + let n = match socket.read(&mut buf).await { + Ok(0) => { + println!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + println!("read error: {:?}", e); + break; + } + }; + println!("{}", core::str::from_utf8(&buf[..n]).unwrap()); + } + Timer::after(Duration::from_millis(3000)).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 +}