BLE Eddystone UID not broadcasting

I am trying to get my Arduino nano 33 BLE to behave as a BLE Eddystone beacon where it broadcasts a UID. The code compiles and uploads without issue, but none of my BLE utility apps recognise the device as broadcasting in Eddystone UID format.

I am trying to work out what I'm doing wrong:

/*
  Eddystone Beacon UID Example

  This example creates a BLE beacon using the Eddystone format.

  The circuit:
    Arduino Nano 33 BLE, or Arduino Nano 33 BLE Sense board.

  This example code is in the public domain.
*/

/********************************************************************************
* This file contains macros used for Eddystone packet configuration. For details
* of Eddystone, see:
*
* https://developers.google.com/beacons/eddystone
*
* For details of the core Eddystone frame types (URL, UID and TLM), see the 
* following documentation:
*
* https://github.com/google/eddystone/tree/master/eddystone-url
* https://github.com/google/eddystone/tree/master/eddystone-uid
* https://github.com/google/eddystone/tree/master/eddystone-tlm
*
********************************************************************************/


#include <ArduinoBLE.h>

// Standard Eddystone Flags
#define FLAGS_UID                       (0x00u)
#define FLAGS_URL                       (0x10u)
#define FLAGS_TLM                       (0x20u)

// The preconfigured TX power levels
#define PWR_LVL_3_DBM                   (0x03)  /*   3 dBm */
#define PWR_LVL_0_DBM                   (0x00)  /*   0 dBm */
#define PWR_LVL_NEG_6_DBM               (0xFA)  /*  -6 dBm */
#define PWR_LVL_NEG_18_DBM              (0xEE)  /* -18 dBm */


#define MAX_ADVERTISING_PACKET_SIZE     (20u)

/* Eddystone packet index of the UID service data */       
#define UID_SERVICE_INDEX               (00u)
/* Eddystone packet index of the name space ID */ 
#define NAME_SPACE_INDEX                (02u)
/* Eddystone packet index of the instance ID */     
#define INSTANCE_INDEX                  (12u)
/* Eddystone packet indexes of the reserved fields */     
#define RFU_INDEX_0                     (18u)
#define RFU_INDEX_1                     (19u)
/* Data written to reserved Eddystone fields */    
#define RFU_DEFLT_VALUE                 (0u)

/* UID Service data for the UID frame */    
const uint8_t UID_SERVICE_DATA[2] = {
  FLAGS_UID,                /* Signifies Eddystone UID */\
  PWR_LVL_NEG_6_DBM        /* TX Ranging data: -6dB*/
};

// UID Data - split between Namespace ID (10) and Instance ID (6)
const uint8_t UID_NAMESPACEDATA[10] = {
  /* Namespace ID */
  0xCB,  // NID[0]
  0x6F,  // NID[1]
  0x15,  // NID[2]
  0xCE,  // NID[3]
  0x20,  // NID[4]
  0x2A,  // NID[5]
  0xCE,  // NID[6]
  0x15,  // NID[7]
  0x6F,  // NID[8]
  0xCB   // NID[9]
};

const uint8_t UID_INSTANCEDATA[6] = {
  /* Instance ID */
  0x01,  // IID[0]
  0x00,  // IID[1]
  0x00,  // IID[2]
  0x00,  // IID[3]
  0x00,  // IID[4]
  0x01   // IID[5]
};

// BLE Eddystone Broadcasting Service
BLEService EddystoneService("FEAA");

// BLE Eddystone Broadcasting Characteristic
BLECharacteristic EddystoneChar("FEAB",  // standard 16-bit characteristic UUID for Eddystone Beacons
    BLERead | BLEBroadcast, MAX_ADVERTISING_PACKET_SIZE); // this allows for both connecting and listening to advertisements

uint8_t AdvertisementData[MAX_ADVERTISING_PACKET_SIZE];


void ConfigureEddystoneServiceData() {
  
  memset(AdvertisementData, 0x00, sizeof(AdvertisementData));
  
  memcpy (&AdvertisementData[UID_SERVICE_INDEX], &UID_SERVICE_DATA, sizeof(UID_SERVICE_DATA));
  /* Load Name-space ID */
  memcpy (&AdvertisementData[NAME_SPACE_INDEX], &UID_NAMESPACEDATA, sizeof(UID_NAMESPACEDATA));
  /* Load Instance ID  */
  memcpy (&AdvertisementData[INSTANCE_INDEX], &UID_INSTANCEDATA, sizeof(UID_INSTANCEDATA));
  /* Load the reserved fields with default values */
  AdvertisementData[RFU_INDEX_0] = RFU_DEFLT_VALUE;              
  AdvertisementData[RFU_INDEX_1] = RFU_DEFLT_VALUE;    
  
}

