Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed
main.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2013 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include <events/mbed_events.h> 00018 #include <mbed.h> 00019 #include "ble/BLE.h" 00020 00021 /** This example demonstrates all the basic setup required 00022 * to advertise, scan and connect to other devices. 00023 * 00024 * It contains a single class that performs both scans and advertisements. 00025 * 00026 * The demonstrations happens in sequence, after each "mode" ends 00027 * the demo jumps to the next mode to continue. There are several modes 00028 * that show scanning and several showing advertising. These are configured 00029 * according to the two arrays containing parameters. During scanning 00030 * a connection will be made to a connectable device upon its discovery. 00031 */ 00032 00033 static const uint8_t DEVICE_NAME[] = "GAP_device"; 00034 00035 /* Duration of each mode in milliseconds */ 00036 static const size_t MODE_DURATION_MS = 6000; 00037 00038 /* Time between each mode in milliseconds */ 00039 static const size_t TIME_BETWEEN_MODES_MS = 2000; 00040 00041 /* how long to wait before disconnecting in milliseconds */ 00042 static const size_t CONNECTION_DURATION = 3000; 00043 00044 typedef struct { 00045 GapAdvertisingParams::AdvertisingType_t adv_type; 00046 uint16_t interval; 00047 uint16_t timeout; 00048 } AdvModeParam_t; 00049 00050 typedef struct { 00051 uint16_t interval; 00052 uint16_t window; 00053 uint16_t timeout; 00054 bool active; 00055 } ScanModeParam_t; 00056 00057 /** the entries in this array are used to configure our advertising 00058 * parameters for each of the modes we use in our demo */ 00059 static const AdvModeParam_t advertising_params[] = { 00060 /* advertising type interval timeout */ 00061 { GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED, 40,/*ms*/ 3/*s*/}, 00062 { GapAdvertisingParams::ADV_SCANNABLE_UNDIRECTED, 100, 4 }, 00063 { GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED, 100, 0 } 00064 }; 00065 00066 /* when we cycle through all our advertising modes we will move to scanning modes */ 00067 00068 /** the entries in this array are used to configure our scanning 00069 * parameters for each of the modes we use in our demo */ 00070 static const ScanModeParam_t scanning_params[] = { 00071 /* interval window timeout active */ 00072 { 4,/*ms*/ 4,/*ms*/ 0,/*s*/ false }, 00073 { 160, 100, 3, false }, 00074 { 160, 40, 0, true }, 00075 { 500, 10, 0, false } 00076 }; 00077 00078 /* parameters to use when attempting to connect to maximise speed of connection */ 00079 static const GapScanningParams connection_scan_params( 00080 GapScanningParams::SCAN_INTERVAL_MAX, 00081 GapScanningParams::SCAN_WINDOW_MAX, 00082 3, 00083 false 00084 ); 00085 00086 /* get number of items in our arrays */ 00087 static const size_t SCAN_PARAM_SET_MAX = 00088 sizeof(scanning_params) / sizeof(GapScanningParams); 00089 static const size_t ADV_PARAM_SET_MAX = 00090 sizeof(advertising_params) / sizeof(GapAdvertisingParams); 00091 00092 00093 /** Demonstrate advertising, scanning and connecting 00094 */ 00095 class GAPDevice : private mbed::NonCopyable<GAPDevice> 00096 { 00097 public: 00098 GAPDevice() : 00099 _ble(BLE::Instance()), 00100 _led1(LED1, 0), 00101 _set_index(0), 00102 _is_in_scanning_mode(false), 00103 _is_connecting(false), 00104 _on_duration_end_id(0), 00105 _scan_count(0) { }; 00106 00107 ~GAPDevice() 00108 { 00109 if (_ble.hasInitialized()) { 00110 _ble.shutdown(); 00111 } 00112 }; 00113 00114 /** Start BLE interface initialisation */ 00115 void run() 00116 { 00117 ble_error_t error; 00118 00119 if (_ble.hasInitialized()) { 00120 printf("Ble instance already initialised.\r\n"); 00121 return; 00122 } 00123 00124 /* this will inform us off all events so we can schedule their handling 00125 * using our event queue */ 00126 _ble.onEventsToProcess( 00127 makeFunctionPointer(this, &GAPDevice::schedule_ble_events) 00128 ); 00129 00130 /* handle timeouts, for example when connection attempts fail */ 00131 _ble.gap().onTimeout( 00132 makeFunctionPointer(this, &GAPDevice::on_timeout) 00133 ); 00134 00135 error = _ble.init(this, &GAPDevice::on_init_complete); 00136 00137 if (error) { 00138 printf("Error returned by BLE::init.\r\n"); 00139 return; 00140 } 00141 00142 /* to show we're running we'll blink every 500ms */ 00143 _event_queue.call_every(500, this, &GAPDevice::blink); 00144 00145 /* this will not return until shutdown */ 00146 _event_queue.dispatch_forever(); 00147 }; 00148 00149 private: 00150 /** This is called when BLE interface is initialised and starts the first mode */ 00151 void on_init_complete(BLE::InitializationCompleteCallbackContext *event) 00152 { 00153 if (event->error) { 00154 printf("Error during the initialisation\r\n"); 00155 return; 00156 } 00157 00158 /* print device address */ 00159 Gap::AddressType_t addr_type; 00160 Gap::Address_t addr; 00161 _ble.gap().getAddress(&addr_type, addr); 00162 printf("Device address: %02x:%02x:%02x:%02x:%02x:%02x\r\n", 00163 addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); 00164 00165 /* all calls are serialised on the user thread through the event queue */ 00166 _event_queue.call(this, &GAPDevice::demo_mode_start); 00167 }; 00168 00169 /** queue up start of the current demo mode */ 00170 void demo_mode_start() 00171 { 00172 if (_is_in_scanning_mode) { 00173 /* when scanning we want to connect to a peer device so we need to 00174 * attach callbacks that are used by Gap to notify us of events */ 00175 _ble.gap().onConnection(this, &GAPDevice::on_connect); 00176 _ble.gap().onDisconnection(this, &GAPDevice::on_disconnect); 00177 00178 _event_queue.call(this, &GAPDevice::scan); 00179 } else { 00180 _event_queue.call(this, &GAPDevice::advertise); 00181 } 00182 00183 /* for performance measurement keep track of duration of the demo mode */ 00184 _demo_duration.start(); 00185 /* keep track of our state */ 00186 _is_connecting = false; 00187 00188 /* queue up next demo mode */ 00189 _on_duration_end_id = _event_queue.call_in( 00190 MODE_DURATION_MS, this, &GAPDevice::on_duration_end 00191 ); 00192 00193 printf("\r\n"); 00194 } 00195 00196 /** Set up and start advertising */ 00197 void advertise() 00198 { 00199 ble_error_t error; 00200 GapAdvertisingData advertising_data; 00201 00202 /* add advertising flags */ 00203 advertising_data.addFlags(GapAdvertisingData::LE_GENERAL_DISCOVERABLE 00204 | GapAdvertisingData::BREDR_NOT_SUPPORTED); 00205 00206 /* add device name */ 00207 advertising_data.addData( 00208 GapAdvertisingData::COMPLETE_LOCAL_NAME, 00209 DEVICE_NAME, 00210 sizeof(DEVICE_NAME) 00211 ); 00212 00213 error = _ble.gap().setAdvertisingPayload(advertising_data); 00214 00215 if (error) { 00216 printf("Error during Gap::setAdvertisingPayload\r\n"); 00217 return; 00218 } 00219 00220 /* set the advertising parameters according to currently selected set, 00221 * see @AdvertisingType_t for explanation of modes */ 00222 GapAdvertisingParams::AdvertisingType_t adv_type = 00223 advertising_params[_set_index].adv_type; 00224 00225 /* how many milliseconds between advertisements, lower interval 00226 * increases the chances of being seen at the cost of more power */ 00227 uint16_t interval = advertising_params[_set_index].interval; 00228 00229 /* advertising will continue for this many seconds or until connected */ 00230 uint16_t timeout = advertising_params[_set_index].timeout; 00231 00232 _ble.gap().setAdvertisingType(adv_type); 00233 _ble.gap().setAdvertisingInterval(interval); 00234 _ble.gap().setAdvertisingTimeout(timeout); 00235 00236 error = _ble.gap().startAdvertising(); 00237 00238 if (error) { 00239 printf("Error during Gap::startAdvertising.\r\n"); 00240 return; 00241 } 00242 00243 printf("Advertising started (type: 0x%x, interval: %dms, timeout: %ds)\r\n", 00244 adv_type, interval, timeout); 00245 }; 00246 00247 /** Set up and start scanning */ 00248 void scan() 00249 { 00250 ble_error_t error; 00251 00252 /* scanning happens repeatedly, interval is the number of milliseconds 00253 * between each cycle of scanning */ 00254 uint16_t interval = scanning_params[_set_index].interval; 00255 00256 /* number of milliseconds we scan for each time we enter 00257 * the scanning cycle after the interval set above */ 00258 uint16_t window = scanning_params[_set_index].window; 00259 00260 /* how long to repeat the cycles of scanning in seconds */ 00261 uint16_t timeout = scanning_params[_set_index].timeout; 00262 00263 /* active scanning will send a scan request to any scanable devices that 00264 * we see advertising */ 00265 bool active = scanning_params[_set_index].active; 00266 00267 /* set the scanning parameters according to currently selected set */ 00268 error = _ble.gap().setScanParams(interval, window, timeout, active); 00269 00270 if (error) { 00271 printf("Error during Gap::setScanParams\r\n"); 00272 return; 00273 } 00274 00275 /* start scanning and attach a callback that will handle advertisements 00276 * and scan requests responses */ 00277 error = _ble.gap().startScan(this, &GAPDevice::on_scan); 00278 00279 if (error) { 00280 printf("Error during Gap::startScan\r\n"); 00281 return; 00282 } 00283 00284 printf("Scanning started (interval: %dms, window: %dms, timeout: %ds).\r\n", 00285 interval, window, timeout); 00286 }; 00287 00288 /** After a set duration this cycles to the next demo mode 00289 * unless a connection happened first */ 00290 void on_duration_end() 00291 { 00292 print_performance(); 00293 00294 /* alloted time has elapsed, move to next demo mode */ 00295 _event_queue.call(this, &GAPDevice::demo_mode_end); 00296 }; 00297 00298 /** Look at scan payload to find a peer device and connect to it */ 00299 void on_scan(const Gap::AdvertisementCallbackParams_t *params) 00300 { 00301 /* keep track of scan events for performance reporting */ 00302 _scan_count++; 00303 00304 /* don't bother with analysing scan result if we're already connecting */ 00305 if (_is_connecting) { 00306 return; 00307 } 00308 00309 /* parse the advertising payload, looking for a discoverable device */ 00310 for (uint8_t i = 0; i < params->advertisingDataLen; ++i) { 00311 /* The advertising payload is a collection of key/value records where 00312 * byte 0: length of the record excluding this byte 00313 * byte 1: The key, it is the type of the data 00314 * byte [2..N] The value. N is equal to byte0 - 1 */ 00315 const uint8_t record_length = params->advertisingData[i]; 00316 if (record_length == 0) { 00317 continue; 00318 } 00319 const uint8_t type = params->advertisingData[i + 1]; 00320 const uint8_t *value = params->advertisingData + i + 2; 00321 00322 /* connect to a discoverable device */ 00323 if ((type == GapAdvertisingData::FLAGS) 00324 && (*value & GapAdvertisingData::LE_GENERAL_DISCOVERABLE)) { 00325 00326 /* abort timeout as the mode will end on disconnection */ 00327 _event_queue.cancel(_on_duration_end_id); 00328 00329 printf("We found a connectable device\r\n"); 00330 00331 ble_error_t error = _ble.gap().connect( 00332 params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, 00333 NULL, &connection_scan_params 00334 ); 00335 00336 if (error) { 00337 printf("Error during Gap::connect\r\n"); 00338 /* since no connection will be attempted end the mode */ 00339 _event_queue.call(this, &GAPDevice::demo_mode_end); 00340 return; 00341 } 00342 00343 /* we may have already scan events waiting 00344 * to be processed so we need to remember 00345 * that we are already connecting and ignore them */ 00346 _is_connecting = true; 00347 00348 return; 00349 } 00350 00351 i += record_length; 00352 } 00353 }; 00354 00355 /** This is called by Gap to notify the application we connected, 00356 * in our case it immediately disconnects */ 00357 void on_connect(const Gap::ConnectionCallbackParams_t *connection_event) 00358 { 00359 print_performance(); 00360 00361 printf("Connected in %dms\r\n", _demo_duration.read_ms()); 00362 00363 /* cancel the connect timeout since we connected */ 00364 _event_queue.cancel(_on_duration_end_id); 00365 00366 _event_queue.call_in( 00367 CONNECTION_DURATION, &_ble.gap(), &Gap::disconnect, Gap::REMOTE_USER_TERMINATED_CONNECTION 00368 ); 00369 }; 00370 00371 /** This is called by Gap to notify the application we disconnected, 00372 * in our case it calls demo_mode_end() to progress the demo */ 00373 void on_disconnect(const Gap::DisconnectionCallbackParams_t *event) 00374 { 00375 printf("Disconnected\r\n"); 00376 00377 /* we have successfully disconnected ending the demo, move to next mode */ 00378 _event_queue.call(this, &GAPDevice::demo_mode_end); 00379 }; 00380 00381 /** called if timeout is reached during advertising, scanning 00382 * or connection initiation */ 00383 void on_timeout(const Gap::TimeoutSource_t source) 00384 { 00385 _demo_duration.stop(); 00386 00387 switch (source) { 00388 case Gap::TIMEOUT_SRC_ADVERTISING: 00389 printf("Stopped advertising early due to timeout parameter\r\n"); 00390 break; 00391 case Gap::TIMEOUT_SRC_SCAN: 00392 printf("Stopped scanning early due to timeout parameter\r\n"); 00393 break; 00394 case Gap::TIMEOUT_SRC_CONN: 00395 printf("Failed to connect after scanning %d advertisements\r\n", _scan_count); 00396 _event_queue.call(this, &GAPDevice::print_performance); 00397 _event_queue.call(this, &GAPDevice::demo_mode_end); 00398 break; 00399 default: 00400 printf("Unexpected timeout\r\n"); 00401 break; 00402 } 00403 }; 00404 00405 /** clean up after last run, cycle to the next mode and launch it */ 00406 void demo_mode_end() 00407 { 00408 /* reset the demo ready for the next mode */ 00409 _scan_count = 0; 00410 _demo_duration.stop(); 00411 _demo_duration.reset(); 00412 00413 /* cycle through all demo modes */ 00414 _set_index++; 00415 00416 /* switch between advertising and scanning when we go 00417 * through all the params in the array */ 00418 if (_set_index >= (_is_in_scanning_mode? SCAN_PARAM_SET_MAX : ADV_PARAM_SET_MAX)) { 00419 _set_index = 0; 00420 _is_in_scanning_mode = !_is_in_scanning_mode; 00421 } 00422 00423 _ble.shutdown(); 00424 _event_queue.break_dispatch(); 00425 }; 00426 00427 /** print some information about our radio activity */ 00428 void print_performance() 00429 { 00430 /* measure time from mode start, may have been stopped by timeout */ 00431 uint16_t duration = _demo_duration.read_ms(); 00432 00433 if (_is_in_scanning_mode) { 00434 /* convert ms into timeslots for accurate calculation as internally 00435 * all durations are in timeslots (0.625ms) */ 00436 uint16_t interval_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS( 00437 scanning_params[_set_index].interval 00438 ); 00439 uint16_t window_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS( 00440 scanning_params[_set_index].window 00441 ); 00442 uint16_t duration_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS( 00443 duration 00444 ); 00445 /* this is how long we scanned for in timeslots */ 00446 uint16_t rx_ts = (duration_ts / interval_ts) * window_ts; 00447 /* convert to milliseconds */ 00448 uint16_t rx_ms = (rx_ts * GapScanningParams::UNIT_0_625_MS) / 1000; 00449 00450 printf("We have scanned for %dms with an interval of %d" 00451 " timeslot and a window of %d timeslots\r\n", 00452 duration, interval_ts, window_ts); 00453 00454 printf("We have been listening on the radio for at least %dms\r\n", rx_ms); 00455 00456 } else /* advertising */ { 00457 00458 /* convert ms into timeslots for accurate calculation as internally 00459 * all durations are in timeslots (0.625ms) */ 00460 uint16_t interval_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS( 00461 advertising_params[_set_index].interval 00462 ); 00463 uint16_t duration_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS( 00464 duration 00465 ); 00466 /* this is how many times we advertised */ 00467 uint16_t events = duration_ts / interval_ts; 00468 00469 printf("We have advertised for %dms" 00470 " with an interval of %d timeslots\r\n", 00471 duration, interval_ts); 00472 00473 /* non-scannable and non-connectable advertising 00474 * skips rx events saving on power consumption */ 00475 if (advertising_params[_set_index].adv_type 00476 == GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED) { 00477 printf("We created at least %d tx events\r\n", events); 00478 } else { 00479 printf("We created at least %d tx and rx events\r\n", events); 00480 } 00481 } 00482 }; 00483 00484 /** Schedule processing of events from the BLE middleware in the event queue. */ 00485 void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) 00486 { 00487 _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents)); 00488 }; 00489 00490 /** Blink LED to show we're running */ 00491 void blink(void) 00492 { 00493 _led1 = !_led1; 00494 }; 00495 00496 private: 00497 BLE &_ble; 00498 events::EventQueue _event_queue; 00499 DigitalOut _led1; 00500 00501 /* Keep track of our progress through demo modes */ 00502 size_t _set_index; 00503 bool _is_in_scanning_mode; 00504 bool _is_connecting; 00505 00506 /* Remember the call id of the function on _event_queue 00507 * so we can cancel it if we need to end the mode early */ 00508 int _on_duration_end_id; 00509 00510 /* Measure performance of our advertising/scanning */ 00511 Timer _demo_duration; 00512 size_t _scan_count; 00513 }; 00514 00515 int main() 00516 { 00517 GAPDevice gap_device; 00518 00519 while (1) { 00520 gap_device.run(); 00521 wait_ms(TIME_BETWEEN_MODES_MS); 00522 printf("\r\nStarting next GAP demo mode\r\n"); 00523 }; 00524 00525 return 0; 00526 }
Generated on Tue Jul 12 2022 18:49:42 by
1.7.2