Detect iBeacons using Spark Core and BLE Mini

Detect iBeacons using Spark Core and BLE Mini

Here is a sample application I worked after BLE Mini HCI experiment. This sample uses BLE Mini with HCI Firmware to scan iBeacons. I tested with RFDuino with iBeacon example and Apple AirLocate example. The AirLocate application turns your iOS 7.0 or later device into an iBeacon transmitter.

To detect a BLE Device/iBeacon, we have to:

  1. Initialize HCI
  2. Start device discovery
  3. Wait for GAP_DeviceInformation packet (event code 0x060D). We will receive this for each device scanned.
  4. Parse the GAP_DeviceInformation packet which contains RSSI, iBeacon UUID, Major, Minor and Measured Power
  5. Wait for the scan to complete and repeat step 2 onwards

Here I am using BLE Mini as the BLE Central device to scan surrounding BLE device and communicate with Spark Core using Serial. The application initialize and start device discovery, waits for scanned devices and then publish the result using Spark.publish. After scanning is started, we will get GAP_DeviceInformation for every device present in the surrounding area. The device information contains RSSI, iBeacon UUID, Major, Minor and Measured Power.

Using the RSSI value we get from the Device Information packet, we can find the distance of the device using the formula described here. This is only an approximate distance, we can use this one to determine whether the device is too close or too far, etcā€¦ The Apple iBeacon framework uses some other formula that is not public. Radius Networks derived the above formula and is used in the iBeacon library.

The application looks for a beacon, when found one and if the distance is less than 0.5m then turn on an LED. We can test this by placing an iBeacon close to the Spark Core. If it is very close the LED turns on and when we move away from Spark Core the LED turns off.

Here is a demo video of the sample application in action.

You can also use the companion Web Application to see the nearby iBeacons. To use the Web Page, you should replace the tags deviceid and accesstoken.

Wiring

  1. BLE Mini Vin to 5V
  2. BLE GND to GND
  3. BLE Mini Tx to Rx
  4. BLE Mini Rx to Tx
  5. LED to Spark Core D2

Screenshots

enter image description here

enter image description here

enter image description here

enter image description here

4 Likes

One issue I am facing is, when multiple beacons are there, some junk characters are reading from Serial. When only one is there then it is correct, could someone suggest any possible causes?

Thanks @krvarma this will hopefully be a great help for my project. Iā€™m currently just trying to get my ios device to function as a peripheral so the BLE Mini can discover it.

As I am not part of the Apple SDK world (yet), I was curious if you saw this post on the redbear forum about using the LightBlue app to set your device to peripheral mode, and if so, had you played with it?

https://redbearlab.zendesk.com/entries/50793156-FInding-Devices-Using-BLE-Mini

1 Like

