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.

Various BLE beacon hardware used for Bluetooth LE advertising and proximity detection 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 to true, the device address is not included in the advertisement header.
  • IncludeTxRssi
    If set to true, 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 wclBluetoothLeBeaconWatcher class. It decodes dozens of standard packet types, including Eddystone, Apple Continuity, Drone Remote ID.
Can I transmit custom BLE advertising packets?
Yes, the wclBluetoothLeAdvertiser class allows transmitting both predefined and fully custom advertisement packets, subject to driver restrictions.