Bluetooth LE Advertisement
Bluetooth LE Advertisement is a way to broadcast data without a connection. It is used in BLE Beacons, Apple Continuity, Drone Remote ID, etc. The Bluetooth Framework allows you to receive, decode, and transmit custom advertisement packets, including Extended Advertisement for Bluetooth 5.
Bluetooth Low Energy allows devices to broadcast some information. Such broadcasting is known as Bluetooth LE Advertisement. It is widely used by BLE devices to transmit data without establishing a connection. The advertising is also used during the discovery process and includes important information about Bluetooth-enabled devices.
The common usage of Bluetooth LE Advertising is Bluetooth Beacons. A BLE beacon is a hardware transmitter - a class of Bluetooth
Low Energy devices that broadcasts its identifier to nearby portable electronic devices. The technology enables smartphones, tablets, and other
devices to perform actions when in close proximity to a beacon. Also, Apple uses Bluetooth LE Advertising in their
Continuity protocol. The Drone Remote ID (DRI) is the
other technology that also uses the BLE Advertisement.
The Bluetooth Framework provides methods to receive and decode Bluetooth LE advertisement
packets (also known as frames) as well as build and broadcast your own (custom) advertisements. The library package includes the
Beacons sample application that shows how to enable Bluetooth LE advertisement capturing and broadcasting. Below you can find
a short overview of the Beacons sample application with code snippets.
Receive BLE Advertisements
To receive Bluetooth LE advertisements with the Bluetooth Framework, use the
wclBluetoothLeBeaconWatcher class. It monitors nearby BLE devices and decodes their advertisement frames. The class can
decode the following Bluetooth LE advertisements:
- Appearance
- Basic Information
- Extended Information
- 16 bit Service Data
- 32 bit Service Data
- 128 bit Service Data
- 16 bit Solicitation Service
- 32 bit Solicitation Service
- 128 bit Solicitation Service
- TX Power
- 128 bit UUID
- Alt Beacon
- Eddystone TLM
- Eddystone UID
- Eddystone URL
- Apple Continuity
- Microsoft CDP Beacon
- Drone Remote ID
- Manufacturer Raw
The unknown frames passed to an application as a raw byte stream. The stream can be decoded by your application if needed.
Starting the Beacon Watcher
Before capturing advertisements you must obtain a working radio from the wclBluetoothManager, configure the
watcher's scanning mode, and hook the frame events. The snippet below shows the minimal start/stop logic and an example event handler.
procedure TfmMain.btWatcherStartClick(Sender: TObject);
var
	Radio: TwclBluetoothRadio;
	Res: Integer;
begin
	Radio := GetRadio;
	if Radio <> nil then begin
		BeaconWatcher.ScanningMode := smActive;
		BeaconWatcher.AllowExtendedAdvertisements := True;
		Res := BeaconWatcher.Start(Radio);
		if Res <> WCL_E_SUCCESS then
			Trace('Start watcher failed', Res);
	end;
end;
procedure TfmMain.btWatcherStopClick(Sender: TObject);
var
	Res: Integer;
begin
	Res := BeaconWatcher.Stop;
	if Res <> WCL_E_SUCCESS then
		Trace('Stop watcher failed', Res);
end;
procedure TfmMain.BeaconWatcherAdvertisementFrameInformation(Sender: TObject;
	const Address: Int64; const Timestamp: Int64; const Rssi: SByte; const Name: string;
	const PacketType: TwclBluetoothLeAdvertisementType; const Flags: TwclBluetoothLeAdvertisementFlags);
begin
	Log('Frame from ' + IntToHex(Address, 12) + ' Name: ' + Name + ' RSSI: ' + IntToStr(Rssi));