@techbutler, I tried LightBlue Application. It can create virtual peripheral devices. If you have LightBlue app installed you can press the ā€œ+ā€ at the right corner and select which device to simulate. One of the best tool you can use to play with is the BTool from TI (http://www.ti.com/tool/ble-stack). I have created virtual Temperature and Alarm Service peripheral and here is the BTool log, please note that you have to update the USBCDC firmware to use BLE Mini with BTool.

[1] : <Tx> - 12:25:25.404
-Type		: 0x01 (Command)
-Opcode		: 0xFE04 (GAP_DeviceDiscoveryRequest)
-Data Length	: 0x03 (3) byte(s)
 Mode		: 0x03 (All)
 ActiveScan	: 0x01 (Enable)
 WhiteList		: 0x00 (Disable)
Dump(Tx):
01 04 FE 03 03 01 00 
------------------------------------------------------------------------------------------------------------------------
[2] : <Rx> - 12:25:25.412
-Type		: 0x04 (Event)
-EventCode	: 0xFF (HCI_LE_ExtEvent)
-Data Length	: 0x06 (6) bytes(s)
 Event		: 0x067F (GAP_HCI_ExtentionCommandStatus)
 Status		: 0x00 (Success)
 OpCode		: 0xFE04 (GAP_DeviceDiscoveryRequest)
 DataLength	: 0x00 (0)
Dump(Rx):
04 FF 06 7F 06 00 04 FE 00 
------------------------------------------------------------------------------------------------------------------------
[3] : <Rx> - 12:25:25.502
-Type		: 0x04 (Event)
-EventCode	: 0xFF (HCI_LE_ExtEvent)
-Data Length	: 0x22 (34) bytes(s)
 Event		: 0x060D (GAP_DeviceInformation)
 Status		: 0x00 (Success)
 EventType	: 0x00 (Connectable Undirect Advertisement)
 AddrType	: 0x03 (PrivateResolve)
 Addr		: 5C:97:40:3F:26:CB
 Rssi		: 0xD2 (210)
 DataLength	: 0x15 (21)
 Data		: 02:01:1A:11:07:54:F1:AD:DE:00:00:00:00:00:00:00:
		  00:54:F1:AD:DE
Dump(Rx):
04 FF 22 0D 06 00 00 03 CB 26 3F 40 97 5C D2 15 
02 01 1A 11 07 54 F1 AD DE 00 00 00 00 00 00 00 
00 54 F1 AD DE 
------------------------------------------------------------------------------------------------------------------------
[4] : <Rx> - 12:25:25.512
-Type		: 0x04 (Event)
-EventCode	: 0xFF (HCI_LE_ExtEvent)
-Data Length	: 0x28 (40) bytes(s)
 Event		: 0x060D (GAP_DeviceInformation)
 Status		: 0x00 (Success)
 EventType	: 0x04 (Scan Response)
 AddrType	: 0x03 (PrivateResolve)
 Addr		: 5C:97:40:3F:26:CB
 Rssi		: 0xD2 (210)
 DataLength	: 0x1B (27)
 Data		: 1A:09:54:65:6D:70:65:72:61:74:75:72:65:20:41:6C:
		  61:72:6D:20:53:65:72:76:69:63:65
Dump(Rx):
04 FF 28 0D 06 00 04 03 CB 26 3F 40 97 5C D2 1B 
1A 09 54 65 6D 70 65 72 61 74 75 72 65 20 41 6C 
61 72 6D 20 53 65 72 76 69 63 65 
------------------------------------------------------------------------------------------------------------------------
[5] : <Rx> - 12:25:35.662
-Type		: 0x04 (Event)
-EventCode	: 0xFF (HCI_LE_ExtEvent)
-Data Length	: 0x0C (12) bytes(s)
 Event		: 0x0601 (GAP_DeviceDiscoveryDone)
 Status		: 0x00 (Success)
 NumDevs	: 0x01 (1)
 Device #0
 EventType	: 0x00 (Connectable Undirect Advertisement)
 AddrType	: 0x03 (PrivateResolve)
 Addr		: 5C:97:40:3F:26:CB
Dump(Rx):
04 FF 0C 01 06 00 01 00 03 CB 26 3F 40 97 5C 
------------------------------------------------------------------------------------------------------------------------

hi @krvarma

Thanks for your excellent work. I have a question - would your project (Spark and BLE-Mini) detect these ā€œtagā€ devices?

eg: Trackr / Tile/ StickAndFind

My thoughts is to in each room in my house, have a Spark+BLEMini and each person carries a ā€œtagā€ device with themā€¦thus allowing their location to be known within the house ( and then turn off the lights etc! )

So are these tags compatible so far as you know? I imagine these act as BLE beacons?

Cheers,
Greg

@mgf909, I checked with RFDuino and my iPhone with AirLocate (AirLocate lets your iOS device act as an iBeacon). I do not have TrackR and other devices. But since these are beacon devices, it should work. If you have these devices, can you check and post the results here?

Did you have any luck getting the junk characters removed? Iā€™m having the same issue. Cheers!

Hereā€™s my experience with a Arduino + BLE Mini. Used a Macbook running MacBeacon as an iBeacon.

1 Like

Ohā€¦ @jlr, I missed your post, really sorry about that.

I didnā€™t look at the issue more because of some other tasks. Let me know if you got it correct./

Thanks @krvarma for sharing.
Iā€™m new to sparkcore and was wondering if it would be possible to use your tutorial above and turn spark core into an ibeacon?

Meaning suppose someone with an iphone has my app install, when we walks close to my spark core powered hardware, the iphone will be trigger a notification?

Thanks in advance :smiley:

@bosslee, BLE Mini can act as iBeacon. But turning Spark Core is not possible using BLE Mini unless you change the BLE Mini firmware. There is an Open Source iBeacon Firmware from RebBearLab. This link has the Firmware Binary and Source Code.

Another option is to use RFDuino and load the iBeacon Sketch and change it to communicate to Spark Core via Serial.

I havenā€™t try these methods, so I could be wrong in this.

Thank you @krvarma

thanks @krvarma , this was def helpful. however, when i try to connect the BLE Mini to my arduino Due, I dont seem to be getting any response when checking if serial1.available. now, having said that, is it possible it will not send any data unless there are no iBeacons near by? at the moment I have gimbal sensors but they might not be working properly.

@furssher, for Arduino, you should change to Serial and connect to the hardware serial. Another option is to use the SoftwareSerial library with appropriate pins.

Did you try this?

I am having a similar problem with junk data being collected. Occasionally the correct UUID is displayed along with the max/min, but many times it is incomplete or incorrect. Any idea what could be wrong? Iā€™m using an arduino nano for this application.

#include <SoftwareSerial.h>
#include <stdarg.h>

#define GAP_PROFILE_CENTRAL           0x08
#define KEYLEN                        16
#define B_ADDR_LEN                    6 

#define BUILD_UINT16(loByte, hiByte) \
          ((uint16_t)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8)))

static uint8_t gapCentralRoleTaskId = 0;
static uint8_t  gapCentralRoleIRK[KEYLEN] = {0};
static uint8_t  gapCentralRoleSRK[KEYLEN] = {0};
static uint32_t gapCentralRoleSignCounter = 1;
static uint8_t  gapCentralRoleBdAddr[B_ADDR_LEN];
static uint8_t  gapCentralRoleMaxScanRes = 5;

