BLE - updates to adverts on peripherals

Hello,

My project uses a Nano 33 BLE and a number of W4 beacons from MokoSmart. These are iBeacons with an accelerometer, where the accelerometer data is included in the advertisement in manufacturer data. Using the MokoSmart app, I can see the accelerometer data, and the changes to it as the beacon is moved.

However.

In my code, I can get the beacons and see the accelerometer data. Great, but I can't see updates to the adverts, so I don't see the changes to the accelerometer data. I'm using a fork of the ArduinoBLE library that exposes getAdvertisment, and an addition of mine to allow scanForManufacturer.

My question is, how do I get the updated advertisement data? My code correctly discovers the beacons and strips the data out, I use BLE.poll() in my 'loop' function and then call getAdvertisement on each discovered beacon in turn.

I'm wondering whether the ArdiunoBLE library is the right one. Should I go for the mbed library at board level?

The IDs shown in the code below are the manufacturer ID of the beacons.

My code:

#include <Arduino.h>
#include <ArduinoBLE.h>
#include <cstdint>

REDIRECT_STDOUT_TO(Serial)

typedef struct
{
  BLEDevice beacon;
  String name;
} myBeacon;

myBeacon discoveredBeacons[20];
int discoveredBeaconCount = 0;

//#define useMSB

int lastDiscoverMS = 0;
float totalDuration = 0;
float countDiscovers = 0.0;
const char* BeaconManufacturerUUID = "4c000215e2c56db5dffb48d2b060d0f5a71096e";
const char* id = "e2c56db5dffb48d2b060d0f5a71096e";
uint8_t advertisement[64] = {0};

void setup()
{
  Serial.begin(115200);
  waitForStart();
  Serial.print("Sketch:   ");   Serial.println(__FILE__);
  Serial.print("Uploaded: ");   Serial.println(__DATE__);
  Serial.println("BLE Starting...");
  if (!BLE.begin()) 
  {
    printf("******starting BLE failed!\n");
    while (1);
  }
  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
  BLE.setEventHandler(BLEDeviceLastEvent, blePeripheralEventLastHandler);

  BLE.setEventHandler(BLEDiscovered, blePeripheralDiscoveredHandler);
  startScan();
}

void blePeripheralConnectHandler(BLEDevice peripheral)
{
  String name = peripheral.localName();
  Serial.print("Connected event, peripheral: ");
  Serial.println(name);
}

void blePeripheralDisconnectHandler(BLEDevice peripheral)
{
  String name = peripheral.localName();
  Serial.print("Disconnected event, peripheral: ");
  Serial.println(name);
}

void blePeripheralEventLastHandler(BLEDevice peripheral)
{
  String name = peripheral.localName();
  Serial.print("blePeripheralEventLastHandler event, peripheral: ");
  Serial.println(name);
}

void blePeripheralDiscoveredHandler(BLEDevice peripheral)
{
  bool found = false;
  String name = peripheral.localName();
  Serial.print("discovered: '"); Serial.print(name); Serial.println("'");
  for (int i = 0; i < discoveredBeaconCount; i++)
  {
    if (discoveredBeacons[i].name == name)
    {
      found = true;
      break;
    }
  }
  if (!found)
  {
    Serial.print("\tAdding '"); Serial.print(name); Serial.print("' to discovered list ");
    discoveredBeacons[discoveredBeaconCount].beacon = peripheral;
    discoveredBeacons[discoveredBeaconCount].name = name;
    discoveredBeaconCount++;
    Serial.print(" discovered count: '"); Serial.print(discoveredBeaconCount); Serial.print("'   ");
    peripheral.connect();
    Serial.println("connected.");
  }
}

void reportDevice(BLEDevice peripheral)
{
  if (peripheral.hasLocalName())
  {
    Serial.print(peripheral.localName());
    int now = millis();
    int duration = now - lastDiscoverMS;
    Serial.print(" "); Serial.print(now - lastDiscoverMS);
    if (countDiscovers > 0)
    {
      Serial.print(" avg: ");
      totalDuration += (float)duration;

      float avg = totalDuration / countDiscovers;
      Serial.print(" "); Serial.print(avg);
      Serial.print("ms");
    }
    sendAccelerometer(peripheral);
    Serial.println("");
    lastDiscoverMS = now;
    countDiscovers++;
  }
}

void waitForStart()
{
  int now = millis();
  int stop = now + 5000;
  while (!Serial && now < stop)
  {
    delay(100);
    now = millis();
  }
}

void startScan()
{
  printf("starting scan\n");
  int scanResult =  BLE.scanForManufacturer("e2c56db5dffb48d2b060d0f5a71096e0");//BLE.scan();
  printf("started scan\n");
}

int convertFrom2sComplement(int baseNumber)
{
  if (!(baseNumber & 0x8000))
  {
    return baseNumber;
  }
  else
  {
    int x = baseNumber & 0x7fff;
    int y = -32768 + ((int)x);
    return y;
  }
}

