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.
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
Generated on Wed Jul 13 2022 20:38:28 by 1.7.2