void setup() {
  Serial.begin( 9600 );
  while (!Serial);

  // begin BLE initialization
  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);
  }
  Serial.println(F("Eddystone UID Beacon Example"));

  ConfigureEddystoneServiceData();

  Serial.println(F("Advertising Payload defined is: "));
  for (uint8_t i = 0; i < MAX_ADVERTISING_PACKET_SIZE; i++) {
    if (AdvertisementData[i]<10) Serial.print(0);
    Serial.print(AdvertisementData[i], HEX);
    if (i < MAX_ADVERTISING_PACKET_SIZE-1) Serial.print(" ");
  }
  Serial.println("");
  
  BLE.setLocalName("EddyBea");
  EddystoneService.addCharacteristic(EddystoneChar);      // add the eddystone characteristic
  Serial.print("Broadcast confirmed: ");
  Serial.println(EddystoneChar.broadcast());
  EddystoneChar.writeValue(AdvertisementData, MAX_ADVERTISING_PACKET_SIZE); // set initial value for this characteristic
  BLE.addService(EddystoneService);                       // Add the eddystone service
 
  // start advertising
  BLE.setAdvertisedService(EddystoneService);             // add the service UUID
  BLE.advertise();
  
}

void loop() {
  BLE.poll();
}

The BLE Scanner from bluepixel on iPhone shows a (EddyStomeTM) after the Arduino name in the scan display. When I connect I can see a 0xFEAA custom service and read a long HEX string but the app does not decode anything like a URL or so.

The nRF Connect app shows an Eddystone logo and prints Type:EddystoneTM. When I connect I can see the same 0xFEAA Service with a 0xFEAB characteristic which is readable and returns the same byte array printed as HEX value. It matches UID_SERVICE_DATA, UID_NAMESPACEDATA, UID_INSTANCEDATA in your sketch, all together as one long list of bytes.

Do you have some Google/Android hardware? Maybe you need that for native support.

That is strange.

I tested this on my Android phone (OS version 9.0). I used 3 BLE utility apps (nRFconnect, BLE Scanner and Bluetooth LE Scanner). I created an Eddystone "twin" product using a PSoC4 module with a different Instance ID. The PSoC4 module is correctly identified as an Eddystone beacon.

My Arduino nano 33 BLE board displays strange behaviour as the Advertising data is not shown. In fact, if I use the setLocalName this local name is then stored in the service data. If I do not include this name then nothing is stored for some reason.

As such, either I am not doing this correctly or there is a bug with the ArduinoBLE library.

WHEN NO NAME IS USED:

THE PSoC4 MODULE:

WHEN I INCLUDE THE LOCAL NAME:

Klaus_K:
The BLE Scanner from bluepixel on iPhone shows a (EddyStomeTM) after the Arduino name in the scan display. When I connect I can see a 0xFEAA custom service and read a long HEX string but the app does not decode anything like a URL or so.

The nRF Connect app shows an Eddystone logo and prints Type:EddystoneTM. When I connect I can see the same 0xFEAA Service with a 0xFEAB characteristic which is readable and returns the same byte array printed as HEX value. It matches UID_SERVICE_DATA, UID_NAMESPACEDATA, UID_INSTANCEDATA in your sketch, all together as one long list of bytes.

Do you have some Google/Android hardware? Maybe you need that for native support.

Just to clarify, with Beacons you do not connect with the device, in fact many set connectability to false or have a config service when you connect. The beacon data needs to reside with the Advertised Service UUID.

I have subsequently discovered that when you add this line of code

  EddystoneChar.writeValue(AdvertisementData, MAX_ADVERTISING_PACKET_SIZE); // set initial value for this characteristic

And you have given that characteristic the property "BLEBroadcast" then it should have the data to the Advertised Service UUID as data via a protected ArduinoBLE library function:

GAP.setAdvertisedServiceData(serviceUuid, value, length);

but this does not appear to be working.

gerrikoio:
Just to clarify, with Beacons you do not connect with the device, in fact many set connectability to false or have a config service when you connect. The beacon data needs to reside with the Advertised Service UUID.

I know, I did not have a working beacon and could not confirm how a beacon would look in the apps. I just wanted to make sure I describe everything in case it is helpful.
Anyways I rediscovered a BBC microbit in my possession after watching a YouTube video on how to create an Eddystone beacon with it (takes less than 2 minutes).

gerrikoio:
but this does not appear to be working.

This is not your fault. :slight_smile: With the help of the app and the screenshot of your PSoC, I have the beacon now working on the Arduino Nano 33 BLE.
I likely have broken something in the ArduinoBLE library because I had to modify some code which I do not understand yet.

