bluetooth advertisement support
thanks for the library. I found I was able to see my devices; I wanted to communicate with them.
I'd like to communicate with you too and see if this makes sense or if you have other ideas on implementation? Let me know if you have any thoughts on what I've got setup so far. the windows implementation is the only implementation that works now.
I'm trying to understand why I'm getting these full errors:
Windows advertisement started with company ID: 59.
2024-10-27T15:18:46.976723Z ERROR bluest::windows::adapter: Unable to send AdvertisingDevice: TrySendError { kind: Full }
2024-10-27T15:18:47.178354Z ERROR bluest::windows::adapter: Unable to send AdvertisingDevice: TrySendError { kind: Full }
2024-10-27T15:18:47.178949Z ERROR bluest::windows::adapter: Unable to send AdvertisingDevice: TrySendError { kind: Full }
I will keep playing. There's dead code and junk in there (ignore the Adapter modifications that was testing); it's a work in progress. :)
it needs more work for review... just fyi im looking at it. if you have any suggestions or feedback let me know.
I'd like to contribute advertisement support but based on the filtering discussion #24; I'm concerned I may be spending time on something that won't be something you're interested in. without any additional feedback on this, I am holding on additional effort.
Sorry, I didn't look at this because it was closed. In the future, if you mark a work-in-progress PR as "draft" instead of closing it I'll be better able to review it.
Supporting host advertising is an appropriate thing for bluest to support, so I'm happy to consider PRs to add it. We just need to be careful with the API design so that it meets bluest's design goals of being cross-platform, idiomatic rust, and a thin wrapper around the OS APIs.
The biggest platform issue an advertising API is going to have is with CoreBluetooth (iOS/MacOS), which has the following wrinkles:
- Advertising is done through a different object than interactions with remote peripherals (
CBPeripheralManagervsCBCentralManager). This shouldn't be a big deal, but it means the advertising mechanism will need to be able to lazily create that object, without making users who aren't using host advertsing pay the cost of creating it. - The only advertisment data that is allowed is device name and service UUIDs (and I think the service UUIDs are restricted to be non-sensitive UUIDs as well). It's probably ok to just return an error if the user tries to provide disallowed advertisement data.
I'd suggest using the existing AdvertisementData struct to provide the advertising data. I'd also be inclined to use a RAII-style API where calling Adapter::start_advertising(data) returns an AdvertisingGuard that stops advertising when it is dropped.
I do not have a Mac as of now but would be interested in helping with Linux and Windows implementations.
The only advertisement data that is allowed is device name and service UUIDs
huh. crazy limitation.
I do not have a Mac as of now but would be interested in helping with Linux and Windows implementations.
That's fine, we can release an unstable version of the feature without Mac support. I just want to make sure the limitations of the Apple APIs are considered when designing the bluest API.
please apologize the mess; concepts are there, I'm still using the methods I originally placed in some example code I've yet to refactor.
Are you thinking something like this on the Adapter?
pub fn start_advertising(&self, data: AdvertisementData) -> Result<AdvertisingGuard, String> {
self.0.start_advertising(data)
}
I think just passing ManufacturerData would be more appropriate than AdvertismentData?; those other fields are not applicable.
#[derive(Debug, Clone)]
pub struct AdvertisingGuard {
pub(crate) advertisement: Advertisement,
}
impl Drop for AdvertisingGuard {
fn drop(&mut self) {
// Stop advertising when `AdvertisingGuard` is dropped.
self.advertisement.stop().expect("Failed to stop advertising");
}
}
Yes, that API is what I was thinking. We might also need a async fn stop(self) -> Result<(), Error> method on AdvertisingGuard to allow the user to asynchronously wait for the advertising to actually stop.
I think just passing ManufacturerData would be more appropriate than AdvertismentData?; those other fields are not applicable.
I’m not sure where you got that idea? Any of the fields in AdvertisingData can be included in an advertisement. In fact, as I mentioned above, ManufacturerData is not one of the allowed fields in CoreBluetooth.
https://stackoverflow.com/questions/63175411/ios-omits-manufacturer-data-from-advertisement-in-background-mode "define a 16 bit or 128 bit GATT Service UUID and send out an advert with attached data bytes" wow. yuck, I don't mean to get hung up on corebluetooth but they really made simple hard.
I don't plan to advertise a name or service. Was just thinking control of the raw data would be a sufficient interface. It doesn't matter to me much if that's the struct you want to pack things in. If we're headed towards automatically adding the name or constructing the the ManufacturerData for the caller; idk about that.
corebluetooth made basic BLE advertisements unusable. Do you see doing acrobatics to accomplish "advertisements" via GATT? trying to receive advertisements in the background is basic need and I'd like my devices to work with corebluetooth. now come to find I'm likely going to need a custom solution to get the data...
https://stackoverflow.com/questions/63175411/ios-omits-manufacturer-data-from-advertisement-in-background-mode "define a 16 bit or 128 bit GATT Service UUID and send out an advert with attached data bytes" wow. yuck, I don't mean to get hung up on corebluetooth but they really made simple hard.
Yes, CoreBluetooth is extremely locked-down for security and power management reasons. It's really designed for only two scenarios:
- Connecting to a (non-privileged) GATT service of a remote peripheral
- Making a GATT service available to remote clients
And right now bluest only supports the first of those.
I don't plan to advertise a name or service. Was just thinking control of the raw data would be a sufficient interface. It doesn't matter to me much if that's the struct you want to pack things in. If we're headed towards automatically adding the name or constructing the the ManufacturerData for the caller; idk about that.
You mostly don't have access to or the ability to manipulate the raw data of the advertisement packets with OS APIs (might be possible on Linux, definitely not the others). Even on Windows you have to construct an abstract Advertisement that the OS will serialize for you. bluest should not automatically add or construct anything, it should just make the OS APIs available.
corebluetooth made basic BLE advertisements unusable. Do you see doing acrobatics to accomplish "advertisements" via GATT? trying to receive advertisements in the background is basic need and I'd like my devices to work with corebluetooth. now come to find I'm likely going to need a custom solution to get the data...
I don't know your use-case, but if you want to work with CoreBluetooth your best bet is usually to just use GATT services.
let data = adv_data.to_custom_bytes();
let advertisement_data = AdvertisementData {
local_name: None,
manufacturer_data: Some(ManufacturerData {
company_id: 0x0059,
data: data,
}),
services: vec![],
service_data: HashMap::new(),
tx_power_level: None,
is_connectable: false,
};
let adapter_clone = adapter.clone();
let advertise_task = task::spawn(async move {
match adapter_clone.start_advertising(advertisement_data) {
Ok(advert) => {
println!("Advertising started successfully.");
// Wait for a specified duration to allow it to advertise
let advertise_duration = Duration::from_secs(5); // adjust duration as needed
tokio::time::sleep(advertise_duration).await;
// The `advert` (AdvertisingGuard) will stop advertising automatically on drop
println!("Advertising stopped after waiting for the specified duration.");
}
Err(e) => {
eprintln!("Failed to start advertising: {:?}", e);
}
}
});
this is what using the api looks like -- or let me know where I've gone wrong; the other fields don't apply in my case (just a normal non-connectable advertisement).
I know the documentation says only LocalName and UUIDsKey are supported. The code does work with AdvDataManufacturer on the my macbook. The manufacterer company_id comes through and the next two bytes are incorrect; usually F5 followed by another byte that doesn't match the data sent. Right now the advertisement creates a peripheral_manager per advertisement. I wonder if a lazy_static and shared manager might make more sense. the advertisements are working but not always... and if the machine sleeps; it doesn't continue scanning on wake.
I changed my hardware to read the data it needed from the later bytes and I'm happy to see it working. I may look at linux next. ownership; the guard is taking the ownership of the advertisement in the corebluetooth now. let me know if you have other thoughts.
its wip; there's cruft in there.
The linux scanning I'm having trouble getting the advertisements. Both my windows and macos machines are showing advertisements regularly (about every second) and the linux machine shows the first one and then doesn't report anymore. I added the code for the advertisements but I can't test it or move forward while I'm unable to get the advertisements. the linux implementation makes the inner on Adapter pub; I'm not sure what to do with the adapter...