BLE Advertising
BLE advertising is how a device announces its presence to nearby devices (like phones or computers) and shares information so they can connect. It broadcasts the device name, available services, and capabilities.
Once a central device initiates a connection, the function accepts it and returns a GATT connection instance that can be used to communicate with the connected device.
#![allow(unused)]
fn main() {
/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect.
async fn advertise<'values, 'server, C: Controller>(
name: &'values str,
peripheral: &mut Peripheral<'values, C, DefaultPacketPool>,
server: &'server Server<'values>,
) -> Result<GattConnection<'values, 'server, DefaultPacketPool>, BleHostError<C::Error>> {
let mut advertiser_data = [0; 31];
let len = AdStructure::encode_slice(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::CompleteLocalName(name.as_bytes()),
],
&mut advertiser_data[..],
)?;
let advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..len],
scan_data: &[],
},
)
.await?;
info!("[adv] advertising");
let conn = advertiser.accept().await?.with_attribute_server(server)?;
info!("[adv] connection established");
Ok(conn)
}
}
Let’s break down this function and understand each step.
Preparing Advertisement Data
#![allow(unused)]
fn main() {
let mut advertiser_data = [0; 31];
let len = AdStructure::encode_slice(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::CompleteLocalName(name.as_bytes()),
],
&mut advertiser_data[..],
)?;
}
We create a buffer and encode two pieces of information:
- Flags:
LE_GENERAL_DISCOVERABLEmakes the device visible to all scanners, whileBR_EDR_NOT_SUPPORTEDindicates this is a BLE-only device (no classic Bluetooth). You can find more details about these flags here. - Device Name: The complete local name that appears when scanning for devices
The encode_slice function packs this data into the proper BLE advertising format and returns the actual length used.
Starting Advertisement
#![allow(unused)]
fn main() {
let advertiser = peripheral
.advertise(
&Default::default(),
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..len],
scan_data: &[],
},
)
.await?;
}
We start advertising using ConnectableScannableUndirected mode, which means:
- Connectable: Central devices can connect to us
- Scannable: Central devices can request additional information (though we provide empty
scan_datahere) - Undirected: We’re broadcasting to everyone, not targeting a specific device
The device is now visible to nearby BLE scanners.
Accepting Connections
#![allow(unused)]
fn main() {
let conn = advertiser.accept().await?.with_attribute_server(server)?;
Ok(conn)
}
The accept() call waits for a central device to initiate a connection. Once connected, we transform the connection into a GattConnection using with_attribute_server(), which attaches our GATT server (containing our services and characteristics) to the connection.