end;
void __fastcall TfmMain::btWatcherStartClick(TObject *Sender)
{
	TwclBluetoothRadio* Radio = GetRadio();
	if (Radio != NULL)
	{
		BeaconWatcher->ScanningMode = smActive;
		BeaconWatcher->AllowExtendedAdvertisements = true;
		int Res = BeaconWatcher->Start(Radio);
		if (Res != WCL_E_SUCCESS)
			Trace("Start watcher failed", Res);
	}
}
void __fastcall TfmMain::btWatcherStopClick(TObject *Sender)
{
	int Res = BeaconWatcher->Stop();
	if (Res != WCL_E_SUCCESS)
		Trace("Stop watcher failed", Res);
}
void __fastcall TfmMain::BeaconWatcherAdvertisementFrameInformation(TObject *Sender,
	const __int64 Address, const __int64 Timestamp, const SByte Rssi, const String Name,
	const TwclBluetoothLeAdvertisementType PacketType, const TwclBluetoothLeAdvertisementFlags Flags)
{
	Log("Frame from " + IntToHex(Address, 12) + " Name: " + Name + " RSSI: " + IntToStr(Rssi));
}
private void btWatcherStart_Click(Object sender, EventArgs e)
{
	wclBluetoothRadio Radio = GetRadio();
	if (Radio != null)
	{
		BeaconWatcher.ScanningMode = wclBluetoothLeScanningMode.smActive;
		BeaconWatcher.AllowExtendedAdvertisements = true;
		Int32 Res = BeaconWatcher.Start(Radio);
		if (Res != wclErrors.WCL_E_SUCCESS)
			Trace("Start watcher failed", Res);
	}
}
private void btWatcherStop_Click(Object sender, EventArgs e)
{
	Int32 Res = BeaconWatcher.Stop();
	if (Res != wclErrors.WCL_E_SUCCESS)
		Trace("Stop watcher failed", Res);
}
void BeaconWatcherAdvertisementFrameInformation(Object Sender,
	long Address, long Timestamp, sbyte Rssi, string Name,
	wclBluetoothLeAdvertisementType PacketType, wclBluetoothLeAdvertisementFlag Flags)
{
	Log("Frame from " + Address.ToString("X12") + " Name: " + Name + " RSSI: " + Rssi.ToString());
}
Private Sub btWatcherStart_Click(sender As Object, e As EventArgs) Handles btWatcherStart.Click
	Dim Radio As wclBluetoothRadio = GetRadio()
	If Radio IsNot Nothing Then
		BeaconWatcher.ScanningMode = wclBluetoothLeScanningMode.smActive
		BeaconWatcher.AllowExtendedAdvertisements = True
		Dim Res As Int32 = BeaconWatcher.Start(Radio)
		If Res <> wclErrors.WCL_E_SUCCESS Then
			Trace("Start watcher failed", Res)
		End If
		End If
End Sub
Private Sub btWatcherStop_Click(sender As Object, e As EventArgs) Handles btWatcherStop.Click
	Dim Res As Int32 = BeaconWatcher.Stop()
	If Res <> wclErrors.WCL_E_SUCCESS Then
		Trace("Stop watcher failed", Res)
	End If
End Sub
Private Sub BeaconWatcherAdvertisementFrameInformation(Sender As Object,
	Address As Long, Timestamp As Long, Rssi As SByte, Name As String,
	PacketType As wclBluetoothLeAdvertisementType,
	Flags As wclBluetoothLeAdvertisementFlag) Handles BeaconWatcher.OnAdvertisementFrameInformation
	Log("Frame from " & Address.ToString("X12") & " Name: " & Name & " RSSI: " & Rssi.ToString())
