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.

Dependencies:   C12832

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);
+    }
+
+}
+