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

main.cpp

Committer:
co657_frmb
Date:
2016-10-28
Revision:
2:a19832685c89
Parent:
1:7986f9873e20

File content as of revision 2:a19832685c89:

/*
 *  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 */
#define SERIAL_OUTPUT
#define LCD_OUTPUT

/* FRMB note: with the MBED application shield connected, the various LEDs/buttons on the nRF52 become pretty helpless */

// static DigitalOut led1 (LED1);
// static DigitalOut led2 (LED2);
// static DigitalOut led3 (LED3);
// static DigitalOut led4 (LED4);

static DigitalOut shld_led_r (D5);
static DigitalOut shld_led_g (D9);
static DigitalOut shld_led_b (D8);

static volatile int drop_flag = 0;

#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;
}

void trigger_red_led (void)
{
    shld_led_r = 0;
    drop_flag = 3;
}

void trigger_green_led (void)
{
    shld_led_g = 0;
    drop_flag = 3;
}

#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;

    trigger_green_led ();
    
    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; 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);
    int acnt = 0;

#ifdef SERIAL_OUTPUT
    host.baud (38400);
    
    /* HACK: turn off flow control (okay, this seems to work!) */
    {
        volatile uint32_t *ubase_psel_cts = (uint32_t *)0x40002510;
        volatile uint32_t *ubase_psel_rts = (uint32_t *)0x40002508;
        
        //host.printf ("MSG: UART: PSEL.RTS reg is 0x%8.8x\r\n", *ubase_psel_rts);
        //host.printf ("MSG: UART: PSEL.CTS reg is 0x%8.8x\r\n", *ubase_psel_cts);
        
        // Crude: disconnect CTS/RTS
        *ubase_psel_cts = (*ubase_psel_cts | 0x80000000);
        *ubase_psel_rts = (*ubase_psel_rts | 0x80000000);
    }
#endif

    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

    /* light blue LED to start */
    shld_led_r = 1;
    shld_led_g = 1;
    shld_led_b = 0;
    drop_flag = 10;                     /* ~1s */

    // GROT
    for (;;) {
        acnt++;
        if (acnt == 10) {
            acnt = 0;
            age_blog ();
#ifdef LCD_OUTPUT
            draw_blog ();
#endif
        }
        
        ble.processEvents ();
        if (drop_flag) {
            if (!--drop_flag) {
                shld_led_r = 1;         /* clear LEDs */
                shld_led_g = 1;
                shld_led_b = 1;
            }
        }
        
        Thread::wait (100);
    }

}