Bluetooth BLE scanner iBeacon decoder

A Bluetooth BLE iBeacon observer, that scans for a list of available beacons, then for each iBeacon found, it reads and processes it’s gathered data.

The example presented builds using Platformio with the espressif arduino-esp32 core, and leverages the NimBLE-Arduino Bluetooth library.

Project source code

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino


monitor_speed = 115200


lib_deps = h2zero/NimBLE-Arduino@^1.4.0
// File: main.cpp 
// ESP32_NimBLE_scanner_iBeacon_decoder.
// <green@bug-eyed.monster> MIT License.
//
// An iBeacon observer.
// Scan for servers and log each unique result. 
// Read each logged BLE beacon in turn, filter for
// iBeacons, then process their manufacturer_data.
// Repeat.

#include <Arduino.h>

#include "NimBLEBeacon.h"
#include "NimBLEDevice.h"


#define ENDIAN_CHANGE_U16(x) ((((x)&0xff00) >> 8) + (((x)&0xff) << 8))


void setup() { 

    Serial.begin(115200);

    // Initialise the NimBLE library, witholding any advertisable name.
    NimBLEDevice::init("");
    
    // Create a Scan(ner):
    // Get a pointer to a newly created Scan instance.
    NimBLEScan *pScan = NimBLEDevice::getScan();

    // Set active scanning, this will get more data from the advertiser.
    // Comment-out if not interested in iBeacon name.
    pScan->setActiveScan(true);

    // Block whilst scanning for advertising servers, storing
    // a list of all results, over a period given in seconds.
    Serial.printf("Scanning for BLE advertisers\n");
    NimBLEScanResults results = pScan->start(10);
    Serial.printf("Found %d BLE advertisers\n", results.getCount());
    
    // Iterate through the list of NimBLEAdvertisedDevice's stored
    // in the NimBLEScanResults scan results list.
    for(int i = 0; i < results.getCount(); i++) {

        // Get a pointer to the iterated Device instance.
        NimBLEAdvertisedDevice advertisedDevice = results.getDevice(i);

        // Retrieve the BLE beacon's manufacturer_data.
        std::string strManufacturerData = advertisedDevice.getManufacturerData();

        // Did we find any manufacturer_data?
        if (strManufacturerData != "")
        {
            // Look for Apple ID and iBeacon length
            if (strManufacturerData.length() == 25 &&
                strManufacturerData[0] == 0x4c &&
                strManufacturerData[1] == 0x00 &&
                strManufacturerData[2] == 0x02 &&
                strManufacturerData[3] == 0x15) 
            {
                // Leverage the NimBLEBeacon library 
                // to help process the iBeacon data.
                NimBLEBeacon iBeacon = NimBLEBeacon();

                // Load our NimBLEBeacon object with manufacturer_data.
                iBeacon.setData(strManufacturerData);

                // Process this found iBeacon's data
                Serial.printf("Name     : %s\n",
                        advertisedDevice.getName().c_str());
                Serial.printf("Address  : %s\n",
                        advertisedDevice.getAddress().toString().c_str());
                Serial.printf("UUID     : %s\n",
                        iBeacon.getProximityUUID().toString().c_str());
                Serial.printf("Major    : %d\n",
                        ENDIAN_CHANGE_U16(iBeacon.getMajor()));
                Serial.printf("Minor    : %d\n",
                        ENDIAN_CHANGE_U16(iBeacon.getMinor()));
                Serial.printf("TX power : %d dBm\n",
                        iBeacon.getSignalPower());
                Serial.printf("RSSI     : %d dBm\n",
                        advertisedDevice.getRSSI());
                Serial.println("-----------------------------------------------");
            }
        }
    }
}

void loop() {

    setup();
}

Serial Port Output

Output obtained when run in proximity to one BLE iBeacon device flashed with the companion Bluetooth BLE iBeacon on ESP32.

Scanning for BLE advertisers
Found 2 BLE advertisers
Name     : ESP32-iBeacon
Address  : 08:b6:1f:37:f3:92
UUID     : c6dd05d0-b428-4bd5-8831-4b62651e2b41
Major    : 1
Minor    : 1
TX power : -60 dBm
RSSI     : -64 dBm
-----------------------------------------------
Scanning for BLE advertisers

Suggested reading: https://www.elektor.com/products/develop-your-own-bluetooth-low-energy-applications (no affiliation).