From 802285d0ecca53523215188d5a8d3acb5287ea7d Mon Sep 17 00:00:00 2001 From: Dave Date: Sat, 24 Aug 2024 20:53:20 -0400 Subject: [PATCH] work in progress on switching to direct mode --- gem-remotes-esp32/src/ble_server.rs | 45 +++++++++++++++++---- gem-remotes-esp32/src/commands.rs | 5 +++ gem-remotes-esp32/src/main.rs | 11 +++++ gem-remotes-esp32/src/pair_button_driver.rs | 32 +++++++++++++++ gem-remotes-esp32/src/test_console.rs | 14 +++++++ 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 gem-remotes-esp32/src/pair_button_driver.rs diff --git a/gem-remotes-esp32/src/ble_server.rs b/gem-remotes-esp32/src/ble_server.rs index af32b9a..4383fa2 100644 --- a/gem-remotes-esp32/src/ble_server.rs +++ b/gem-remotes-esp32/src/ble_server.rs @@ -34,6 +34,9 @@ impl BleServer { Commands::NotifyMotorDown { data: 0 }, Commands::NotifyMotorStop { data: 0 }, Commands::NotifyMotorUp { data: 0 }, + Commands::PairTimerExpired, + Commands::AllowPairing, + Commands::EraseBleBonds, ]; let r = dp.get_callback_channel(&cmds); let s = dp.get_cmd_channel(); @@ -55,7 +58,7 @@ impl BleServer { let ble_device = BLEDevice::take(); set_device_security(ble_device); let server = ble_device.get_server(); - set_server_callbacks(server); + set_server_callbacks(server, self.send_q.clone()); let _pairing_service = server.create_service(UUID_SERVICE_PAIR); let lift_service = server.create_service(UUID_SERVICE_LIFT); @@ -94,7 +97,7 @@ impl BleServer { let ble_advertiser = ble_device.get_advertising(); // TODO: we will need to enable / disable the ability to pair! - advertise_pairing(ble_advertiser)?; + advertise(ble_advertiser)?; loop { debug!("Waiting for updates that should be notified via bluetooth"); @@ -111,6 +114,16 @@ impl BleServer { Commands::NotifyMotorStop{data} => { button_up.lock().set_value(&[data]).notify(); } + Commands::PairTimerExpired => { + advertise_to_direct(ble_advertiser).expect("Failed to exit pairing mode"); + self.send_q.send(Commands::PairTimerClear).await?; + } + Commands::AllowPairing => { + advertise_to_undirected(ble_advertiser).expect("Failed to enter pairing mode"); + } + Commands::EraseBleBonds => { + ble_device.delete_all_bonds().expect("Failed trying to erase bluetooth bonding information"); + } _ => { error!("Invalid command received by bluetooth handler {:?}", cmd); // No need to reboot as state is recoverable. @@ -150,7 +163,7 @@ fn on_bluetooth_cmd(sender: &SendQ, args: &mut OnWriteArgs, cmd: Commands) { fn set_device_security(dev: &mut BLEDevice) { dev.security() - // Enable all security protections + // Enable all security protections (including bond, so that bond info is saved) .set_auth(AuthReq::all()) // Options we support for putting in pairing info. // "NoInputOutput" means that we will have "just works" pairing @@ -159,8 +172,8 @@ fn set_device_security(dev: &mut BLEDevice) { .resolve_rpa(); } -fn set_server_callbacks(server: &mut BLEServer) { - server.on_connect(|server, clntdesc| { +fn set_server_callbacks(server: &mut BLEServer, sender: SendQ) { + server.on_connect(move |server, clntdesc| { // Print connected client data info!("client connected: {:?}", clntdesc); // Update connection parameters @@ -172,24 +185,40 @@ fn set_server_callbacks(server: &mut BLEServer) { BLE_LATENCY, BLE_TIMEOUT, ).unwrap(); + sender.send_blocking(Commands::PairTimerClear).unwrap(); + // TODO: Cancel pairing mode timeout }); server.on_disconnect(|_desc, _reason| { info!("Disconnected, back to advertising"); }); } -fn advertise_pairing(advertiser: &Mutex) -> Result<()> { +fn advertise(advertiser: &Mutex) -> Result<()> { trace!("Setting up advertiser"); advertiser .lock() + .advertisement_type(ConnMode::Und) .set_data( BLEAdvertisementData::new() .name(DEVICE_NAME) .add_service_uuid(UUID_SERVICE_PAIR) )?; - // TODO: this appears to run in its own thread; verify. - // TODO: isn't there a restart? We'll need to switch between pairing and not. info!("Staring Bluetooth Server"); advertiser.lock().start()?; Ok(()) } + +fn advertise_to_direct(advertiser: &Mutex) -> Result<()> { + advertiser.lock().stop()?; + advertiser.lock().advertisement_type(ConnMode::Dir).start()?; + Ok(()) +} + +fn advertise_to_undirected(advertiser: &Mutex) -> Result<()> { + advertiser.lock().stop()?; + advertiser.lock().advertisement_type(ConnMode::Und).start()?; + Ok(()) +} + +//TODO set maximum pairs to remember? +//TODO after disconnect, it returns to scanning - will it return to directed scanning? Find out when directed is working. \ No newline at end of file diff --git a/gem-remotes-esp32/src/commands.rs b/gem-remotes-esp32/src/commands.rs index 7d83154..56fc8b5 100644 --- a/gem-remotes-esp32/src/commands.rs +++ b/gem-remotes-esp32/src/commands.rs @@ -23,10 +23,15 @@ pub enum Commands { ButtonTimerExpired, ButtonTimerRestart, ButtonTimerClear, + PairTimerExpired, + AllowPairing, // Also serves as the timer restart command + PairTimerClear, NotifyMotorUp {data: u8}, NotifyMotorDown {data: u8}, NotifyMotorStop {data: u8}, + + EraseBleBonds, } #[non_exhaustive] diff --git a/gem-remotes-esp32/src/main.rs b/gem-remotes-esp32/src/main.rs index e50b593..75eec7c 100644 --- a/gem-remotes-esp32/src/main.rs +++ b/gem-remotes-esp32/src/main.rs @@ -1,5 +1,6 @@ const BUTTON_HOLD_TIME_MS: u64 = 500; const STOP_SAFETY_TIME_MS: u64 = 2000; +const PAIR_TIME_MS: u64 = 10000; //180000; TODO:Debug go back to 3 minutes from 10s // Crates used in release use log::*; //{trace, debug, info, warn, error} @@ -19,12 +20,15 @@ mod motor_controller; mod motor_driver; mod message_timer; mod ble_server; +mod pair_button_driver; use crate::message_timer::MessageTimer; use crate::commands::Commands; //TODO: limit switch driver, would be good in long run if it checked limit switches periodically (every 1s?) to ensure they are still functioning +//TODO: pair button driver, listens to PIC uart or eventually physical button, sends pairing active command and resets pairing timer. + fn main() { // Do basic initialization esp_idf_svc::sys::link_patches(); @@ -84,6 +88,12 @@ async fn main_loop() -> Result<()> { Commands::StopTimerExpired, STOP_SAFETY_TIME_MS, &mut dp); + let mut pairing_timer = MessageTimer::::new_on_dispatch( + Commands::AllowPairing, + Commands::PairTimerClear, + Commands::PairTimerExpired, + PAIR_TIME_MS, + &mut dp); let ble_server = ble_server::BleServer::new(&mut dp); let executor = Executor::new(); @@ -97,6 +107,7 @@ async fn main_loop() -> Result<()> { tasks.push(executor.spawn(motor_control.run())); tasks.push(executor.spawn(button_timer.run())); tasks.push(executor.spawn(stopping_timer.run())); + tasks.push(executor.spawn(pairing_timer.run())); tasks.push(executor.spawn(ble_server.run())); tasks.push(executor.spawn(motor_driver.run())); tasks.push(executor.spawn(dp.cmd_loop())); diff --git a/gem-remotes-esp32/src/pair_button_driver.rs b/gem-remotes-esp32/src/pair_button_driver.rs new file mode 100644 index 0000000..df23b2a --- /dev/null +++ b/gem-remotes-esp32/src/pair_button_driver.rs @@ -0,0 +1,32 @@ +use log::*; //{trace, debug, info, warn, error} +use anyhow::Result; +use async_channel::Sender; +use esp_idf_svc::timer::EspTaskTimerService; +use core::time::Duration; + +use crate::commands::Commands; +use crate::dispatch::Dispatch; + +type SendQ = Sender; + +pub struct PairButtonDriver { + _send: SendQ +} + +impl PairButtonDriver { + pub fn new(dp: &mut Dispatch) -> Self { + let s = dp.get_cmd_channel(); + Self { _send: s } + } + + pub async fn run(&self) -> Result<()> { + let timer_service = EspTaskTimerService::new()?; + let mut async_timer = timer_service.timer_async()?; + debug!("Waiting on pairing button presses"); + loop { + //TO DO: Watch for incoming pair button presses from the PIC and/or hardware buttons + async_timer.after(Duration::from_millis(10000)).await?; // no need to panic on test console driver timer failure + //When we find a press, send PicRecvPair + } + } +} \ No newline at end of file diff --git a/gem-remotes-esp32/src/test_console.rs b/gem-remotes-esp32/src/test_console.rs index 862e340..34eaae5 100644 --- a/gem-remotes-esp32/src/test_console.rs +++ b/gem-remotes-esp32/src/test_console.rs @@ -39,6 +39,9 @@ pub enum Menu<'a> { /// Simulate the PIC controller sending us a Stop character PicRecvStop, + /// Simulate the PIC controller sending a "pair" button press + PicRecvPair, + /// Send a bluetooth characteristic: Up BluetoothUp { /// 0 for not pressed, 1 for pressed @@ -74,10 +77,14 @@ pub enum Menu<'a> { /// Send a bluetooth characteristic: Wifi Password BluetoothWifiPassword { wifipass: &'a str }, + /// Change log level (None: 0, .. Tracing: 5) Log { level: u8 }, /// Abort (resets microcontroller) Abort, + + /// Erase BLE Bonding information + ClearBleBonds, } @@ -101,6 +108,10 @@ pub fn process_menu( cli.writer().write_str("Sending PicButtonStop command")?; let _ = dispatch.send_blocking(Commands::PicRecvStop); } + Menu::PicRecvPair => { + cli.writer().write_str("Sending PIC command and timer reset (the job of the pair button driver, once PIC interface exists)")?; //TODO: PIC get this. + let _ = dispatch.send_blocking(Commands::AllowPairing); + } Menu::BluetoothUp { data } => { cli.writer() .write_str("Sending BluetoothUp")?; @@ -180,6 +191,9 @@ pub fn process_menu( trace!("trace test"); } Menu::Abort => {panic!("CLI user requested abort");} + Menu::ClearBleBonds => { + let _ = dispatch.send_blocking(Commands::EraseBleBonds); + } } Ok(()) }