Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions examples/NimBLE_iBeacon/NimBLE_iBeacon.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* iBeacon example
*
* This example demonstrates how to publish an Apple-compatible iBeacon
*
* Created: on May 26 2025
* Author: lazd
*/

#include <Arduino.h>
#include <NimBLEDevice.h>
#include <NimBLEBeacon.h>

// According to Apple, it's important to have a 100ms advertising time
#define BEACON_ADVERTISING_TIME 160 // 100ms

// Hey, you! Replace this with your own unique UUID with something like https://www.uuidgenerator.net/
const char* iBeaconUUID = "26D0814C-F81C-4B2D-AC57-032E2AFF8642";

void setup() {
NimBLEDevice::init("NimBLEiBeacon");

// Create beacon object
NimBLEBeacon beacon;
beacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!)
beacon.setMajor(1);
beacon.setMinor(1);
beacon.setSignalPower(0xC5); // Not required
beacon.setProximityUUID(BLEUUID(iBeaconUUID)); // Unlike Bluedroid, you do not need to reverse endianness here

// Extract beacon data
NimBLEBeacon::BeaconData beaconData = beacon.getData();

// Create advertisement data
NimBLEAdvertisementData beaconAdvertisementData;
beaconAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED
beaconAdvertisementData.setManufacturerData(reinterpret_cast<const uint8_t*>(&beaconData), sizeof(NimBLEBeacon::BeaconData));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if there's a better way to do this, I noticed I can't directly pass beacon.getData() as it doesn't match any of setManufacturerData's signatures.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I see here is that this isn't needed. Instead what should be done is the beacon class should be fixed to include the flags since that is all that is happening here. They do not change in this repo as BT classic is not supported. Instead lets add this as a fixed data to the beacon data in the beacon class.

Copy link
Contributor Author

@lazd lazd May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds right to me, so what would the API end up looking like? Something like this would be nice:

advertising->setAdvertisementData(beacon.getAdvertisingData());

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay getting back to you. We can change this line to simply beaconAdvertisementData.setManufacturerData(beacon.getData());

In order to make that work you will need to add an operator to the beacondata class so it looks like :

    struct BeaconData {
        uint16_t manufacturerId{0x4c00};
        uint8_t  subType{0x02};
        uint8_t  subTypeLength{0x15};
        uint8_t  proximityUUID[16]{};
        uint16_t major{};
        uint16_t minor{};
        int8_t   signalPower{};
        operator std::vector<uint8_t> () const {
            return std::vector<uint8_t>(reinterpret_cast<const uint8_t*>(this),
                                        reinterpret_cast<const uint8_t*>(this) + sizeof(BeaconData));
        }
    } __attribute__((packed));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented! Now all that is left would be to remove the need for this

beaconAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED

How should we go about doing that? Can we simply omit the call, or do we need to call setFlags() somewhere, like in the NimBLEAdvertisementData constructor?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should leave this for now, it would overly complicate things to accommodate.


// Start advertising
NimBLEAdvertising *advertising = NimBLEDevice::getAdvertising();
advertising->setAdvertisingInterval(BEACON_ADVERTISING_TIME);
advertising->setAdvertisementData(beaconAdvertisementData);
advertising->start();
}

void loop() {}