commit 63022bcfd22f19f49b04cfc1bd7e0d922f887551
parent dbd276d200cd0c4b1a28c5dd5df7a414e4706d63
Author: Daniel Barlow <dan@telent.net>
Date: Sat, 6 Dec 2025 19:48:00 +0000
add wifi "hello world"
Diffstat:
| M | Cargo.lock | | | 74 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
| M | Cargo.toml | | | 13 | +++++++++++++ |
| M | notes.md | | | 89 | +++++++++++++++---------------------------------------------------------------- |
| M | src/main.rs | | | 35 | +++++++++++++++++++---------------- |
| A | src/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
+}