SoftwareSerial BTSerial(6, 7); // RX, TX

void p(char *fmt, ... ){
  char tmp[128]; // resulting string limited to 128 chars
  va_list args;
  va_start (args, fmt );
  vsnprintf(tmp, 128, fmt, args);
  va_end (args);
  Serial.print(tmp);
}

void setup(){
  Serial.begin(9600);
  BTSerial.begin(57600);
  hci_init();
}

void loop(){ 
  if(BTSerial.available()){
    ble_event_process();
  }
}

byte ble_event_process(){
  uint8_t type, event_code, data_len, status1;
  uint16_t event;
  uint8_t buf[64];

  type = BTSerial.read();
  delay(100);
  event_code = BTSerial.read();
  data_len = BTSerial.read();

  for (int i = 0; i < data_len; i++)
    buf[i] = BTSerial.read();

  event = BUILD_UINT16(buf[0], buf[1]);
  status1 = buf[2];

  switch(event){
    case 0x600:{
      hci_start_discovery();

      break;
    }
    case 0x060D:{
      int rssi = buf[11];

      p("\r\n");
      p("RSSI: %d, %d\r\n", buf[11], rssi);
      p("iBeacon UUID: ");

      for(int i=22; i<38; i++){
        if(i<37)
          p("%02X:", buf[i]);
        else
          p("%02X", buf[i]);
      }

      p("\r\n");
      p("iBeaconMajor: %d\r\n", BUILD_UINT16(buf[39], buf[38]));
      p("Minor: %d\r\n", BUILD_UINT16(buf[41], buf[40]));
      p("Measured Power: %X", buf[42]);
      p("\r\n");

      break;
    }
    case 0x601:{
      hci_start_discovery();

      break;
    }
  }
}

int hci_init(){
  return GAP_DeviceInit( gapCentralRoleTaskId, GAP_PROFILE_CENTRAL,
                         gapCentralRoleMaxScanRes, gapCentralRoleIRK,
                         gapCentralRoleSRK, &gapCentralRoleSignCounter );
}

int hci_start_discovery(){
  return GAP_DeviceDiscoveryRequest();
}

int GAP_DeviceInit( uint8_t taskID, uint8_t profileRole, uint8_t maxScanResponses, uint8_t *pIRK, uint8_t *pSRK, uint32_t *pSignCounter )
{
  uint8_t buf[42];
  uint8_t len = 0;

  buf[len++] = 0x01;                  // -Type    : 0x01 (Command)
  buf[len++] = 0x00;                  // -Opcode  : 0xFE00 (GAP_DeviceInit)
  buf[len++] = 0xFE;

  buf[len++] = 0x26;                  // -Data Length
  buf[len++] = profileRole;           //  Profile Role
  buf[len++] = maxScanResponses;      //  MaxScanRsps
  memcpy(&buf[len], pIRK, 16);        //  IRK
  len += 16;
  memcpy(&buf[len], pSRK, 16);        //  SRK
  len += 16;
  memcpy(&buf[len], pSignCounter, 4); //  SignCounter
  len += 4;

  BTSerial.write(buf, len);

  return 1;
}

int GAP_DeviceDiscoveryRequest()
{
  uint8_t buf[20];
  uint8_t len = 0;

  buf[len++] = 0x01;                 // -Type    : 0x01 (Command)
  buf[len++] = 0x04;                 // -Opcode  : 0xFE04 (GAP_DeviceDiscoveryRequest)
  buf[len++] = 0xFE;

  buf[len++] = 0x03;                 // -Data Length
  buf[len++] = 0x03;                 //  Mode
  buf[len++] = 0x01;                 //  ActiveScan
  buf[len++] = 0x00;                 //  WhiteList

  BTSerial.write(buf, len);

  return 1;
}

void readChar(int len){
  int i=0;

  while(i<len){
    char ch = BTSerial.read();

    ++i;
  }
}

@dan678, I still did not figure out what is wrong with it, but working on it. This was kept aside because of some other works and personal projects. I will update the progress here soon.

Hi @krvarma, thanks for the great BLE Spark Core work!
I noticed you were able to connect and read data from a TI sensor tag. Have you been successful with any other BLE peripherals, such as the Lightblue Bean for example? Iā€™m trying to use your work and build on it to include the ability to connect to a Bean. Any pointers?

Thanks!

@robertcedwards, I have only connected SensorTag and iPhone 5s configured as iBeacon using AirLocate, But it should work work with other beacons.

Is there any way to increase the ā€˜refresh rateā€™ of the beacon detection and publishing?

Thanks for a great project share @krvarma. Iā€™m printing the found beacons to the serial monitor, but only getting the updated set of found beacons every 5 - 10 seconds - is there any way to make this closer to 0.5 - 1 seconds?

Thanks guys, and apologies if this is a really silly question, Iā€™m new to this :blush:

@Mike100, you can try reduce the delay of 1000ms on Line NO. 105. I have given it for the two consecutive publishes.