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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002  *  main.cpp -- test program to _simply_ detect iBeacons (and anything else advertising in a similar way)
00003  *  Fred Barnes, October 2016
00004  */
00005 
00006 /* NOTE: based heavily on mbed-os-example-ble's BLE_LEDBlinker example (trimmed down) */
00007 
00008 /* Further note: this is a hack in places */
00009 
00010 #include "mbed.h"
00011 #include "ble/BLE.h"
00012 #include "ble/Gap.h"
00013 #include "C12832.h"
00014 
00015 /* output choice */
00016 #define SERIAL_OUTPUT
00017 #define LCD_OUTPUT
00018 
00019 /* FRMB note: with the MBED application shield connected, the various LEDs/buttons on the nRF52 become pretty helpless */
00020 
00021 // static DigitalOut led1 (LED1);
00022 // static DigitalOut led2 (LED2);
00023 // static DigitalOut led3 (LED3);
00024 // static DigitalOut led4 (LED4);
00025 
00026 static DigitalOut shld_led_r (D5);
00027 static DigitalOut shld_led_g (D9);
00028 static DigitalOut shld_led_b (D8);
00029 
00030 static volatile int drop_flag = 0;
00031 
00032 #ifdef SERIAL_OUTPUT
00033 static Serial host (USBTX, USBRX);
00034 #endif
00035 
00036 #ifdef LCD_OUTPUT
00037 static C12832 shld_lcd (D11, D13, D12, D7, D10);   /* LCD on the shield (128x32) */
00038 #endif
00039 
00040 
00041 
00042 #define HLEN_MAX    (128)
00043 
00044 typedef struct beacon_log {
00045     uint8_t age;                                    /* approx seconds (0xff == dead/gone) */
00046     uint16_t addr_hi;                               /* two high order bytes */
00047     uint32_t addr_lo;                               /* four low order bytes */
00048     int8_t rssi;                                    /* RSSI (-ve) */
00049     uint8_t type;                                   /* crudely cast */
00050 } beacon_log_t;
00051 
00052 #define MAX_BEACONS (32)
00053 
00054 static beacon_log_t blog[MAX_BEACONS];
00055 
00056 void bubble_blog (int idx)
00057 {
00058     beacon_log_t tmp;
00059     
00060     /* crude bubble-sort for single insert/update */
00061     if (idx <= 0) {
00062         return;
00063     }
00064     memcpy (&tmp, &blog[idx], sizeof (beacon_log_t));
00065     while ((idx > 0) && (blog[idx-1].age > tmp.age)) {
00066         memcpy (&blog[idx], &blog[idx-1], sizeof (beacon_log_t));
00067         idx--;
00068     }
00069     memcpy (&blog[idx], &tmp, sizeof (beacon_log_t));
00070     
00071     return;
00072 }
00073 
00074 void age_blog (void)
00075 {
00076     int i;
00077     
00078     for (i=0; i<MAX_BEACONS; i++) {
00079         if (blog[i].age == 0xff) {
00080             break;
00081         }
00082         blog[i].age++;
00083     }
00084     return;
00085 }
00086 
00087 void trigger_red_led (void)
00088 {
00089     shld_led_r = 0;
00090     drop_flag = 3;
00091 }
00092 
00093 void trigger_green_led (void)
00094 {
00095     shld_led_g = 0;
00096     drop_flag = 3;
00097 }
00098 
00099 #ifdef LCD_OUTPUT
00100 void draw_blog (void)
00101 {
00102     shld_lcd.cls ();
00103     for (int i=0; (i<3) && (blog[i].age != 0xff); i++) {
00104         shld_lcd.locate (0, (i * 10));
00105         shld_lcd.printf ("%4.4X%8.8X", blog[i].addr_hi, blog[i].addr_lo);
00106         shld_lcd.locate (72, (i * 10));
00107         shld_lcd.printf ("%d", blog[i].rssi);
00108         shld_lcd.locate (96, (i * 10));
00109         shld_lcd.printf ("%u", blog[i].type);
00110         shld_lcd.locate (108, (i * 10));
00111         shld_lcd.printf ("%u", blog[i].age);
00112     }
00113     shld_lcd.copy_to_lcd ();
00114     return;
00115 }
00116 #endif
00117 
00118 void scan_advert_got (const Gap::AdvertisementCallbackParams_t *params)
00119 {
00120     int i, last;
00121     uint32_t a_lo;
00122     uint16_t a_hi;
00123     int newb = 0;
00124 
00125     trigger_green_led ();
00126     
00127     a_hi = ((uint16_t)params->peerAddr[5] << 8) | (uint16_t)params->peerAddr[4];
00128     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];
00129     
00130     /* scribble the data into the log */
00131     for (i=0, last=-1; i<MAX_BEACONS; i++) {
00132         /* see if we match */
00133         if (blog[i].age == 0xff) {
00134             last = i;
00135             break;
00136         } else if ((blog[i].addr_hi == a_hi) && (blog[i].addr_lo == a_lo)) {
00137             /* this one! */
00138             break;
00139         }
00140     }
00141     if (last >= 0) {
00142         /* new(ish) beacon */
00143         i = last;
00144         newb = 1;
00145     } else if ((i == MAX_BEACONS) && (last < 0)) {
00146         /* ran out of room searching for slot, use last */
00147         i = MAX_BEACONS - 1;
00148         newb = 1;
00149     }   /* else found it somewhere, just reset age */
00150 
00151     if (newb) {
00152         blog[i].addr_hi = a_hi;
00153         blog[i].addr_lo = a_lo;
00154         blog[i].type = (uint8_t)params->type;
00155     }
00156     blog[i].rssi = (int8_t)params->rssi;
00157     blog[i].age = 0;
00158     bubble_blog (i);
00159     
00160 #ifdef SERIAL_OUTPUT
00161     host.printf ("ADV:");
00162     for (i=Gap::ADDR_LEN - 1; i>=0; i--) {          // backwards
00163         host.printf ("%2.2x", params->peerAddr[i]);
00164     }
00165     host.printf (":%d:%u:%d:", params->rssi, (unsigned int)params->type, params->advertisingDataLen);
00166     for (i=0; i<params->advertisingDataLen; i++) {
00167         uint8_t ch = (uint8_t)params->advertisingData[i];
00168         
00169         host.printf ("%2.2x", ch);
00170     }
00171     
00172     host.printf ("\r\n");
00173 #endif
00174 #ifdef LCD_OUTPUT
00175     draw_blog ();
00176 #endif
00177     
00178     return;
00179 }
00180 
00181 #define SCAN_INTERVAL (20)
00182 #define SCAN_WINDOW (20)
00183 
00184 
00185 void ble_init_done (BLE::InitializationCompleteCallbackContext *params)
00186 {
00187     BLE &ble = params->ble;
00188     ble_error_t err = params->error;
00189     
00190     if (err != BLE_ERROR_NONE) {
00191 #ifdef SERIAL_OUTPUT
00192         host.printf ("ERR:Failed to initialise BLE, error code %d\r\n", (int)err);
00193 #endif
00194         return;
00195     }
00196     
00197     if (ble.getInstanceID () != BLE::DEFAULT_INSTANCE) {
00198         // erm..
00199         return;
00200     }
00201     
00202     // setup for scanning
00203     err = ble.gap().setScanInterval (SCAN_INTERVAL);
00204     if (err != BLE_ERROR_NONE) {
00205 #ifdef SERIAL_OUTPUT
00206         host.printf ("ERR:Failed to set scanning interval (%d), code %d\r\n", SCAN_INTERVAL, (int)err);
00207 #endif
00208         return;
00209     }
00210     
00211     err = ble.gap().setScanWindow (SCAN_WINDOW);
00212     if (err != BLE_ERROR_NONE) {
00213 #ifdef SERIAL_OUTPUT
00214         host.printf ("ERR:Failed to set scanning window (%d), code %d\r\n", SCAN_WINDOW, (int)err);
00215 #endif
00216         return;
00217     }
00218     
00219     err = ble.gap().setScanTimeout (0);
00220     if (err != BLE_ERROR_NONE) {
00221 #ifdef SERIAL_OUTPUT
00222         host.printf ("ERR:Failed to set scan timeout (0), code %d\r\n", (int)err);
00223 #endif
00224         return;
00225     }
00226     
00227     err = ble.gap().setActiveScanning (false);
00228     if (err != BLE_ERROR_NONE) {
00229 #ifdef SERIAL_OUTPUT
00230         host.printf ("ERR:Failed to set active scan (false), code %d\r\n", (int)err);
00231 #endif
00232         return;
00233     }
00234     
00235 #ifdef SERIAL_OUTPUT   
00236     host.printf ("MSG:Scan parameters set, off we go..\r\n");
00237 #endif
00238 //    Thread::wait (500);
00239     
00240     err = ble.gap().startScan (scan_advert_got);
00241     if (err != BLE_ERROR_NONE) {
00242 #ifdef SERIAL_OUTPUT
00243         host.printf ("ERR:Failed to start scanning (code %d)\r\n", (int)err);
00244 #endif
00245         return;
00246     }
00247 
00248     return;    
00249 }
00250 
00251 
00252 // main() runs in its own thread in the OS
00253 // (note the calls to Thread::wait below for delays)
00254 int main()
00255 {
00256     BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
00257     int acnt = 0;
00258 
00259 #ifdef SERIAL_OUTPUT
00260     host.baud (38400);
00261     
00262     /* HACK: turn off flow control (okay, this seems to work!) */
00263     {
00264         volatile uint32_t *ubase_psel_cts = (uint32_t *)0x40002510;
00265         volatile uint32_t *ubase_psel_rts = (uint32_t *)0x40002508;
00266         
00267         //host.printf ("MSG: UART: PSEL.RTS reg is 0x%8.8x\r\n", *ubase_psel_rts);
00268         //host.printf ("MSG: UART: PSEL.CTS reg is 0x%8.8x\r\n", *ubase_psel_cts);
00269         
00270         // Crude: disconnect CTS/RTS
00271         *ubase_psel_cts = (*ubase_psel_cts | 0x80000000);
00272         *ubase_psel_rts = (*ubase_psel_rts | 0x80000000);
00273     }
00274 #endif
00275 
00276     for (int i=0; i<MAX_BEACONS; i++) {
00277         memset (&blog[i], 0x00, sizeof (beacon_log_t));
00278         blog[i].age = 0xff;
00279     }
00280 
00281     /* Note: most of the real initialisation is done inside "ble_init_done" */
00282     ble.init (ble_init_done);
00283 
00284 #ifdef LCD_OUTPUT
00285     shld_lcd.set_auto_up (0);
00286     shld_lcd.cls ();
00287     shld_lcd.locate (1, 1);
00288     shld_lcd.printf ("Hello, CO657!");
00289     shld_lcd.copy_to_lcd ();
00290     Thread::wait (500);
00291 #endif
00292 
00293     /* light blue LED to start */
00294     shld_led_r = 1;
00295     shld_led_g = 1;
00296     shld_led_b = 0;
00297     drop_flag = 10;                     /* ~1s */
00298 
00299     // GROT
00300     for (;;) {
00301         acnt++;
00302         if (acnt == 10) {
00303             acnt = 0;
00304             age_blog ();
00305 #ifdef LCD_OUTPUT
00306             draw_blog ();
00307 #endif
00308         }
00309         
00310         ble.processEvents ();
00311         if (drop_flag) {
00312             if (!--drop_flag) {
00313                 shld_led_r = 1;         /* clear LEDs */
00314                 shld_led_g = 1;
00315                 shld_led_b = 1;
00316             }
00317         }
00318         
00319         Thread::wait (100);
00320     }
00321 
00322 }
00323