Sample NordicSemicondictor nRF52 beacon discovery for CO657 (University of Kent, UK); builds on mbed-os, derived from LEDBeacon sample code (on github). Writes out beacons recently seen to serial port and/or LCD (#ifdef job) on the MBED application shield.
Diff: main.cpp
- Revision:
- 0:f4e27396137e
- Child:
- 1:7986f9873e20
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Oct 27 16:53:49 2016 +0000 @@ -0,0 +1,291 @@ +/* + * main.cpp -- test program to _simply_ detect iBeacons (and anything else advertising in a similar way) + * Fred Barnes, October 2016 + */ + +/* NOTE: based heavily on mbed-os-example-ble's BLE_LEDBlinker example (trimmed down) */ + +/* Further note: this is a hack in places */ + +#include "mbed.h" +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "C12832.h" + +/* output choice */ +#undef SERIAL_OUTPUT +#define LCD_OUTPUT + +static DigitalOut led1 (LED1); +static DigitalOut led2 (LED2); +static DigitalOut led3 (LED3); +static DigitalOut led4 (LED4); + +#ifdef SERIAL_OUTPUT +static Serial host (USBTX, USBRX); +#endif + +#ifdef LCD_OUTPUT +static C12832 shld_lcd (D11, D13, D12, D7, D10); /* LCD on the shield (128x32) */ +#endif + + + +#define HLEN_MAX (128) + +typedef struct beacon_log { + uint8_t age; /* approx seconds (0xff == dead/gone) */ + uint16_t addr_hi; /* two high order bytes */ + uint32_t addr_lo; /* four low order bytes */ + int8_t rssi; /* RSSI (-ve) */ + uint8_t type; /* crudely cast */ +} beacon_log_t; + +#define MAX_BEACONS (32) + +static beacon_log_t blog[MAX_BEACONS]; + +void bubble_blog (int idx) +{ + beacon_log_t tmp; + + /* crude bubble-sort for single insert/update */ + if (idx <= 0) { + return; + } + memcpy (&tmp, &blog[idx], sizeof (beacon_log_t)); + while ((idx > 0) && (blog[idx-1].age > tmp.age)) { + memcpy (&blog[idx], &blog[idx-1], sizeof (beacon_log_t)); + idx--; + } + memcpy (&blog[idx], &tmp, sizeof (beacon_log_t)); + + return; +} + +void age_blog (void) +{ + int i; + + for (i=0; i<MAX_BEACONS; i++) { + if (blog[i].age == 0xff) { + break; + } + blog[i].age++; + } + return; +} + +#ifdef LCD_OUTPUT +void draw_blog (void) +{ + shld_lcd.cls (); + for (int i=0; (i<3) && (blog[i].age != 0xff); i++) { + shld_lcd.locate (0, (i * 10)); + shld_lcd.printf ("%4.4X%8.8X", blog[i].addr_hi, blog[i].addr_lo); + shld_lcd.locate (72, (i * 10)); + shld_lcd.printf ("%d", blog[i].rssi); + shld_lcd.locate (96, (i * 10)); + shld_lcd.printf ("%u", blog[i].type); + shld_lcd.locate (108, (i * 10)); + shld_lcd.printf ("%u", blog[i].age); + } + shld_lcd.copy_to_lcd (); + return; +} +#endif + +void scan_advert_got (const Gap::AdvertisementCallbackParams_t *params) +{ + int i, last; + uint32_t a_lo; + uint16_t a_hi; + int newb = 0; + + a_hi = ((uint16_t)params->peerAddr[5] << 8) | (uint16_t)params->peerAddr[4]; + a_lo = ((uint32_t)params->peerAddr[3] << 24) | ((uint32_t)params->peerAddr[2] << 16) | ((uint32_t)params->peerAddr[1] << 8) | (uint32_t)params->peerAddr[0]; + + /* scribble the data into the log */ + for (i=0, last=-1; i<MAX_BEACONS; i++) { + /* see if we match */ + if (blog[i].age == 0xff) { + last = i; + break; + } else if ((blog[i].addr_hi == a_hi) && (blog[i].addr_lo == a_lo)) { + /* this one! */ + break; + } + } + if (last >= 0) { + /* new(ish) beacon */ + i = last; + newb = 1; + } else if ((i == MAX_BEACONS) && (last < 0)) { + /* ran out of room searching for slot, use last */ + i = MAX_BEACONS - 1; + newb = 1; + } /* else found it somewhere, just reset age */ + + if (newb) { + blog[i].addr_hi = a_hi; + blog[i].addr_lo = a_lo; + blog[i].type = (uint8_t)params->type; + } + blog[i].rssi = (int8_t)params->rssi; + blog[i].age = 0; + bubble_blog (i); + +#ifdef SERIAL_OUTPUT + host.printf ("ADV:"); + for (i=Gap::ADDR_LEN - 1; i>=0; i--) { // backwards + host.printf ("%2.2x", params->peerAddr[i]); + } + host.printf (":%d:%u:%d:", params->rssi, (unsigned int)params->type, params->advertisingDataLen); + for (i=0; (i<params->advertisingDataLen) && (hlen < (HLEN_MAX-4)); i++) { + uint8_t ch = (uint8_t)params->advertisingData[i]; + + host.printf ("%2.2x", ch); + } + + host.printf ("\r\n"); +#endif +#ifdef LCD_OUTPUT + draw_blog (); +#endif + + return; +} + +#define SCAN_INTERVAL (20) +#define SCAN_WINDOW (20) + + +void ble_init_done (BLE::InitializationCompleteCallbackContext *params) +{ + BLE &ble = params->ble; + ble_error_t err = params->error; + + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to initialise BLE, error code %d\r\n", (int)err); +#endif + return; + } + + if (ble.getInstanceID () != BLE::DEFAULT_INSTANCE) { + // erm.. + return; + } + + // setup for scanning + err = ble.gap().setScanInterval (SCAN_INTERVAL); + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to set scanning interval (%d), code %d\r\n", SCAN_INTERVAL, (int)err); +#endif + return; + } + + err = ble.gap().setScanWindow (SCAN_WINDOW); + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to set scanning window (%d), code %d\r\n", SCAN_WINDOW, (int)err); +#endif + return; + } + + err = ble.gap().setScanTimeout (0); + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to set scan timeout (0), code %d\r\n", (int)err); +#endif + return; + } + + err = ble.gap().setActiveScanning (false); + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to set active scan (false), code %d\r\n", (int)err); +#endif + return; + } + +#ifdef SERIAL_OUTPUT + host.printf ("MSG:Scan parameters set, off we go..\r\n"); +#endif +// Thread::wait (500); + + err = ble.gap().startScan (scan_advert_got); + if (err != BLE_ERROR_NONE) { +#ifdef SERIAL_OUTPUT + host.printf ("ERR:Failed to start scanning (code %d)\r\n", (int)err); +#endif + return; + } + + return; +} + + +// main() runs in its own thread in the OS +// (note the calls to Thread::wait below for delays) +int main() +{ + BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); + uint8_t lw = 0x01; + int acnt = 0; + +#ifdef SERIAL_OUTPUT + host.baud (38400); +#endif + + led1 = 1; + led2 = 0; + led3 = 0; + led4 = 0; + + for (int i=0; i<MAX_BEACONS; i++) { + memset (&blog[i], 0x00, sizeof (beacon_log_t)); + + blog[i].age = 0xff; + } + + /* Note: most of the real initialisation is done inside "ble_init_done" */ + ble.init (ble_init_done); + +#ifdef LCD_OUTPUT + shld_lcd.set_auto_up (0); + shld_lcd.cls (); + shld_lcd.locate (1, 1); + shld_lcd.printf ("Hello, CO657!"); + shld_lcd.copy_to_lcd (); + Thread::wait (500); +#endif + + // GROT + for (;;) { + led1 = (lw & 0x01) ? 0 : 1; + led2 = (lw & 0x02) ? 0 : 1; + led4 = (lw & 0x04) ? 0 : 1; + led3 = (lw & 0x08) ? 0 : 1; + + lw <<= 1; + if (lw == 0x10) { + lw = 0x01; + } + acnt++; + if (acnt == 10) { + acnt = 0; + age_blog (); +#ifdef LCD_OUTPUT + draw_blog (); +#endif + } + + ble.processEvents (); + + Thread::wait (100); + } + +} +