End Sub
void CBeaconsDlg::OnBnClickedButtonWatcherStart()
{
	CwclBluetoothRadio* Radio = GetRadio();
	if (Radio != NULL)
	{
		BeaconWatcher.ScanningMode = smActive;
		BeaconWatcher.AllowExtendedAdvertisements = true;
		int Res = BeaconWatcher.Start(Radio);
		if (Res != WCL_E_SUCCESS)
			Trace(_T("Start watcher failed"), Res);
	}
}
void CBeaconsDlg::OnBnClickedButtonWatcherStop()
{
	int Res = BeaconWatcher.Stop();
	if (Res != WCL_E_SUCCESS)
		Trace(_T("Stop watcher failed"), Res);
}
CwclBluetoothRadio* CBeaconsDlg::GetRadio()
{
	if (!BluetoothManager.Active)
		BluetoothManager.Open();
	CwclBluetoothRadio* Radio = NULL;
	BluetoothManager.GetLeRadio(Radio);
	return Radio;
}
void CBeaconsDlg::BeaconWatcherAdvertisementFrameInformation(void* Sender,
	const __int64 Address, const __int64 Timestamp, const char Rssi,
	const tstring& Name, const wclBluetoothLeAdvertisementType PacketType,
	const wclBluetoothLeAdvertisementFlags& Flags)
{
	UNREFERENCED_PARAMETER(Sender);
	// Store or display the frame data
}
Transmit BLE Advertising
The Bluetooth Framework also allows you to transmit your own BLE advertisements. The
wclBluetoothLeAdvertiser class switches your application to Bluetooth LE Advertiser mode. The class allows advertising
a few predefined advertisements as well as any custom advertisement (there are some restrictions described below).
Supported Advertisement Types
The following advertisement types are reserved and are not allowed:
| Data Type (hex) | Constant (UUID) | Description |
|---|---|---|
00 |
LE_GAP_AD_TYPE_NONE |
Empty flags |
01 |
LE_GAP_AD_TYPE_FLAGS |
Flags |
08 |
LE_GAP_AD_TYPE_LOCAL_NAME_SHORT |
Shortened Local Name |
09 |
LE_GAP_AD_TYPE_LOCAL_NAME_COMPLETE |
Complete Local Name |
1B |
LE_GAP_AD_TYPE_ADDRESS |
LE Bluetooth Device Address |
1C |
LE_GAP_AD_TYPE_ROLE |
LE Role |
The following advertisement types are system-reserved and are not allowed if the advertisement is used with the Microsoft Bluetooth driver:
| Data Type (hex) | Constant (UUID) | Description |
|---|---|---|
02 |
LE_GAP_AD_TYPE_SERVICES_16_MORE |
Incomplete List of 16-bit Service UUIDs |
03 |
LE_GAP_AD_TYPE_SERVICES_16_ALL |
Complete List of 16-bit Service Class UUIDs |
04 |
LE_GAP_AD_TYPE_SERVICES_32_MORE |
Incomplete List of 32-bit Service UUIDs |
05 |
LE_GAP_AD_TYPE_SERVICES_32_ALL |
Complete List of 32-bit Service Class UUIDs |
06 |
LE_GAP_AD_TYPE_SERVICES_128_MORE |
Incomplete List of 128-bit Service UUIDs |
07 |
LE_GAP_AD_TYPE_SERVICES_128_ALL |
Complete List of 128-bit Service Class UUIDs |
0A |
LE_GAP_AD_TYPE_TXPOWER |
Tx Power Level |
0D |
LE_GAP_AD_TYPE_COD |
Class of Device |
0E |
LE_GAP_AD_TYPE_PAIRING_HASH_C |
Simple Pairing Hash C192 |
0F |
LE_GAP_AD_TYPE_PAIRING_RAND_R |
Simple Pairing Randomizer R192 |
10 |
LE_GAP_AD_TYPE_DEVICE_ID |
Security Manager TK Values |
11 |
LE_GAP_AD_TYPE_SM_OOB_FLAGS |
Security Manager Out-of-Band Flags |
12 |
LE_GAP_AD_TYPE_CON_INTERVALS |
Slave Connection Interval Range |
17 |
LE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS |
Public Target Address |
18 |
LE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS |
Random Target Address |
19 |
LE_GAP_AD_TYPE_APPEARANCE |
Appearance |
1A |
LE_GAP_AD_TYPE_ADV_INTERVAL |
Advertising Interval |
1D |
LE_GAP_AD_TYPE_PAIRING_HASH_C_256 |
Simple Pairing Hash C256 |
1E |
LE_GAP_AD_TYPE_PAIRING_RAND_R_256 |
Simple Pairing Randomizer R256 |
3D |
LE_GAP_AD_TYPE_3D_INFO |
3D Information Data |
Extended Advertisement
If your hardware supports Bluetooth 5 and above, the Bluetooth Framework
can use the Extended Advertisement Format when used with the Microsoft Bluetooth driver on Windows 10 and above.
The Extended Advertisement Format can be enabled by setting the UseExtendedAdvertisement property of the
wclBluetoothLeAdvertiser class. When this property is set to true, the three
additional properties are used during advertisement:
Anonymous
If set totrue, the device address is not included in the advertisement header.IncludeTxRssi
If set totrue, the transmit power level is included in the advertisement header.PrefferedTxRssi
If enabled (to disable, set the property's value to-127), requests that the radio use the indicated transmit power level for the advertisement.
If your application enables the Extended Advertisement by setting the UseExtendedAdvertisement property to
true but your hardware or OS does not support this feature, the
WCL_E_BLUETOOTH_LE_EXT_ADV_NOT_SUPPORTED error code will be returned when an application starts the advertisement.
Starting the LE Advertiser
The wclBluetoothLeAdvertiser class allows you to broadcast standard beacon frames (iBeacon,
Eddystone, AltBeacon) as well as fully custom raw advertisements. The example below creates an iBeacon and
a manufacturer-specific advertisement, enables extended advertising, and starts the transmission.
const
	BEACON_UUID: TGUID = '{09039835-4A80-443B-87AA-DC565D09EA61}';
procedure TfmMain.btAdvertiserStartClick(Sender: TObject);
var
	Radio: TwclBluetoothRadio;
	Adv: TwclBluetoothLeAdvertisement;
	Data: TwclBluetoothLeAdvertisementFrameRawData;
	Res: Integer;
begin
	Radio := GetRadio;
	if Radio = nil then
		Exit;
	LeAdvertiser.Clear;
	// iBeacon
	Adv := TwclBluetoothLeiBeaconAdvertisement.Create(-5, $0101, $0202, BEACON_UUID);
	LeAdvertiser.Add(Adv);
	// Manufacturer specific
	SetLength(Data, 2);
	Data[0] := $12;
	Data[1] := $34;
	Adv := TwclBluetoothLeManufacturerAdvertisement.Create($010E, Data);
	LeAdvertiser.Add(Adv);
	// Extended advertisement (Bluetooth 5)
	LeAdvertiser.UseExtendedAdvertisement := True;
	LeAdvertiser.Anonymous := False;
	LeAdvertiser.IncludeTxRssi := True;
	LeAdvertiser.PreferredTxRssi := 10;
	LeAdvertiser.Interval := 100;
	Res := LeAdvertiser.Start(Radio);
	if Res <> WCL_E_SUCCESS then
		Trace('Start advertiser failed', Res);
end;
procedure TfmMain.btAdvertiserStopClick(Sender: TObject);
var
	Res: Integer;
begin
	Res := LeAdvertiser.Stop;
	if Res <> WCL_E_SUCCESS then
		Trace('Stop advertiser failed', Res);
end;
const GUID BEACON_UUID = {0x09039835, 0x4A80, 0x443B, {0x87, 0xAA, 0xDC, 0x56, 0x5D, 0x09, 0xEA, 0x61}};
void __fastcall TfmMain::btAdvertiserStartClick(TObject *Sender)
{
	TwclBluetoothRadio* Radio = GetRadio();
	if (Radio == NULL)
		return;
	LeAdvertiser->Clear();
	// iBeacon
	TwclBluetoothLeiBeaconAdvertisement* Adv;
	Adv = new TwclBluetoothLeiBeaconAdvertisement(-5, 0x0101, 0x0202, BEACON_UUID);
	LeAdvertiser->Add(Adv);
	// Manufacturer specific
	TwclBluetoothLeAdvertisementFrameRawData Data;
	Data.Length = 2;
	Data[0] = 0x12;
	Data[1] = 0x34;
	Adv = new TwclBluetoothLeManufacturerAdvertisement(0x010E, Data);
	LeAdvertiser->Add(Adv);
	// Extended advertisement
	LeAdvertiser->UseExtendedAdvertisement = true;
	LeAdvertiser->Anonymous = false;
	LeAdvertiser->IncludeTxRssi = true;
	LeAdvertiser->PreferredTxRssi = 10;
	LeAdvertiser->Interval = 100;
	int Res = LeAdvertiser->Start(Radio);
	if (Res != WCL_E_SUCCESS)
		Trace("Start advertiser failed", Res);
}
void __fastcall TfmMain::btAdvertiserStopClick(TObject *Sender)
{
	int Res = LeAdvertiser->Stop();
	if (Res != WCL_E_SUCCESS)
		Trace("Stop advertiser failed", Res);
}
private static readonly Guid BEACON_UUID = new Guid("{09039835-4A80-443B-87AA-DC565D09EA61}");
private void btAdvertiserStart_Click(Object sender, EventArgs e)
{
	wclBluetoothRadio Radio = GetRadio();
	if (Radio == null)
		return;
	LeAdvertiser.Clear();
	// iBeacon
	wclBluetoothLeAdvertisement Adv = new wclBluetoothLeiBeaconAdvertisement(-5, 0x0101, 0x0202, BEACON_UUID);
	LeAdvertiser.Add(Adv);
	// Manufacturer specific
	byte[] Data = new byte[2] { 0x12, 0x34 };
	Adv = new wclBluetoothLeManufacturerAdvertisement(0x010E, Data);
	LeAdvertiser.Add(Adv);
	// Extended advertisement
	LeAdvertiser.UseExtendedAdvertisement = true;
	LeAdvertiser.Anonymous = false;
	LeAdvertiser.IncludeTxRssi = true;
	LeAdvertiser.PreferredTxRssi = 10;
	LeAdvertiser.Interval = 100;
	Int32 Res = LeAdvertiser.Start(Radio);
	if (Res != wclErrors.WCL_E_SUCCESS)
		Trace("Start advertiser failed", Res);
}
private void btAdvertiserStop_Click(Object sender, EventArgs e)
{
	Int32 Res = LeAdvertiser.Stop();
	if (Res != wclErrors.WCL_E_SUCCESS)
		Trace("Stop advertiser failed", Res);
}
Private Shared ReadOnly BEACON_UUID As New Guid("{09039835-4A80-443B-87AA-DC565D09EA61}")
Private Sub btAdvertiserStart_Click(sender As Object, e As EventArgs) Handles btAdvertiserStart.Click
	Dim Radio As wclBluetoothRadio = GetRadio()
	If Radio Is Nothing Then Return
	LeAdvertiser.Clear()
	' iBeacon
	Dim Adv As wclBluetoothLeAdvertisement = New wclBluetoothLeiBeaconAdvertisement(-5, &H101, &H202, BEACON_UUID)
	LeAdvertiser.Add(Adv)
	' Manufacturer specific
	Dim Data As Byte() = New Byte() {&H12, &H34}
	Adv = New wclBluetoothLeManufacturerAdvertisement(&H10E, Data)
	LeAdvertiser.Add(Adv)
	' Extended advertisement
	LeAdvertiser.UseExtendedAdvertisement = True
	LeAdvertiser.Anonymous = False
	LeAdvertiser.IncludeTxRssi = True
	LeAdvertiser.PreferredTxRssi = 10
	LeAdvertiser.Interval = 100
	Dim Res As Int32 = LeAdvertiser.Start(Radio)
	If Res <> wclErrors.WCL_E_SUCCESS Then Trace("Start advertiser failed", Res)
End Sub
Private Sub btAdvertiserStop_Click(sender As Object, e As EventArgs) Handles btAdvertiserStop.Click
	Dim Res As Int32 = LeAdvertiser.Stop()
	If Res <> wclErrors.WCL_E_SUCCESS Then Trace("Stop advertiser failed", Res)
End Sub
static const GUID BEACON_UUID = { 0x09039835, 0x4A80, 0x443B, { 0x87, 0xAA, 0xDC, 0x56, 0x5D, 0x09, 0xEA, 0x61 } };
void CBeaconsDlg::OnBnClickedButtonAdvertiserStart()
{
	CwclBluetoothRadio* Radio = GetRadio();
	if (Radio == NULL) return;
	LeAdvertiser.Clear();
	// iBeacon
	CwclBluetoothLeAdvertisement* Adv;
	Adv = new CwclBluetoothLeiBeaconAdvertisement(-5, 0x0101, 0x0202, BEACON_UUID);
	LeAdvertiser.Add(Adv);
	// Manufacturer specific
	wclBluetoothLeAdvertisementFrameRawData Data;
	Data.resize(2);
	Data[0] = 0x12;
	Data[1] = 0x34;
	Adv = new CwclBluetoothLeManufacturerAdvertisement(0x010E, Data);
	LeAdvertiser.Add(Adv);
	// Extended advertisement
	LeAdvertiser.UseExtendedAdvertisement = true;
	LeAdvertiser.Anonymous = false;
	LeAdvertiser.IncludeTxRssi = true;
	LeAdvertiser.PreferredTxRssi = 10;
	LeAdvertiser.Interval = 100;
	int Res = LeAdvertiser.Start(Radio);
	if (Res != WCL_E_SUCCESS)
		Trace(_T("Start advertiser failed"), Res);
}
void CBeaconsDlg::OnBnClickedButtonAdvertiserStop()
{
	int Res = LeAdvertiser.Stop();
	if (Res != WCL_E_SUCCESS)
		Trace(_T("Stop advertiser failed"), Res);
}
Frequently Asked Questions
- What is Bluetooth LE Advertisement?
- It is the broadcast transmission of data by BLE devices without establishing a connection, used for beacons, identification, and telemetry.
- How to receive BLE packets with Bluetooth Framework?
- Use the
wclBluetoothLeBeaconWatcherclass. It decodes dozens of standard packet types, including Eddystone, Apple Continuity, Drone Remote ID. - Can I transmit custom BLE advertising packets?
- Yes, the
wclBluetoothLeAdvertiserclass allows transmitting both predefined and fully custom advertisement packets, subject to driver restrictions.