use btle::error::IOError; use btle::le; use btle::le::advertisement::{AdStructureType, StaticAdvBuffer}; use btle::le::advertisement_structures::local_name::CompleteLocalName; use btle::le::advertiser::AdvertisingInterval; #[allow(unused_imports)] use core::convert::{TryFrom, TryInto}; pub fn main() -> Result<(), Box> { let mut runtime = tokio::runtime::Builder::new() .enable_all() .build() .expect("can't make async runtime"); runtime.block_on(async move { #[cfg(unix)] dump_bluez( std::env::args() .skip(1) .next() .unwrap_or("0".to_owned()) .parse() .expect("invalid adapter id"), ) .await; #[cfg(feature = "hci_usb")] dump_usb().await?; #[cfg(not(unix))] dump_not_supported()?; Ok(()) }) } pub fn dump_not_supported() -> Result<(), Box> { eprintln!("no known support adapter for this platform. (When this example was written)"); Ok(()) } #[cfg(unix)] pub fn dump_bluez(adapter_id: u16) -> Result<(), Box> { use btle::error::StdError; let manager = btle::hci::socket::Manager::new().map_err(StdError)?; let socket = match manager.get_adapter_socket(btle::hci::socket::AdapterID(adapter_id)) { Ok(socket) => socket, Err(btle::hci::socket::HCISocketError::PermissionDenied) => { eprintln!("Permission denied error when opening the HCI socket. Maybe run as sudo?"); return Err(btle::hci::socket::HCISocketError::PermissionDenied) .map_err(StdError) .map_err(Into::into); } Err(e) => return Err(StdError(e).into()), }; let async_socket = btle::hci::socket::AsyncHCISocket::try_from(socket)?; let stream = btle::hci::stream::Stream::new(async_socket); let adapter = btle::hci::adapters::Adapter::new(stream); dump_adapter(adapter) .await .map_err(|e| Box::new(btle::error::StdError(e)))?; Result::<(), Box>::Ok(()) } #[cfg(feature = "hci_usb")] pub async fn dump_usb() -> Result<(), btle::hci::adapter::Error> { use btle::hci::usb; println!("opening first device..."); let device: usbw::libusb::device::Device = usb::device::bluetooth_adapters( usbw::libusb::context::Context::default() .map_err(btle::hci::usb::Error::from)? .device_list() .iter(), ) .next() .ok_or(IOError::NotFound)??; println!("using {:?}", device); let mut adapter = usb::adapter::Adapter::open(device)?; adapter.reset()?; dump_adapter(adapter).await } /// Block and wait for an enter press on `StdIn`. Similar to C++ `std::cin.ignore()`. pub fn cin_ignore() { std::io::stdin() .read_line(&mut String::new()) .expect("cin_ignore failed"); } pub async fn dump_adapter( adapter: A, ) -> Result<(), btle::hci::adapter::Error> { let adapter = btle::hci::adapters::Adapter::new(adapter); let mut le = adapter.le(); println!("resetting adapter..."); le.adapter.reset().await?; println!("settings advertise parameters..."); // Set BLE Scan parameters (when to scan, how long, etc) le.set_advertising_parameters(le::advertiser::AdvertisingParameters { // Advertise as fast as possible for testing interval_min: AdvertisingInterval::MIN, interval_max: AdvertisingInterval::MIN, ..Default::default() }) .await?; // Enable scanning for advertisement packets. let name_struct = btle::le::advertisement_structures::local_name::LocalName::Complete( CompleteLocalName::new("Hello from Rust!"), ); // StaticAdvBuffer is just a stack allocated Vec with enough room for a full advertisement. let buf = name_struct .pack_into_storage::() .map_err(btle::hci::StreamError::CommandError)?; println!("setting advertisement data to `{:?}`", buf.as_ref()); le.set_advertising_data(buf.as_ref()).await?; println!("enabling advertising..."); le.set_advertising_enable(true).await?; println!("advertising!"); cin_ignore(); println!("stopping..."); le.set_advertising_enable(false).await?; println!("done!"); Ok(()) }