void sendAccelerometer(BLEDevice peripheral)
{
  // Response package structure
  // Len.   Type.    Value                               b start   end
  // 02.    0x0A.    Tx Power 0x00                             0     1
  //
  // 16.    0x16.    2 bytes  service UUID (0xFF 01).          2     3
  //                 1 byte  battery Level (0x64).             4     4
  //                 2 bytes major (0x00 00).                  5     6
  //                 2 bytes minor (0x00 00)                   7     8
  //                 1 byte  rssi (0x50)                       9     9
  //                 1 byte  firmware version (0x85)          10    10
  //                 2 bytes 3-axis-x (0xA0 00)               11    12
  //                 2 bytes 3-axis-y (0xF0 FF).              13    14
  //                 2 bytes 3-axis-z (0xE0 03)               15    16

  // 07.    0x09.   BLE Name 0x53 30 30 30 30 30

  // Sample:
  // UUID 0xFF 01
  // 0x64 00 00 00 00 50 85 A0 00 F0 FF E0 03
  // Becomes:
  // 1 byte  battery Level     0x64
  // 2 bytes major             0x00 00
  // 2 bytes minor             0x00 00
  // 1 byte  rssi              0x50
  // 1 byte  firmware version  0x85
  // 2 bytes 3-axis-x          0xA0 00
  // 2 bytes 3-axis-y          0xF0 FF
  // 2 bytes 3-axis-z          0xE0 03

  byte advertisement[64] = { 0 };
  int adLength = peripheral.getAdvertisement(advertisement, 64);
  byte sensorAdvertisementData[64];
  for (int x = 0; x < sizeof(sensorAdvertisementData); x++)
  {
    sensorAdvertisementData[x] = 0;
  }

  int sensorAdvertisementLength = 0;
  for (int i = 0; i < 64;)
  {
    int eirLength = advertisement[i++];
    int eirType = advertisement[i++];
    if (eirType == 0xFF)
    {
      sensorAdvertisementLength = eirLength;
      for (int j = 0; j < (eirLength - 1); j++)
      {
        byte thisByte = advertisement[i + j];
        sensorAdvertisementData[j] = thisByte;
      }
    }
    if (eirType == 0x16)
    {
      // copy each byte and print.  could have used memcpy and sprintf
      sensorAdvertisementLength = eirLength;
      printf("16 iBeaconData: <%d> <", eirLength);
      for (int j = 0; j < eirLength - 1; j++)
      {
        byte thisByte = advertisement[i + j];
        sensorAdvertisementData[j] = thisByte;
        printf("%02x ", thisByte);
      }
      printf(">");
      int axisOffset = eirLength - 7;

      uint8_t xhigh = sensorAdvertisementData[axisOffset];
      uint8_t xlow = sensorAdvertisementData[axisOffset + 1];
      uint16_t xRaw = xhigh << 8 | xlow;
      uint16_t xAxis = convertFrom2sComplement(xRaw);

      axisOffset += 2;
      uint8_t yhigh = sensorAdvertisementData[axisOffset];
      uint8_t ylow = sensorAdvertisementData[axisOffset + 1];
      uint16_t yRaw =  yhigh << 8 | ylow;
      uint16_t yAxis = convertFrom2sComplement(yRaw);

      axisOffset += 2;
      uint8_t zhigh = sensorAdvertisementData[axisOffset];
      uint8_t zlow = sensorAdvertisementData[axisOffset + 1];
      uint16_t zRaw =  zhigh << 8 | zlow;
      uint16_t zAxis = convertFrom2sComplement(zRaw);
      printf("x(%02X %02X), y(%02X %02X), z(%02X %02X)   (%d,%d,%d) -> (%d,%d,%d)",
             xhigh, xlow, yhigh, ylow, zhigh, zlow,
             xRaw, yRaw, zRaw, xAxis, yAxis, zAxis);
      break;
    }
    i += (eirLength - 1);
  }
}

void loop()
{
  BLE.poll(100);
  for (int i = 0; i < discoveredBeaconCount; i++)
  {
    int adLength = discoveredBeacons[i].beacon.getAdvertisement(advertisement, 64);
    reportDevice(discoveredBeacons[i].beacon);
  }
}

The ArduinoBLE code I'm using is attached.

ArduinoBLE.zip (886.1 KB)

The beacon data sheet is in section '2.1.2 MkiBeacon 3-Axis Acc sensor data' at:
http://doc.mokotechnology.com/index.php?s=/page/265#2.1.2%20MkiBeacon%20-%203-axis%20Acc%20sensor%20data(Response%20packet)

Ok, so I’ve put together an update for the library that raises an event showing the updated advertisement. Once I’ve tested it and it’s working efficiently I’ll submit a PR.

Ian