use bluer::{
adv::Advertisement,
gatt::local::{
Application, Characteristic, CharacteristicNotify, CharacteristicNotifyMethod, CharacteristicRead,
CharacteristicWrite, CharacteristicWriteMethod, Service,
},
};
use futures::FutureExt;
use std::{collections::BTreeMap, sync::Arc, time::Duration};
use tokio::{
io::{AsyncBufReadExt, BufReader},
sync::Mutex,
time::sleep,
};
include!("gatt.inc");
#[tokio::main(flavor = "current_thread")]
async fn main() -> bluer::Result<()> {
env_logger::init();
let session = bluer::Session::new().await?;
let adapter = session.default_adapter().await?;
adapter.set_powered(true).await?;
println!("Advertising on Bluetooth adapter {} with address {}", adapter.name(), adapter.address().await?);
let mut manufacturer_data = BTreeMap::new();
manufacturer_data.insert(MANUFACTURER_ID, vec![0x21, 0x22, 0x23, 0x24]);
let le_advertisement = Advertisement {
service_uuids: vec![SERVICE_UUID].into_iter().collect(),
manufacturer_data,
discoverable: Some(true),
local_name: Some("gatt_server".to_string()),
..Default::default()
};
let adv_handle = adapter.advertise(le_advertisement).await?;
println!("Serving GATT service on Bluetooth adapter {}", adapter.name());
let value = Arc::new(Mutex::new(vec![0x10, 0x01, 0x01, 0x10]));
let value_read = value.clone();
let value_write = value.clone();
let value_notify = value.clone();
let app = Application {
services: vec![Service {
uuid: SERVICE_UUID,
primary: true,
characteristics: vec![Characteristic {
uuid: CHARACTERISTIC_UUID,
read: Some(CharacteristicRead {
read: true,
fun: Box::new(move |req| {
let value = value_read.clone();
async move {
let value = value.lock().await.clone();
println!("Read request {:?} with value {:x?}", &req, &value);
Ok(value)
}
.boxed()
}),
..Default::default()
}),
write: Some(CharacteristicWrite {
write: true,
write_without_response: true,
method: CharacteristicWriteMethod::Fun(Box::new(move |new_value, req| {
let value = value_write.clone();
async move {
println!("Write request {:?} with value {:x?}", &req, &new_value);
let mut value = value.lock().await;
*value = new_value;
Ok(())
}
.boxed()
})),
..Default::default()
}),
notify: Some(CharacteristicNotify {
notify: true,
method: CharacteristicNotifyMethod::Fun(Box::new(move |mut notifier| {
let value = value_notify.clone();
async move {
tokio::spawn(async move {
println!(
"Notification session start with confirming={:?}",
notifier.confirming()
);
loop {
{
let mut value = value.lock().await;
println!("Notifying with value {:x?}", &*value);
if let Err(err) = notifier.notify(value.to_vec()).await {
println!("Notification error: {}", &err);
break;
}
println!("Decrementing each element by one");
for v in &mut *value {
*v = v.saturating_sub(1);
}
}
sleep(Duration::from_secs(5)).await;
}
println!("Notification session stop");
});
}
.boxed()
})),
..Default::default()
}),
..Default::default()
}],
..Default::default()
}],
..Default::default()
};
let app_handle = adapter.serve_gatt_application(app).await?;
println!("Service ready. Press enter to quit.");
let stdin = BufReader::new(tokio::io::stdin());
let mut lines = stdin.lines();
let _ = lines.next_line().await;
println!("Removing service and advertisement");
drop(app_handle);
drop(adv_handle);
sleep(Duration::from_secs(1)).await;
Ok(())
}