commit 2cfe436ac9e02c9bea7588041ace96ccb48580b0
parent 938cb4fda0d48015bf89033904cb9d6cf03a1eac
Author: Daniel Barlow <dan@telent.net>
Date: Mon, 2 Feb 2026 21:19:39 +0000
scan wireless networks & publish over ble
does not actually connect yet
Diffstat:
4 files changed, 110 insertions(+), 42 deletions(-)
diff --git a/client/ble.py b/client/ble.py
@@ -3,26 +3,59 @@ import readline
import rlcompleter
import asyncio
from bleak import BleakClient,BleakScanner
+import sys
readline.parse_and_bind("tab: complete")
address = "FF:E4:05:1A:8F:FF"
-CURRENT_INDEX_UUID = "76be25c2-0803-44fc-a97c-bcb56c69c926"
+
+UUIDS = {
+ 'connection_state': "76be25c2-0801-44fc-a97c-bcb56c69c926",
+ 'max_index': "76be25c2-0802-44fc-a97c-bcb56c69c926",
+ 'current_index': "76be25c2-0803-44fc-a97c-bcb56c69c926",
+ 'current_network': "76be25c2-0804-44fc-a97c-bcb56c69c926",
+ 'secret': "76be25c2-0805-44fc-a97c-bcb56c69c926",
+}
+
+networks = []
+max_index = 0
+
+async def show_networks(client):
+ global max_index
+ global networks
+ max_index = ord(await client.read_gatt_char(UUIDS['max_index']))
+ print(f"{max_index} networks available")
+ networks = [None]
+
+ for i in range(1, max_index + 1):
+ await client.write_gatt_char(UUIDS['current_index'], bytes([i]))
+ current_network = await client.read_gatt_char(UUIDS['current_network'])
+ networks.append(current_network)
+ print(f"{i}: {current_network}")
+
+async def choose_network(client, name):
+ for i in range(1, max_index + 1):
+ if networks[i].find(name) >= 0:
+ print(f"found {name} in {networks[i]}")
+ chosen = i
+ break
+ if chosen:
+ await client.write_gatt_char(UUIDS['current_index'], bytes([chosen]))
+ new_index = await client.read_gatt_char(UUIDS['current_index'])
+ print(f"set to {new_index[0]}")
async def main(address):
- print("go")
+ preferred = sys.argv[1] if len(sys.argv) > 1 else None
+ print(f"choose {preferred}")
devices = await BleakScanner.discover()
for d in devices:
print(d)
async with BleakClient(address, timeout=30) as client:
- current_index = await client.read_gatt_char(CURRENT_INDEX_UUID)
- print(f"Model Number: {current_index}")
- current_index = ord(current_index) + 1
- await client.write_gatt_char(CURRENT_INDEX_UUID, bytes([current_index]))
- current_index = await client.read_gatt_char(CURRENT_INDEX_UUID)
- print(f"Model Number: {current_index}")
+ await show_networks(client)
+ if preferred != None:
+ await choose_network(client, preferred.encode(encoding="utf-8"))
asyncio.run(main(address))
diff --git a/ecu-esp32/src/main.rs b/ecu-esp32/src/main.rs
@@ -10,6 +10,7 @@ use esp_backtrace as _;
use esp_println as _;
use esp_alloc as _;
+use alloc::vec::Vec;
pub mod ecu;
pub mod wifi;
@@ -27,15 +28,19 @@ use embassy_executor::Spawner;
use embassy_futures::join::join3;
use embassy_time::Timer;
-use esp_radio::Controller;
+use esp_radio::{
+ Controller,
+ wifi::{
+ ClientConfig,
+ ModeConfig,
+ }
+};
use log::{info};
use crate::ecu::Ecu;
use crate::ota::Flasher;
-
-
extern crate alloc;
// This creates a default app-descriptor required by the esp-idf bootloader.
@@ -86,13 +91,31 @@ async fn main(s : Spawner) {
let esp_radio_ctrl = // esp_radio::init().expect("esp_radio init failed");
&*mk_static!(Controller<'static>, esp_radio::init().unwrap());
+ let (mut wifi_controller, interfaces) =
+ esp_radio::wifi::new(esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
+ let wifi_interface = interfaces.sta;
+ let station_config = ModeConfig::Client(ClientConfig::default());
+ wifi_controller.set_config(&station_config).expect("can't config wifi");
+ wifi_controller.start_async().await.expect("wifi failed to start");
+
+ let wifi_networks : Vec<[u8;40]> =
+ crate::wifi::scan_networks(&mut wifi_controller).await.into_iter().map(
+ |ap| {
+ let mut s : [u8; 40] = [0u8; 40];
+ s[0] = (ap.signal_strength & 0x7f) as u8;
+ s[1..7].copy_from_slice(&ap.bssid);
+ let name = ap.ssid.as_bytes();
+ s[8..(8+name.len())].copy_from_slice(name);
+ s
+ }
+ ).collect();
+
join3(
crate::wifi::start_wifi(&s,
- esp_radio_ctrl,
+ wifi_controller, wifi_interface,
flasher,
- peripherals.WIFI,
&mut ecu),
- wibble::run_ble(&esp_radio_ctrl, peripherals.BT),
+ wibble::run_ble(&esp_radio_ctrl, peripherals.BT, wifi_networks),
async {
loop {
Timer::after_millis(1000).await;
diff --git a/ecu-esp32/src/wibble.rs b/ecu-esp32/src/wibble.rs
@@ -6,7 +6,7 @@ use log::{warn,info};
use embassy_futures::join::join;
use embassy_futures::select::select;
-use alloc::string::String;
+use alloc::vec::Vec;
/// Max number of connections
const CONNECTIONS_MAX: usize = 1;
@@ -48,7 +48,8 @@ struct WifiProvisioningService {
pub async fn run_ble(radio_init: &RadioController<'static>,
- peripheral: esp_hal::peripherals::BT<'_>) {
+ peripheral: esp_hal::peripherals::BT<'_>,
+ networks: Vec<[u8; 40]>) {
// let radio_init = esp_radio::init().expect("Failed to initialize Wi-Fi/BLE controller");
let connector = BleConnector::new(&radio_init, peripheral, Default::default()).expect("connector");
let controller: ExternalController<_, 20> = ExternalController::new(connector);
@@ -78,7 +79,7 @@ pub async fn run_ble(radio_init: &RadioController<'static>,
match advertise("Eculocate", &mut peripheral, &server).await {
Ok(conn) => {
// set up tasks when the connection is established to a central, so they don't run when no one is connected.
- let a = gatt_events_task(&server, &conn);
+ let a = gatt_events_task(&server, &conn, &networks);
let b = custom_task(&server, &conn, &stack);
// run until any task ends (usually because the connection has been closed),
// then return to advertising state.
@@ -107,8 +108,16 @@ async fn ble_task<C: Controller, P: PacketPool>(mut runner: Runner<'_, C, P>) {
///
/// This function will handle the GATT events and process them.
/// This is how we interact with read and write requests.
-async fn gatt_events_task<P: PacketPool>(server: &Server<'_>, conn: &GattConnection<'_, '_, P>) -> Result<(), Error> {
- let current_index = server.wifi_provisioning_service.current_network_index;
+async fn gatt_events_task<P: PacketPool>(server: &Server<'_>,
+ conn: &GattConnection<'_, '_, P>,
+ networks: &Vec<[u8; 40]> ) -> Result<(), Error> {
+ let wps = &server.wifi_provisioning_service;
+ for ap in networks {
+ info!("ap {:?}", ap)
+ };
+ let max_index : u8 = (networks.len() & 0xff).try_into().unwrap();
+ server.set(&wps.max_network_index, &max_index);
+ let current_index = wps.current_network_index;
let reason = loop {
match conn.next().await {
GattConnectionEvent::Disconnected { reason } => break reason,
@@ -123,10 +132,20 @@ async fn gatt_events_task<P: PacketPool>(server: &Server<'_>, conn: &GattConnect
// }
// }
GattEvent::Write(event) => {
- info!("[gatt] write handle {:?}", event.handle());
+ info!("[gatt] write handle {:?} {:?}", event.handle(),
+ event.data());
if event.handle() == current_index.handle {
- info!("[gatt] switch current index: {:?}", event.data());
- }
+ let i = (event.data()[0]) as usize;
+ info!("[gatt] switch current index: {:?}", i);
+ if i == 0 {
+ server.set(&wps.current_network, &[0u8;40]);
+ } else if i <= networks.len() {
+ server.set(&wps.current_network,
+ &(networks[i - 1]));
+ } else {
+ warn!("index {:?} out of range", i);
+ }
+ }
},
_ => {}
};
diff --git a/ecu-esp32/src/wifi.rs b/ecu-esp32/src/wifi.rs
@@ -28,7 +28,7 @@ use esp_hal::{
};
use esp_println::println;
use esp_radio::{
- Controller,
+// Controller,
wifi::{
ClientConfig,
ModeConfig,
@@ -37,6 +37,7 @@ use esp_radio::{
WifiDevice,
WifiEvent,
WifiStaState,
+ AccessPointInfo
},
};
@@ -130,26 +131,27 @@ impl SessionStore {
}
}
+pub async fn scan_networks(controller: &mut WifiController<'_>) -> Vec<AccessPointInfo> {
+ println!("Scan");
+ let scan_config = ScanConfig::default().with_max(10);
+ let result = controller
+ .scan_with_config_async(scan_config)
+ .await
+ .expect("scan failed");
+ result
+}
pub async fn start_wifi(spawner: &Spawner,
- esp_radio_ctrl: &'static Controller<'static>,
+ controller: WifiController<'static>,
+ wifi_interface: WifiDevice<'static>,
flasher: crate::ota::Flasher<'_>,
- wifi_peripheral: esp_hal::peripherals::WIFI<'static>,
ecu: &mut crate::ecu::Ecu<'_>) {
-
-
- 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,
@@ -227,15 +229,6 @@ async fn connection(mut controller: WifiController<'static>) {
controller.set_config(&station_config).unwrap();
controller.start_async().await.expect("wifi controller failed to start");
- 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...");
let station_config = ModeConfig::Client(