Creating iBeacon with Raspberry Pi using BlueZ Example Code


Update (Jul 9, 2019):
Step 1 is no longer required since Raspbian Buster (2019-06-20). Please check the updated steps in here.


 

This post shows steps to create an iBeacon with Raspberry Pi, by modifying BlueZ BLE Advertisement example code (i.e. “example-advertisement).

Note:
Even though it uses Bluetooth Low Energy standard, iBeacon is Apple’s proprietary protocol and making/deploying iBeacon devices requires the license from Apple [1]. The scope of this post is limited to getting familiar with BlueZ advertising example code and iBeacon format for experimental purpose.

Here are the contents of this post.

Contents
– Prerequisites
– Steps
1. Enable experimental flag
2. Download BlueZ
3. Modify BLE Advertisement Example Code
4. Test
– Summary
– Reference

 

Prerequisites
Here are some prerequisites before start.

  • Raspberry Pi 3 B+ or Raspberry Pi Zero W
    Or any other model with Bluetooth adapter (e.g. Plugable BLE Adapter, Panda Bluetooth 4.0 Adapter)
  • Raspbian Stretch (Nov 2017)
    I would suggest to use Desktop because I got “DBus.Error.AccessDenied” for some reason when I ran “example-advertisement” on Raspbian Lite. It seems it’s similar to this but couldn’t figure out the cause/workaround.
  • Internet access
    It’s required to download BlueZ example code. Here is a Wi-Fi Setup steps. If you already have the code, you can work offline.

 

Steps
1. Enable experimental flag
Bluetooth service needs to be executed with experimental flag since ‘LEAdvertisingManager1’ interface, which is used in the example code, is experimental feature in BlueZ 5.43 (pre-installed in Stretch Nov 2017). In case you’ve updated BlueZ to 5.48 or higher, skip Step 1 since the interface have been marked as stable since 5.48. [2]

1-1. Open the configuration file for Bluetooth service.

sudo nano /lib/systemd/system/bluetooth.service

1-2. Find a line starting with “ExecStart” and add “–experimental” at the end of the line. After modification, the line should look like this:

ExecStart=/usr/libexec/bluetooth/bluetoothd --experimental

1-3. Reboot the board to restart Bluetooth service.

sudo reboot

1-4. Check the status of Bluetooth service. [Optional]

systemctl status bluetooth.service

You should see the experimental flag. (Line 9 in the example below)

pi@raspberrypi:~ $ systemctl status bluetooth.service 
● bluetooth.service - Bluetooth service
   Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2018-02-21 14:31:19 UTC; 4s ago
     Docs: man:bluetoothd(8)
 Main PID: 1122 (bluetoothd)
   Status: "Running"
   CGroup: /system.slice/bluetooth.service
           └─1122 /usr/lib/bluetooth/bluetoothd --experimental

 

2. Download BlueZ
2-1. Download BlueZ source code.

wget www.kernel.org/pub/linux/bluetooth/bluez-5.43.tar.xz

2-2. Extract the archive file.

tar xvf bluez-5.43.tar.xz

2-3. Make sure that the sample code works. [Optional]

./bluez-5.43/test/example-advertisement

Output should be like this:

$ ./bluez-5.43/test/example-advertisement
GetAll
returning props
Advertisement registered

 

3. Modify BLE Advertisement Example Code
3-1. Copy the example code.

cp ./bluez-5.43/test/example-advertisement ./example-ibeacon

3-2. Open the file and look for TestAdvertisement class.

3-3. Replace ‘__init__’ method:

    def __init__(self, bus, index):
        Advertisement.__init__(self, bus, index, 'peripheral')
        self.add_service_uuid('180D')
        self.add_service_uuid('180F')
        self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
        self.add_service_data('9999', [0x00, 0x01, 0x02, 0x03, 0x04])
        self.include_tx_power = True

with:

    def __init__(self, bus, index):
        company_id =   0x004C
        beacon_type = [0x02, 0x15]
        uuid =        [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
                       0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
        major =       [0x11, 0x22]
        minor =       [0x33, 0x44]
        tx_power =    [0xB3]
        Advertisement.__init__(self, bus, index, 'peripheral')
        self.add_manufacturer_data(company_id, beacon_type + uuid + major + minor + tx_power)

 

4. Test
4-1. Run the code. The console output should be same as Step 2-3.

./example-ibeacon

4-2. Find your ibeacon running on Raspberry Pi using a beacon scanner app (e.g. nRF Connect).

 

Summary
If everything goes well, the Raspberry Pi should be broadcasting iBeacon message and you can find it with a scanner app. Some of the iBeacon data can be configured on Step 3-3 for your application. Here is a brief explanation of those data.

  • ‘company_id’
    Company Identifiers are defined by Bluetooth SIG [3]. For iBeacon, it must be Apple’s ID (i.e. 0x004C).
  • ‘beacon_type’
    It must be ‘0x0215’ which means Proximity Beacon.
  • ‘uuid’
    Proximity UUID is an application unique ID.
  • major‘ and ‘minor
    Major Number and Minor Number are application dependent values which can be used to identify more precise location, etc.
  • ‘tx_power’
    Tx Power is a device dependent value. It must be calibrated for each device when it’s deployed.

 

References
[1] iBeacon – Apple Developer
[2] BlueZ Release Notes
[3] Company Identifiers – Bluetooth SIG