Change name, basic pairing indicator flag
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
|
||||
use log::*; //{trace, debug, info, warn, error}
|
||||
use esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEAdvertising, BLEDevice, BLEServer, NimbleProperties, OnWriteArgs};
|
||||
use esp32_nimble::utilities::{mutex::Mutex, BleUuid};
|
||||
use esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEDevice, BLEServer, NimbleProperties, OnWriteArgs};
|
||||
use esp32_nimble::utilities::BleUuid;
|
||||
use anyhow::Result;
|
||||
use closure::closure;
|
||||
use bitflags::bitflags;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::dispatch::{Dispatch, RecvQ, SendQ};
|
||||
use crate::commands::Commands;
|
||||
@ -14,10 +17,15 @@ const BLE_MAX_INTERVAL: u16 = 48; // x 1.25ms
|
||||
const BLE_LATENCY: u16 = 0; // Number of packets that can be missed, extending interval
|
||||
const BLE_TIMEOUT: u16 = 500; // x10ms
|
||||
|
||||
const SVC_DATA_PAIRING: [u8; 1] = [1]; // TODO: bitflag these
|
||||
const SVC_DATA_NO_PAIRING: [u8; 1] = [0];
|
||||
|
||||
// Bit flags in the custom service byte.
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct SvcFlags: u8 {
|
||||
const PAIRING_MODE = 1 << 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Names appear to truncate at 31 characters.
|
||||
const DEVICE_NAME: &str = "Gem Remotes";
|
||||
|
||||
// The [u8] version is, for whatever reason, little-endian. This should equate to the UUID "9966ad5a-f13c-4b61-ba66-0861e08d09b4".
|
||||
@ -27,10 +35,14 @@ const UUID_SERVICE_LIFT: BleUuid = uuid128!("c1400000-8dda-45a3-959b-d23a0f8f53d
|
||||
const UUID_BUTTON_UP: BleUuid = uuid128!("c1401121-8dda-45a3-959b-d23a0f8f53d7");
|
||||
const UUID_BUTTON_DOWN: BleUuid = uuid128!("c1401122-8dda-45a3-959b-d23a0f8f53d7");
|
||||
const UUID_BUTTON_STOP: BleUuid = uuid128!("c1401123-8dda-45a3-959b-d23a0f8f53d7");
|
||||
const UUID_BLUETOOTH_NAME: BleUuid = uuid128!("c1401224-8dda-45a3-959b-d23a0f8f53d7");
|
||||
|
||||
pub struct BleServer {
|
||||
send_q: SendQ,
|
||||
recv_q: RecvQ,
|
||||
svc_flags: SvcFlags,
|
||||
dev_name: String,
|
||||
|
||||
}
|
||||
|
||||
impl BleServer {
|
||||
@ -42,23 +54,27 @@ impl BleServer {
|
||||
Commands::PairTimerExpired,
|
||||
Commands::AllowPairing,
|
||||
Commands::EraseBleBonds,
|
||||
Commands::BluetoothName { data: Arc::new(String::new())},
|
||||
];
|
||||
let r = dp.get_callback_channel(&cmds);
|
||||
let s = dp.get_cmd_channel();
|
||||
let dev_name = DEVICE_NAME; //TODO: read this from NVS if it is present.
|
||||
BleServer {
|
||||
send_q: s.clone(),
|
||||
recv_q: r.clone(),
|
||||
svc_flags: SvcFlags::empty(),
|
||||
dev_name: String::from_str(dev_name).unwrap(), //Unwrap should be fine here because we control this string
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&self) -> Result<()> {
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
match self.do_run().await {
|
||||
Ok(_) => {error!("Exited bluetooth server wait loop with no error.");panic!();}
|
||||
Err(e) => {error!("Bluetooth task encountered error {}", e);panic!();}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn do_run(&self) -> Result<()> {
|
||||
pub async fn do_run(&mut self) -> Result<()> {
|
||||
trace!("Entering BLE Run");
|
||||
let ble_device = BLEDevice::take();
|
||||
set_device_security(ble_device);
|
||||
@ -69,7 +85,7 @@ impl BleServer {
|
||||
trace!("Setting up GATT");
|
||||
//TODO: require authentication (bonding counts?) for these!
|
||||
let sender = self.send_q.clone();
|
||||
//let mut cmd = Commands::BluetoothUp { data: 0 };
|
||||
// --- Button Up Bluetooth GATT ----------------------------------------------------------
|
||||
let button_up = lift_service.lock().create_characteristic(
|
||||
UUID_BUTTON_UP,
|
||||
NimbleProperties::READ | NimbleProperties::WRITE_NO_RSP | NimbleProperties::NOTIFY,
|
||||
@ -78,7 +94,7 @@ impl BleServer {
|
||||
.on_write(closure!(clone sender, |args: &mut OnWriteArgs| {
|
||||
on_bluetooth_cmd(&sender, args, Commands::BluetoothUp {data: 0})
|
||||
}));
|
||||
|
||||
// --- Button Down Bluetooth GATT --------------------------------------------------------
|
||||
let button_down = lift_service.lock().create_characteristic(
|
||||
UUID_BUTTON_DOWN,
|
||||
NimbleProperties::READ | NimbleProperties::WRITE_NO_RSP | NimbleProperties::NOTIFY,
|
||||
@ -87,7 +103,7 @@ impl BleServer {
|
||||
.on_write(closure!(clone sender, |args: &mut OnWriteArgs| {
|
||||
on_bluetooth_cmd(&sender, args, Commands::BluetoothDown {data: 0})
|
||||
}));
|
||||
|
||||
// --- Button Stop Bluetooth GATT --------------------------------------------------------
|
||||
let button_stop = lift_service.lock().create_characteristic(
|
||||
UUID_BUTTON_STOP,
|
||||
NimbleProperties::READ | NimbleProperties::WRITE_NO_RSP | NimbleProperties::NOTIFY,
|
||||
@ -96,12 +112,17 @@ impl BleServer {
|
||||
.on_write(closure!(clone sender, |args: &mut OnWriteArgs| {
|
||||
on_bluetooth_cmd(&sender, args, Commands::BluetoothStop {_data: 0})
|
||||
}));
|
||||
|
||||
|
||||
let ble_advertiser = ble_device.get_advertising();
|
||||
|
||||
// TODO: we will need to enable / disable the ability to pair!
|
||||
advertise(ble_advertiser)?;
|
||||
// --- Device Name Bluetooth GATT --------------------------------------------------------
|
||||
let device_name = lift_service.lock().create_characteristic(
|
||||
UUID_BLUETOOTH_NAME,
|
||||
NimbleProperties::READ | NimbleProperties::WRITE,
|
||||
);
|
||||
device_name.lock().set_value(self.dev_name.as_bytes())
|
||||
.on_write(closure!(clone sender, |args: &mut OnWriteArgs| {
|
||||
on_bluetooth_cmd(&sender, args, Commands::BluetoothName { data: Arc::new(String::new())}.clone())
|
||||
}));
|
||||
// Default to not pairable
|
||||
self.advertise_unpairable()?;
|
||||
|
||||
loop {
|
||||
debug!("Waiting for updates that should be notified via bluetooth");
|
||||
@ -119,17 +140,19 @@ impl BleServer {
|
||||
button_up.lock().set_value(&[data]).notify();
|
||||
}
|
||||
Commands::PairTimerExpired => {
|
||||
//advertise_to_direct(ble_advertiser).expect("Failed to exit pairing mode");
|
||||
debug!("pairing mode / non-pairing mode not currently supported");
|
||||
self.advertise_unpairable()?;
|
||||
self.send_q.send(Commands::PairTimerClear).await?;
|
||||
}
|
||||
Commands::AllowPairing => {
|
||||
//advertise_to_undirected(ble_advertiser).expect("Failed to enter pairing mode");
|
||||
self.advertise_pairable()?;
|
||||
debug!("pairing mode / non-pairing mode not currently supported");
|
||||
}
|
||||
Commands::EraseBleBonds => {
|
||||
ble_device.delete_all_bonds().expect("Failed trying to erase bluetooth bonding information");
|
||||
}
|
||||
Commands::BluetoothName { data } => {
|
||||
self.update_name(data.as_str())?; // TODO: consider not failing over trivial name change problem
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid command received by bluetooth handler {:?}", cmd);
|
||||
// No need to reboot as state is recoverable.
|
||||
@ -138,6 +161,45 @@ impl BleServer {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_name(&mut self, new_name: &str) -> Result<()> {
|
||||
self.dev_name.clear();
|
||||
let mut name = new_name;
|
||||
if name.len() > 31 {name = &new_name[..31];}
|
||||
self.dev_name.push_str(name);
|
||||
|
||||
self.advertise()?;
|
||||
//TODO: save name to nvs
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn advertise(&self) -> Result<()> {
|
||||
let ble_dev = BLEDevice::take();
|
||||
let ble_adv = ble_dev.get_advertising();
|
||||
let mut adv = ble_adv.lock();
|
||||
if adv.is_advertising() {
|
||||
adv.stop()?;
|
||||
}
|
||||
let mut beacon_data = BLEAdvertisementData::new();
|
||||
beacon_data.name(self.dev_name.as_str());
|
||||
beacon_data.service_data(UUID_SERVICE_PAIR, &[self.svc_flags.bits()]);
|
||||
adv
|
||||
.advertisement_type(ConnMode::Und)
|
||||
.set_data(&mut beacon_data)?;
|
||||
info!("Staring Bluetooth Server");
|
||||
adv.start()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn advertise_pairable(&mut self) -> Result<()> {
|
||||
self.svc_flags = self.svc_flags | SvcFlags::PAIRING_MODE;
|
||||
self.advertise()
|
||||
}
|
||||
|
||||
fn advertise_unpairable(&mut self) -> Result<()> {
|
||||
self.svc_flags = self.svc_flags - SvcFlags::PAIRING_MODE;
|
||||
self.advertise()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn on_bluetooth_cmd(sender: &SendQ, args: &mut OnWriteArgs, cmd: Commands) {
|
||||
@ -159,6 +221,17 @@ fn on_bluetooth_cmd(sender: &SendQ, args: &mut OnWriteArgs, cmd: Commands) {
|
||||
sender.send_blocking(Commands::BluetoothStop {_data: v[0]} )
|
||||
} else {error!("Received zero-byte bluetooth characteristic update {:?}", cmd); Ok(())}
|
||||
}
|
||||
Commands::BluetoothName { data: _ } => {
|
||||
if v.len() > 0 {
|
||||
let name = String::from_utf8_lossy(v).into_owned();
|
||||
sender.send_blocking(Commands::BluetoothName { data: Arc::new(name) })
|
||||
} else {
|
||||
// If the user clears the name, revert to the default.
|
||||
// Unwrap is safe here because we control the string.
|
||||
sender.send_blocking(Commands::BluetoothName { data: Arc::new(String::from_str(DEVICE_NAME).unwrap()) })
|
||||
}
|
||||
}
|
||||
//TODO when we get name changes, truncate to 31 chars, because that's what we have anyway.
|
||||
_ => {error!("Tried to handle an unknown bluetooth command: {:?}",cmd);Ok(())}
|
||||
};
|
||||
match attempt {
|
||||
@ -199,20 +272,5 @@ fn set_server_callbacks(server: &mut BLEServer, sender: SendQ) {
|
||||
});
|
||||
}
|
||||
|
||||
fn advertise(advertiser: &Mutex<BLEAdvertising>) -> Result<()> {
|
||||
trace!("Setting up advertiser");
|
||||
let mut beacon_data = BLEAdvertisementData::new();
|
||||
beacon_data.name(DEVICE_NAME);
|
||||
//.add_service_uuid(UUID_SERVICE_PAIR);
|
||||
beacon_data.service_data(UUID_SERVICE_PAIR, &SVC_DATA_PAIRING);
|
||||
advertiser
|
||||
.lock()
|
||||
.advertisement_type(ConnMode::Und)
|
||||
.set_data(&mut beacon_data)?;
|
||||
info!("Staring Bluetooth Server");
|
||||
advertiser.lock().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.
|
||||
Reference in New Issue
Block a user