Do you feel comfortable modifying the library when I describe what I have done, so we can see whether we can figure this out together? You can always reinstall the library.

this is not your fault. :slight_smile: With the help of the app and the screenshot of your PSoC, I have the beacon now working on the Arduino Nano 33 BLE.
I likely have broken something in the ArduinoBLE library because I had to modify some code which I do not understand yet.

Do you feel comfortable modifying the library when I describe what I have done, so we can see whether we can figure this out together? You can always reinstall the library.

Yes I'm comfortable with the library.

I'm impressed... I tried a few mods myself but nothing worked so I'm rather curious to learn what mods you did to get this working.

I went trough the modifications and reduced everything to the critical lines of code. Its just in the following file.

ArduinoBLE\src\utility\GAP.cpp in the GAPClass::advertise() function

  // if (_serviceData && _serviceDataLength > 0 && advertisingDataLen >= (_serviceDataLength + 4)) 
  {
    advertisingData[advertisingDataLen++] = _serviceDataLength + 3;
    advertisingData[advertisingDataLen++] = 0x16;

    // memcpy(&advertisingData[advertisingDataLen], &_serviceDataUuid, sizeof(_serviceDataUuid));
    // advertisingDataLen += sizeof(_serviceDataUuid);
    
    uint16_t eddyStoneUuid = 0xFEAA;
    memcpy(&advertisingData[advertisingDataLen], &eddyStoneUuid, sizeof(eddyStoneUuid));
    advertisingDataLen += sizeof(eddyStoneUuid);    

    memcpy(&advertisingData[advertisingDataLen],_serviceData, _serviceDataLength);
    advertisingDataLen += _serviceDataLength;
  }

There are two points

  1. The IF statement
  • _serviceData - seems OK no data then you cant add it to the advertisement
  • _serviceDataLength > 0 same as above but testing the length
  • advertisingDataLen >= (_serviceDataLength + 4) - this seems wrong, not sure what this test is supposed to do
    I have an idea what might be needed here but I do not want to lead you into my line of thinking. Maybe your ideas will be better/different when you look at it from your perspective. I will let you know in the next post.
  1. The _serviceDataUuid copied at this point is 0x1801 and not the one you set in your sketch. So, I fixed it by setting the 0xFEAA UUID. However today I found, if you switch the following lines in your sketch. The fix is not necessary, because the correct _serviceDataUuid is copied then.
  // EddystoneChar.writeValue(AdvertisementData, MAX_ADVERTISING_PACKET_SIZE); // set initial value for this characteristic
  BLE.addService(EddystoneService);                       // Add the eddystone service
  EddystoneChar.writeValue(AdvertisementData, MAX_ADVERTISING_PACKET_SIZE); // set initial value for this characteristic

It took awhile but I got it working with only one small change to the ArduinoBLE library.

I discovered two things in the process.

Firstly, the order in which to place the commands matters within the Arduino code.

So I found this order worked:

  BLE.setAdvertisedService(EddystoneService);             // add the service UUID
  BLE.addService(EddystoneService);                       // Add the eddystone service
  EddystoneChar.writeValue(AdvertisementData, MAX_ADVERTISING_PACKET_SIZE); // set initial value for this characteristic
  EddystoneService.addCharacteristic(EddystoneChar);      // add the eddystone characteristic
  // start advertising
  BLE.advertise();

And secondly, there is indeed an error in the ArduinoBLE library...

As you had spotted the change needs to be made in GAPClass::advertise() function

The error lies here (as you had spotted that something was wrong) advertisingDataLen >= (_serviceDataLength + 4)

Instead of "advertisingDataLen" this should read 31 (or the max length of advertising data array), as in
31 >= (_serviceDataLength + 4) or you never want to have _serviceDataLength + 4 to exceed 31 bytes.

gerrikoio:
Instead of "advertisingDataLen" this should read 31 (or the max length of advertising data array), as in
31 >= (_serviceDataLength + 4) or you never want to have _serviceDataLength + 4 to exceed 31 bytes.

Good, looks like we came to the same conclusion. :slight_smile: Regarding the solution do you think the code should read?

if (_serviceData && _serviceDataLength > 0 && ( advertisingDataLen + _serviceDataLength + 4 ) <= 31 )

This would also cover the case where somebody already attached some manufacturer data. This happens in the lines just before this statement.

if (_serviceData && _serviceDataLength > 0 && ( advertisingDataLen + _serviceDataLength + 4 ) <= 31 )

It took me a few goes to check but now I agree. Yes, this is correct! Well done.

Opened for wallano

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.