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

Committer:
co657_frmb
Date:
Fri Oct 28 09:58:31 2016 +0000
Revision:
2:a19832685c89
Parent:
1:7986f9873e20
disable flow-control on serial port (manual)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
co657_frmb 0:f4e27396137e 1 /*
co657_frmb 0:f4e27396137e 2 * main.cpp -- test program to _simply_ detect iBeacons (and anything else advertising in a similar way)
co657_frmb 0:f4e27396137e 3 * Fred Barnes, October 2016
co657_frmb 0:f4e27396137e 4 */
co657_frmb 0:f4e27396137e 5
co657_frmb 0:f4e27396137e 6 /* NOTE: based heavily on mbed-os-example-ble's BLE_LEDBlinker example (trimmed down) */
co657_frmb 0:f4e27396137e 7
co657_frmb 0:f4e27396137e 8 /* Further note: this is a hack in places */
co657_frmb 0:f4e27396137e 9
co657_frmb 0:f4e27396137e 10 #include "mbed.h"
co657_frmb 0:f4e27396137e 11 #include "ble/BLE.h"
co657_frmb 0:f4e27396137e 12 #include "ble/Gap.h"
co657_frmb 0:f4e27396137e 13 #include "C12832.h"
co657_frmb 0:f4e27396137e 14
co657_frmb 0:f4e27396137e 15 /* output choice */
co657_frmb 2:a19832685c89 16 #define SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 17 #define LCD_OUTPUT
co657_frmb 0:f4e27396137e 18
co657_frmb 1:7986f9873e20 19 /* FRMB note: with the MBED application shield connected, the various LEDs/buttons on the nRF52 become pretty helpless */
co657_frmb 1:7986f9873e20 20
co657_frmb 1:7986f9873e20 21 // static DigitalOut led1 (LED1);
co657_frmb 1:7986f9873e20 22 // static DigitalOut led2 (LED2);
co657_frmb 1:7986f9873e20 23 // static DigitalOut led3 (LED3);
co657_frmb 1:7986f9873e20 24 // static DigitalOut led4 (LED4);
co657_frmb 1:7986f9873e20 25
co657_frmb 1:7986f9873e20 26 static DigitalOut shld_led_r (D5);
co657_frmb 1:7986f9873e20 27 static DigitalOut shld_led_g (D9);
co657_frmb 1:7986f9873e20 28 static DigitalOut shld_led_b (D8);
co657_frmb 1:7986f9873e20 29
co657_frmb 1:7986f9873e20 30 static volatile int drop_flag = 0;
co657_frmb 0:f4e27396137e 31
co657_frmb 0:f4e27396137e 32 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 33 static Serial host (USBTX, USBRX);
co657_frmb 0:f4e27396137e 34 #endif
co657_frmb 0:f4e27396137e 35
co657_frmb 0:f4e27396137e 36 #ifdef LCD_OUTPUT
co657_frmb 0:f4e27396137e 37 static C12832 shld_lcd (D11, D13, D12, D7, D10); /* LCD on the shield (128x32) */
co657_frmb 0:f4e27396137e 38 #endif
co657_frmb 0:f4e27396137e 39
co657_frmb 0:f4e27396137e 40
co657_frmb 0:f4e27396137e 41
co657_frmb 0:f4e27396137e 42 #define HLEN_MAX (128)
co657_frmb 0:f4e27396137e 43
co657_frmb 0:f4e27396137e 44 typedef struct beacon_log {
co657_frmb 0:f4e27396137e 45 uint8_t age; /* approx seconds (0xff == dead/gone) */
co657_frmb 0:f4e27396137e 46 uint16_t addr_hi; /* two high order bytes */
co657_frmb 0:f4e27396137e 47 uint32_t addr_lo; /* four low order bytes */
co657_frmb 0:f4e27396137e 48 int8_t rssi; /* RSSI (-ve) */
co657_frmb 0:f4e27396137e 49 uint8_t type; /* crudely cast */
co657_frmb 0:f4e27396137e 50 } beacon_log_t;
co657_frmb 0:f4e27396137e 51
co657_frmb 0:f4e27396137e 52 #define MAX_BEACONS (32)
co657_frmb 0:f4e27396137e 53
co657_frmb 0:f4e27396137e 54 static beacon_log_t blog[MAX_BEACONS];
co657_frmb 0:f4e27396137e 55
co657_frmb 0:f4e27396137e 56 void bubble_blog (int idx)
co657_frmb 0:f4e27396137e 57 {
co657_frmb 0:f4e27396137e 58 beacon_log_t tmp;
co657_frmb 0:f4e27396137e 59
co657_frmb 0:f4e27396137e 60 /* crude bubble-sort for single insert/update */
co657_frmb 0:f4e27396137e 61 if (idx <= 0) {
co657_frmb 0:f4e27396137e 62 return;
co657_frmb 0:f4e27396137e 63 }
co657_frmb 0:f4e27396137e 64 memcpy (&tmp, &blog[idx], sizeof (beacon_log_t));
co657_frmb 0:f4e27396137e 65 while ((idx > 0) && (blog[idx-1].age > tmp.age)) {
co657_frmb 0:f4e27396137e 66 memcpy (&blog[idx], &blog[idx-1], sizeof (beacon_log_t));
co657_frmb 0:f4e27396137e 67 idx--;
co657_frmb 0:f4e27396137e 68 }
co657_frmb 0:f4e27396137e 69 memcpy (&blog[idx], &tmp, sizeof (beacon_log_t));
co657_frmb 0:f4e27396137e 70
co657_frmb 0:f4e27396137e 71 return;
co657_frmb 0:f4e27396137e 72 }
co657_frmb 0:f4e27396137e 73
co657_frmb 0:f4e27396137e 74 void age_blog (void)
co657_frmb 0:f4e27396137e 75 {
co657_frmb 0:f4e27396137e 76 int i;
co657_frmb 0:f4e27396137e 77
co657_frmb 0:f4e27396137e 78 for (i=0; i<MAX_BEACONS; i++) {
co657_frmb 0:f4e27396137e 79 if (blog[i].age == 0xff) {
co657_frmb 0:f4e27396137e 80 break;
co657_frmb 0:f4e27396137e 81 }
co657_frmb 0:f4e27396137e 82 blog[i].age++;
co657_frmb 0:f4e27396137e 83 }
co657_frmb 0:f4e27396137e 84 return;
co657_frmb 0:f4e27396137e 85 }
co657_frmb 0:f4e27396137e 86
co657_frmb 1:7986f9873e20 87 void trigger_red_led (void)
co657_frmb 1:7986f9873e20 88 {
co657_frmb 1:7986f9873e20 89 shld_led_r = 0;
co657_frmb 1:7986f9873e20 90 drop_flag = 3;
co657_frmb 1:7986f9873e20 91 }
co657_frmb 1:7986f9873e20 92
co657_frmb 1:7986f9873e20 93 void trigger_green_led (void)
co657_frmb 1:7986f9873e20 94 {
co657_frmb 1:7986f9873e20 95 shld_led_g = 0;
co657_frmb 1:7986f9873e20 96 drop_flag = 3;
co657_frmb 1:7986f9873e20 97 }
co657_frmb 1:7986f9873e20 98
co657_frmb 0:f4e27396137e 99 #ifdef LCD_OUTPUT
co657_frmb 0:f4e27396137e 100 void draw_blog (void)
co657_frmb 0:f4e27396137e 101 {
co657_frmb 0:f4e27396137e 102 shld_lcd.cls ();
co657_frmb 0:f4e27396137e 103 for (int i=0; (i<3) && (blog[i].age != 0xff); i++) {
co657_frmb 0:f4e27396137e 104 shld_lcd.locate (0, (i * 10));
co657_frmb 0:f4e27396137e 105 shld_lcd.printf ("%4.4X%8.8X", blog[i].addr_hi, blog[i].addr_lo);
co657_frmb 0:f4e27396137e 106 shld_lcd.locate (72, (i * 10));
co657_frmb 0:f4e27396137e 107 shld_lcd.printf ("%d", blog[i].rssi);
co657_frmb 0:f4e27396137e 108 shld_lcd.locate (96, (i * 10));
co657_frmb 0:f4e27396137e 109 shld_lcd.printf ("%u", blog[i].type);
co657_frmb 0:f4e27396137e 110 shld_lcd.locate (108, (i * 10));
co657_frmb 0:f4e27396137e 111 shld_lcd.printf ("%u", blog[i].age);
co657_frmb 0:f4e27396137e 112 }
co657_frmb 0:f4e27396137e 113 shld_lcd.copy_to_lcd ();
co657_frmb 0:f4e27396137e 114 return;
co657_frmb 0:f4e27396137e 115 }
co657_frmb 0:f4e27396137e 116 #endif
co657_frmb 0:f4e27396137e 117
co657_frmb 0:f4e27396137e 118 void scan_advert_got (const Gap::AdvertisementCallbackParams_t *params)
co657_frmb 0:f4e27396137e 119 {
co657_frmb 0:f4e27396137e 120 int i, last;
co657_frmb 0:f4e27396137e 121 uint32_t a_lo;
co657_frmb 0:f4e27396137e 122 uint16_t a_hi;
co657_frmb 0:f4e27396137e 123 int newb = 0;
co657_frmb 0:f4e27396137e 124
co657_frmb 1:7986f9873e20 125 trigger_green_led ();
co657_frmb 1:7986f9873e20 126
co657_frmb 0:f4e27396137e 127 a_hi = ((uint16_t)params->peerAddr[5] << 8) | (uint16_t)params->peerAddr[4];
co657_frmb 0:f4e27396137e 128 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];
co657_frmb 0:f4e27396137e 129
co657_frmb 0:f4e27396137e 130 /* scribble the data into the log */
co657_frmb 0:f4e27396137e 131 for (i=0, last=-1; i<MAX_BEACONS; i++) {
co657_frmb 0:f4e27396137e 132 /* see if we match */
co657_frmb 0:f4e27396137e 133 if (blog[i].age == 0xff) {
co657_frmb 0:f4e27396137e 134 last = i;
co657_frmb 0:f4e27396137e 135 break;
co657_frmb 0:f4e27396137e 136 } else if ((blog[i].addr_hi == a_hi) && (blog[i].addr_lo == a_lo)) {
co657_frmb 0:f4e27396137e 137 /* this one! */
co657_frmb 0:f4e27396137e 138 break;
co657_frmb 0:f4e27396137e 139 }
co657_frmb 0:f4e27396137e 140 }
co657_frmb 0:f4e27396137e 141 if (last >= 0) {
co657_frmb 0:f4e27396137e 142 /* new(ish) beacon */
co657_frmb 0:f4e27396137e 143 i = last;
co657_frmb 0:f4e27396137e 144 newb = 1;
co657_frmb 0:f4e27396137e 145 } else if ((i == MAX_BEACONS) && (last < 0)) {
co657_frmb 0:f4e27396137e 146 /* ran out of room searching for slot, use last */
co657_frmb 0:f4e27396137e 147 i = MAX_BEACONS - 1;
co657_frmb 0:f4e27396137e 148 newb = 1;
co657_frmb 0:f4e27396137e 149 } /* else found it somewhere, just reset age */
co657_frmb 0:f4e27396137e 150
co657_frmb 0:f4e27396137e 151 if (newb) {
co657_frmb 0:f4e27396137e 152 blog[i].addr_hi = a_hi;
co657_frmb 0:f4e27396137e 153 blog[i].addr_lo = a_lo;
co657_frmb 0:f4e27396137e 154 blog[i].type = (uint8_t)params->type;
co657_frmb 0:f4e27396137e 155 }
co657_frmb 0:f4e27396137e 156 blog[i].rssi = (int8_t)params->rssi;
co657_frmb 0:f4e27396137e 157 blog[i].age = 0;
co657_frmb 0:f4e27396137e 158 bubble_blog (i);
co657_frmb 0:f4e27396137e 159
co657_frmb 0:f4e27396137e 160 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 161 host.printf ("ADV:");
co657_frmb 0:f4e27396137e 162 for (i=Gap::ADDR_LEN - 1; i>=0; i--) { // backwards
co657_frmb 0:f4e27396137e 163 host.printf ("%2.2x", params->peerAddr[i]);
co657_frmb 0:f4e27396137e 164 }
co657_frmb 0:f4e27396137e 165 host.printf (":%d:%u:%d:", params->rssi, (unsigned int)params->type, params->advertisingDataLen);
co657_frmb 2:a19832685c89 166 for (i=0; i<params->advertisingDataLen; i++) {
co657_frmb 0:f4e27396137e 167 uint8_t ch = (uint8_t)params->advertisingData[i];
co657_frmb 0:f4e27396137e 168
co657_frmb 0:f4e27396137e 169 host.printf ("%2.2x", ch);
co657_frmb 0:f4e27396137e 170 }
co657_frmb 0:f4e27396137e 171
co657_frmb 0:f4e27396137e 172 host.printf ("\r\n");
co657_frmb 0:f4e27396137e 173 #endif
co657_frmb 0:f4e27396137e 174 #ifdef LCD_OUTPUT
co657_frmb 0:f4e27396137e 175 draw_blog ();
co657_frmb 0:f4e27396137e 176 #endif
co657_frmb 0:f4e27396137e 177
co657_frmb 0:f4e27396137e 178 return;
co657_frmb 0:f4e27396137e 179 }
co657_frmb 0:f4e27396137e 180
co657_frmb 0:f4e27396137e 181 #define SCAN_INTERVAL (20)
co657_frmb 0:f4e27396137e 182 #define SCAN_WINDOW (20)
co657_frmb 0:f4e27396137e 183
co657_frmb 0:f4e27396137e 184
co657_frmb 0:f4e27396137e 185 void ble_init_done (BLE::InitializationCompleteCallbackContext *params)
co657_frmb 0:f4e27396137e 186 {
co657_frmb 0:f4e27396137e 187 BLE &ble = params->ble;
co657_frmb 0:f4e27396137e 188 ble_error_t err = params->error;
co657_frmb 0:f4e27396137e 189
co657_frmb 0:f4e27396137e 190 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 191 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 192 host.printf ("ERR:Failed to initialise BLE, error code %d\r\n", (int)err);
co657_frmb 0:f4e27396137e 193 #endif
co657_frmb 0:f4e27396137e 194 return;
co657_frmb 0:f4e27396137e 195 }
co657_frmb 0:f4e27396137e 196
co657_frmb 0:f4e27396137e 197 if (ble.getInstanceID () != BLE::DEFAULT_INSTANCE) {
co657_frmb 0:f4e27396137e 198 // erm..
co657_frmb 0:f4e27396137e 199 return;
co657_frmb 0:f4e27396137e 200 }
co657_frmb 0:f4e27396137e 201
co657_frmb 0:f4e27396137e 202 // setup for scanning
co657_frmb 0:f4e27396137e 203 err = ble.gap().setScanInterval (SCAN_INTERVAL);
co657_frmb 0:f4e27396137e 204 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 205 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 206 host.printf ("ERR:Failed to set scanning interval (%d), code %d\r\n", SCAN_INTERVAL, (int)err);
co657_frmb 0:f4e27396137e 207 #endif
co657_frmb 0:f4e27396137e 208 return;
co657_frmb 0:f4e27396137e 209 }
co657_frmb 0:f4e27396137e 210
co657_frmb 0:f4e27396137e 211 err = ble.gap().setScanWindow (SCAN_WINDOW);
co657_frmb 0:f4e27396137e 212 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 213 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 214 host.printf ("ERR:Failed to set scanning window (%d), code %d\r\n", SCAN_WINDOW, (int)err);
co657_frmb 0:f4e27396137e 215 #endif
co657_frmb 0:f4e27396137e 216 return;
co657_frmb 0:f4e27396137e 217 }
co657_frmb 0:f4e27396137e 218
co657_frmb 0:f4e27396137e 219 err = ble.gap().setScanTimeout (0);
co657_frmb 0:f4e27396137e 220 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 221 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 222 host.printf ("ERR:Failed to set scan timeout (0), code %d\r\n", (int)err);
co657_frmb 0:f4e27396137e 223 #endif
co657_frmb 0:f4e27396137e 224 return;
co657_frmb 0:f4e27396137e 225 }
co657_frmb 0:f4e27396137e 226
co657_frmb 0:f4e27396137e 227 err = ble.gap().setActiveScanning (false);
co657_frmb 0:f4e27396137e 228 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 229 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 230 host.printf ("ERR:Failed to set active scan (false), code %d\r\n", (int)err);
co657_frmb 0:f4e27396137e 231 #endif
co657_frmb 0:f4e27396137e 232 return;
co657_frmb 0:f4e27396137e 233 }
co657_frmb 0:f4e27396137e 234
co657_frmb 0:f4e27396137e 235 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 236 host.printf ("MSG:Scan parameters set, off we go..\r\n");
co657_frmb 0:f4e27396137e 237 #endif
co657_frmb 0:f4e27396137e 238 // Thread::wait (500);
co657_frmb 0:f4e27396137e 239
co657_frmb 0:f4e27396137e 240 err = ble.gap().startScan (scan_advert_got);
co657_frmb 0:f4e27396137e 241 if (err != BLE_ERROR_NONE) {
co657_frmb 0:f4e27396137e 242 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 243 host.printf ("ERR:Failed to start scanning (code %d)\r\n", (int)err);
co657_frmb 0:f4e27396137e 244 #endif
co657_frmb 0:f4e27396137e 245 return;
co657_frmb 0:f4e27396137e 246 }
co657_frmb 0:f4e27396137e 247
co657_frmb 0:f4e27396137e 248 return;
co657_frmb 0:f4e27396137e 249 }
co657_frmb 0:f4e27396137e 250
co657_frmb 0:f4e27396137e 251
co657_frmb 0:f4e27396137e 252 // main() runs in its own thread in the OS
co657_frmb 0:f4e27396137e 253 // (note the calls to Thread::wait below for delays)
co657_frmb 0:f4e27396137e 254 int main()
co657_frmb 0:f4e27396137e 255 {
co657_frmb 0:f4e27396137e 256 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
co657_frmb 0:f4e27396137e 257 int acnt = 0;
co657_frmb 0:f4e27396137e 258
co657_frmb 0:f4e27396137e 259 #ifdef SERIAL_OUTPUT
co657_frmb 0:f4e27396137e 260 host.baud (38400);
co657_frmb 2:a19832685c89 261
co657_frmb 2:a19832685c89 262 /* HACK: turn off flow control (okay, this seems to work!) */
co657_frmb 2:a19832685c89 263 {
co657_frmb 2:a19832685c89 264 volatile uint32_t *ubase_psel_cts = (uint32_t *)0x40002510;
co657_frmb 2:a19832685c89 265 volatile uint32_t *ubase_psel_rts = (uint32_t *)0x40002508;
co657_frmb 2:a19832685c89 266
co657_frmb 2:a19832685c89 267 //host.printf ("MSG: UART: PSEL.RTS reg is 0x%8.8x\r\n", *ubase_psel_rts);
co657_frmb 2:a19832685c89 268 //host.printf ("MSG: UART: PSEL.CTS reg is 0x%8.8x\r\n", *ubase_psel_cts);
co657_frmb 2:a19832685c89 269
co657_frmb 2:a19832685c89 270 // Crude: disconnect CTS/RTS
co657_frmb 2:a19832685c89 271 *ubase_psel_cts = (*ubase_psel_cts | 0x80000000);
co657_frmb 2:a19832685c89 272 *ubase_psel_rts = (*ubase_psel_rts | 0x80000000);
co657_frmb 2:a19832685c89 273 }
co657_frmb 0:f4e27396137e 274 #endif
co657_frmb 0:f4e27396137e 275
co657_frmb 0:f4e27396137e 276 for (int i=0; i<MAX_BEACONS; i++) {
co657_frmb 0:f4e27396137e 277 memset (&blog[i], 0x00, sizeof (beacon_log_t));
co657_frmb 0:f4e27396137e 278 blog[i].age = 0xff;
co657_frmb 0:f4e27396137e 279 }
co657_frmb 0:f4e27396137e 280
co657_frmb 0:f4e27396137e 281 /* Note: most of the real initialisation is done inside "ble_init_done" */
co657_frmb 0:f4e27396137e 282 ble.init (ble_init_done);
co657_frmb 0:f4e27396137e 283
co657_frmb 0:f4e27396137e 284 #ifdef LCD_OUTPUT
co657_frmb 0:f4e27396137e 285 shld_lcd.set_auto_up (0);
co657_frmb 0:f4e27396137e 286 shld_lcd.cls ();
co657_frmb 0:f4e27396137e 287 shld_lcd.locate (1, 1);
co657_frmb 0:f4e27396137e 288 shld_lcd.printf ("Hello, CO657!");
co657_frmb 0:f4e27396137e 289 shld_lcd.copy_to_lcd ();
co657_frmb 0:f4e27396137e 290 Thread::wait (500);
co657_frmb 0:f4e27396137e 291 #endif
co657_frmb 0:f4e27396137e 292
co657_frmb 1:7986f9873e20 293 /* light blue LED to start */
co657_frmb 1:7986f9873e20 294 shld_led_r = 1;
co657_frmb 1:7986f9873e20 295 shld_led_g = 1;
co657_frmb 1:7986f9873e20 296 shld_led_b = 0;
co657_frmb 1:7986f9873e20 297 drop_flag = 10; /* ~1s */
co657_frmb 1:7986f9873e20 298
co657_frmb 0:f4e27396137e 299 // GROT
co657_frmb 0:f4e27396137e 300 for (;;) {
co657_frmb 0:f4e27396137e 301 acnt++;
co657_frmb 0:f4e27396137e 302 if (acnt == 10) {
co657_frmb 0:f4e27396137e 303 acnt = 0;
co657_frmb 0:f4e27396137e 304 age_blog ();
co657_frmb 0:f4e27396137e 305 #ifdef LCD_OUTPUT
co657_frmb 0:f4e27396137e 306 draw_blog ();
co657_frmb 0:f4e27396137e 307 #endif
co657_frmb 0:f4e27396137e 308 }
co657_frmb 0:f4e27396137e 309
co657_frmb 0:f4e27396137e 310 ble.processEvents ();
co657_frmb 1:7986f9873e20 311 if (drop_flag) {
co657_frmb 1:7986f9873e20 312 if (!--drop_flag) {
co657_frmb 1:7986f9873e20 313 shld_led_r = 1; /* clear LEDs */
co657_frmb 1:7986f9873e20 314 shld_led_g = 1;
co657_frmb 1:7986f9873e20 315 shld_led_b = 1;
co657_frmb 1:7986f9873e20 316 }
co657_frmb 1:7986f9873e20 317 }
co657_frmb 0:f4e27396137e 318
co657_frmb 0:f4e27396137e 319 Thread::wait (100);
co657_frmb 0:f4e27396137e 320 }
co657_frmb 0:f4e27396137e 321
co657_frmb 0:f4e27396137e 322 }
co657_frmb 0:f4e27396137e 323