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.
main.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2018 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 #include "gap/Gap.h" 00021 #include "gap/AdvertisingDataParser.h" 00022 #include "pretty_printer.h" 00023 00024 /** This example demonstrates all the basic setup required 00025 * to advertise, scan and connect to other devices. 00026 * 00027 * It contains a single class that performs both scans and advertisements. 00028 * 00029 * The demonstrations happens in sequence, after each "mode" ends 00030 * the demo jumps to the next mode to continue. There are several modes 00031 * that show scanning and several showing advertising. These are configured 00032 * according to the two arrays containing parameters. During scanning 00033 * a connection will be made to a connectable device upon its discovery. 00034 */ 00035 00036 events::EventQueue event_queue; 00037 00038 /* Duration of each mode in milliseconds */ 00039 static const size_t MODE_DURATION_MS = 6000; 00040 00041 /* Time between each mode in milliseconds */ 00042 static const size_t TIME_BETWEEN_MODES_MS = 2000; 00043 00044 /* how long to wait before disconnecting in milliseconds */ 00045 static const size_t CONNECTION_DURATION = 3000; 00046 00047 /* how many advertising sets we want to crate at once */ 00048 static const uint8_t ADV_SET_NUMBER = 2; 00049 00050 static const uint16_t MAX_ADVERTISING_PAYLOAD_SIZE = 1000; 00051 00052 typedef struct { 00053 ble::advertising_type_t type; 00054 ble::adv_interval_t min_interval; 00055 ble::adv_interval_t max_interval; 00056 } DemoAdvParams_t; 00057 00058 typedef struct { 00059 ble::scan_interval_t interval; 00060 ble::scan_window_t window; 00061 ble::scan_duration_t duration; 00062 bool active; 00063 } DemoScanParam_t; 00064 00065 /** the entries in this array are used to configure our advertising 00066 * parameters for each of the modes we use in our demo */ 00067 static const DemoAdvParams_t advertising_params[] = { 00068 /* advertising type | min interval - 0.625us | max interval - 0.625us */ 00069 { ble::advertising_type_t::CONNECTABLE_UNDIRECTED, ble::adv_interval_t(40), ble::adv_interval_t(80) }, 00070 { ble::advertising_type_t::SCANNABLE_UNDIRECTED, ble::adv_interval_t(100), ble::adv_interval_t(200) }, 00071 { ble::advertising_type_t::NON_CONNECTABLE_UNDIRECTED, ble::adv_interval_t(100), ble::adv_interval_t(200) } 00072 }; 00073 00074 /* when we cycle through all our advertising modes we will move to scanning modes */ 00075 00076 /** the entries in this array are used to configure our scanning 00077 * parameters for each of the modes we use in our demo */ 00078 static const DemoScanParam_t scanning_params[] = { 00079 /* interval window duration active */ 00080 /* 0.625ms 0.625ms 10ms */ 00081 { ble::scan_interval_t(4), ble::scan_window_t(4), ble::scan_duration_t(0), false }, 00082 { ble::scan_interval_t(160), ble::scan_window_t(100), ble::scan_duration_t(300), false }, 00083 { ble::scan_interval_t(160), ble::scan_window_t(40), ble::scan_duration_t(0), true }, 00084 { ble::scan_interval_t(500), ble::scan_window_t(10), ble::scan_duration_t(0), false } 00085 }; 00086 00087 /* helper that gets the number of items in arrays */ 00088 template<class T, size_t N> 00089 size_t arraysize(const T (&)[N]) 00090 { 00091 return N; 00092 } 00093 00094 /** Demonstrate advertising, scanning and connecting 00095 */ 00096 class GapDemo : private mbed::NonCopyable<GapDemo>, public ble::Gap::EventHandler 00097 { 00098 public: 00099 GapDemo(BLE& ble, events::EventQueue& event_queue) : 00100 _ble(ble), 00101 _gap(ble.gap()), 00102 _event_queue(event_queue), 00103 _led1(LED1, 0), 00104 _set_index(0), 00105 _is_in_scanning_mode(true), 00106 _is_connecting(false), 00107 _on_duration_end_id(0), 00108 _scan_count(0), 00109 _blink_event(0) { 00110 for (uint8_t i = 0; i < arraysize(_adv_handles); ++i) { 00111 _adv_handles[i] = ble::INVALID_ADVERTISING_HANDLE; 00112 } 00113 } 00114 00115 ~GapDemo() 00116 { 00117 if (_ble.hasInitialized()) { 00118 _ble.shutdown(); 00119 } 00120 } 00121 00122 /** Start BLE interface initialisation */ 00123 void run() 00124 { 00125 if (_ble.hasInitialized()) { 00126 printf("Ble instance already initialised.\r\n"); 00127 return; 00128 } 00129 00130 /* handle gap events */ 00131 _gap.setEventHandler(this); 00132 00133 ble_error_t error = _ble.init(this, &GapDemo::on_init_complete); 00134 if (error) { 00135 print_error(error, "Error returned by BLE::init"); 00136 return; 00137 } 00138 00139 /* to show we're running we'll blink every 500ms */ 00140 _blink_event = _event_queue.call_every(500, this, &GapDemo::blink); 00141 00142 /* this will not return until shutdown */ 00143 _event_queue.dispatch_forever(); 00144 } 00145 00146 private: 00147 /** This is called when BLE interface is initialised and starts the first mode */ 00148 void on_init_complete(BLE::InitializationCompleteCallbackContext *event) 00149 { 00150 if (event->error) { 00151 print_error(event->error, "Error during the initialisation"); 00152 return; 00153 } 00154 00155 print_mac_address(); 00156 00157 /* setup the default phy used in connection to 2M to reduce power consumption */ 00158 if (is_2m_phy_supported()) { 00159 ble::phy_set_t phys(/* 1M */ false, /* 2M */ true, /* coded */ false); 00160 00161 ble_error_t error = _gap.setPreferredPhys(/* tx */&phys, /* rx */&phys); 00162 if (error) { 00163 print_error(error, "GAP::setPreferedPhys failed"); 00164 } 00165 } 00166 00167 /* all calls are serialised on the user thread through the event queue */ 00168 _event_queue.call(this, &GapDemo::demo_mode_start); 00169 } 00170 00171 /** queue up start of the current demo mode */ 00172 void demo_mode_start() 00173 { 00174 if (_is_in_scanning_mode) { 00175 _event_queue.call(this, &GapDemo::scan); 00176 } else { 00177 _event_queue.call(this, &GapDemo::advertise); 00178 } 00179 00180 /* for performance measurement keep track of duration of the demo mode */ 00181 _demo_duration.start(); 00182 /* keep track of our state */ 00183 _is_connecting = false; 00184 00185 /* queue up next demo mode */ 00186 _on_duration_end_id = _event_queue.call_in( 00187 MODE_DURATION_MS, 00188 this, 00189 &GapDemo::end_demo_mode 00190 ); 00191 00192 printf("\r\n"); 00193 } 00194 00195 /** Set up and start advertising */ 00196 void advertise() 00197 { 00198 const DemoAdvParams_t &adv_params = advertising_params[_set_index]; 00199 00200 /* 00201 * Advertising parameters are mainly defined by an advertising type and 00202 * and an interval between advertisements. lower interval increases the 00203 * chances of being seen at the cost of more power. 00204 * The Bluetooth controller may run concurrent operations with the radio; 00205 * to help it, a minimum and maximum advertising interval should be 00206 * provided. 00207 * 00208 * With Bluetooth 5; it is possible to advertise concurrently multiple 00209 * payloads at different rate. The combination of payload and its associated 00210 * parameters is named an advertising set. To refer to these advertising 00211 * sets the Bluetooth system use an advertising set handle that needs to 00212 * be created first. 00213 * The only exception is the legacy advertising handle which is usable 00214 * on Bluetooth 4 and Bluetooth 5 system. It is created at startup and 00215 * its lifecycle is managed by the system. 00216 */ 00217 ble_error_t error = _gap.setAdvertisingParameters( 00218 ble::LEGACY_ADVERTISING_HANDLE, 00219 ble::AdvertisingParameters( 00220 adv_params.type, 00221 adv_params.min_interval, 00222 adv_params.max_interval 00223 ) 00224 ); 00225 if (error) { 00226 print_error(error, "Gap::setAdvertisingParameters() failed"); 00227 return; 00228 } 00229 00230 /* Set payload for the set */ 00231 /* Use the simple builder to construct the payload; it fails at runtime 00232 * if there is not enough space left in the buffer */ 00233 error = _gap.setAdvertisingPayload( 00234 ble::LEGACY_ADVERTISING_HANDLE, 00235 ble::AdvertisingDataSimpleBuilder<ble::LEGACY_ADVERTISING_MAX_SIZE>() 00236 .setFlags() 00237 .setName("Legacy advertiser") 00238 .getAdvertisingData() 00239 ); 00240 if (error) { 00241 print_error(error, "Gap::setAdvertisingPayload() failed"); 00242 return; 00243 } 00244 00245 /* Start advertising the set */ 00246 error = _gap.startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); 00247 if (error) { 00248 print_error(error, "Gap::startAdvertising() failed"); 00249 return; 00250 } 00251 00252 printf("Advertising started (type: 0x%x, interval: [%d : %d]ms)\r\n", 00253 adv_params.type.value(), 00254 adv_params.min_interval.valueInMs(), adv_params.max_interval.valueInMs() ); 00255 00256 if (is_extended_advertising_supported()) { 00257 advertise_extended(); 00258 } 00259 } 00260 00261 void advertise_extended() 00262 { 00263 const DemoAdvParams_t &adv_params = advertising_params[_set_index]; 00264 00265 /* this is the memory backing for the payload */ 00266 uint8_t adv_buffer[MAX_ADVERTISING_PAYLOAD_SIZE]; 00267 00268 /* how many sets */ 00269 uint8_t max_adv_set = std::min( 00270 _gap.getMaxAdvertisingSetNumber(), 00271 (uint8_t) arraysize(_adv_handles) 00272 ); 00273 00274 /* one advertising set is reserved for legacy advertising */ 00275 if (max_adv_set < 2) { 00276 return; 00277 } 00278 00279 /* how much payload in a set */ 00280 uint16_t max_adv_size = std::min( 00281 (uint16_t) _gap.getMaxAdvertisingDataLength(), 00282 MAX_ADVERTISING_PAYLOAD_SIZE 00283 ); 00284 00285 /* create and start all requested (and possible) advertising sets */ 00286 for (uint8_t i = 0; i < (max_adv_set - 1); ++i) { 00287 /* create the advertising set with its parameter */ 00288 /* this time we do not use legacy PDUs */ 00289 ble_error_t error = _gap.createAdvertisingSet( 00290 &_adv_handles[i], 00291 ble::AdvertisingParameters( 00292 adv_params.type, 00293 adv_params.min_interval, 00294 adv_params.max_interval 00295 ).setUseLegacyPDU(false) 00296 ); 00297 if (error) { 00298 print_error(error, "Gap::createAdvertisingSet() failed"); 00299 return; 00300 } 00301 00302 /* use the helper to build the payload */ 00303 ble::AdvertisingDataBuilder adv_data_builder( 00304 adv_buffer, 00305 max_adv_size 00306 ); 00307 00308 /* set the flags */ 00309 error = adv_data_builder.setFlags(); 00310 if (error) { 00311 print_error(error, "AdvertisingDataBuilder::setFlags() failed"); 00312 return; 00313 } 00314 00315 /* set different name for each set */ 00316 MBED_ASSERT(i < 9); 00317 char device_name[] = "Advertiser x"; 00318 snprintf(device_name, arraysize(device_name), "Advertiser %d", i%10); 00319 error = adv_data_builder.setName(device_name); 00320 if (error) { 00321 print_error(error, "AdvertisingDataBuilder::setName() failed"); 00322 return; 00323 } 00324 00325 /* Set payload for the set */ 00326 error = _gap.setAdvertisingPayload( 00327 _adv_handles[i], 00328 adv_data_builder.getAdvertisingData() 00329 ); 00330 if (error) { 00331 print_error(error, "Gap::setAdvertisingPayload() failed"); 00332 return; 00333 } 00334 00335 /* Start advertising the set */ 00336 error = _gap.startAdvertising(_adv_handles[i]); 00337 if (error) { 00338 print_error(error, "Gap::startAdvertising() failed"); 00339 return; 00340 } 00341 00342 printf("Advertising started (type: 0x%x, interval: [%d : %d]ms)\r\n", 00343 adv_params.type.value(), 00344 adv_params.min_interval.valueInMs(), adv_params.max_interval.valueInMs() ); 00345 } 00346 } 00347 00348 /** Set up and start scanning */ 00349 void scan() 00350 { 00351 const DemoScanParam_t &scan_params = scanning_params[_set_index]; 00352 00353 /* 00354 * Scanning happens repeatedly and is defined by: 00355 * - The scan interval which is the time (in 0.625us) between each scan cycle. 00356 * - The scan window which is the scanning time (in 0.625us) during a cycle. 00357 * If the scanning process is active, the local device sends scan requests 00358 * to discovered peer to get additional data. 00359 */ 00360 ble_error_t error = _gap.setScanParameters( 00361 ble::ScanParameters( 00362 ble::phy_t::LE_1M, // scan on the 1M PHY 00363 scan_params.interval, 00364 scan_params.window, 00365 scan_params.active 00366 ) 00367 ); 00368 if (error) { 00369 print_error(error, "Error caused by Gap::setScanParameters"); 00370 return; 00371 } 00372 00373 /* start scanning and attach a callback that will handle advertisements 00374 * and scan requests responses */ 00375 error = _gap.startScan(scan_params.duration); 00376 if (error) { 00377 print_error(error, "Error caused by Gap::startScan"); 00378 return; 00379 } 00380 00381 printf("Scanning started (interval: %dms, window: %dms, timeout: %dms).\r\n", 00382 scan_params.interval.valueInMs(), scan_params.window.valueInMs(), scan_params.duration.valueInMs()); 00383 } 00384 00385 /** Finish the mode by shutting down advertising or scanning and move to the next mode. */ 00386 void end_demo_mode() 00387 { 00388 if (_is_in_scanning_mode) { 00389 end_scanning_mode(); 00390 } else { 00391 end_advertising_mode(); 00392 } 00393 00394 /* alloted time has elapsed or be connected, move to next demo mode */ 00395 _event_queue.call(this, &GapDemo::next_demo_mode); 00396 } 00397 00398 /** Execute the disconnection */ 00399 void do_disconnect(ble::connection_handle_t handle) 00400 { 00401 printf("Disconnecting\r\n"); 00402 _gap.disconnect(handle, ble::local_disconnection_reason_t::USER_TERMINATION); 00403 } 00404 00405 bool is_2m_phy_supported() 00406 { 00407 return _gap.isFeatureSupported(ble::controller_supported_features_t::LE_2M_PHY); 00408 } 00409 00410 bool is_extended_advertising_supported() 00411 { 00412 return _gap.isFeatureSupported(ble::controller_supported_features_t::LE_EXTENDED_ADVERTISING); 00413 } 00414 00415 00416 private: 00417 /* Gap::EventHandler */ 00418 00419 /** Look at scan payload to find a peer device and connect to it */ 00420 virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event) 00421 { 00422 /* keep track of scan events for performance reporting */ 00423 _scan_count++; 00424 00425 /* don't bother with analysing scan result if we're already connecting */ 00426 if (_is_connecting) { 00427 return; 00428 } 00429 00430 /* only look at events from devices at a close range */ 00431 if (event.getRssi() < -65) { 00432 return; 00433 } 00434 00435 ble::AdvertisingDataParser adv_parser(event.getPayload()); 00436 00437 /* parse the advertising payload, looking for a discoverable device */ 00438 while (adv_parser.hasNext()) { 00439 ble::AdvertisingDataParser::element_t field = adv_parser.next(); 00440 00441 /* skip non discoverable device */ 00442 if (field.type != ble::adv_data_type_t::FLAGS || 00443 field.value.size() != 1 || 00444 !(field.value[0] & GapAdvertisingData::LE_GENERAL_DISCOVERABLE)) { 00445 continue; 00446 } 00447 00448 /* connect to a discoverable device */ 00449 00450 /* abort timeout as the mode will end on disconnection */ 00451 _event_queue.cancel(_on_duration_end_id); 00452 00453 printf("We found a connectable device\r\n"); 00454 ble_error_t error = _gap.connect( 00455 event.getPeerAddressType(), 00456 event.getPeerAddress(), 00457 ble::ConnectionParameters() // use the default connection parameters 00458 ); 00459 if (error) { 00460 print_error(error, "Error caused by Gap::connect"); 00461 /* since no connection will be attempted end the mode */ 00462 _event_queue.call(this, &GapDemo::end_demo_mode); 00463 return; 00464 } 00465 00466 /* we may have already scan events waiting 00467 * to be processed so we need to remember 00468 * that we are already connecting and ignore them */ 00469 _is_connecting = true; 00470 return; 00471 } 00472 } 00473 00474 virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &event) 00475 { 00476 if (event.isConnected()) { 00477 printf("Stopped advertising early due to connection\r\n"); 00478 } 00479 } 00480 00481 virtual void onScanTimeout(const ble::ScanTimeoutEvent&) 00482 { 00483 printf("Stopped scanning early due to timeout parameter\r\n"); 00484 _demo_duration.stop(); 00485 } 00486 00487 /** This is called by Gap to notify the application we connected, 00488 * in our case it immediately disconnects */ 00489 virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) 00490 { 00491 _demo_duration.stop(); 00492 00493 if (event.getStatus() == BLE_ERROR_NONE) { 00494 printf("Connected in %dms\r\n", _demo_duration.read_ms()); 00495 00496 /* cancel the connect timeout since we connected */ 00497 _event_queue.cancel(_on_duration_end_id); 00498 00499 _event_queue.call_in( 00500 CONNECTION_DURATION, 00501 this, 00502 &GapDemo::do_disconnect, 00503 event.getConnectionHandle() 00504 ); 00505 } else { 00506 printf("Failed to connect after scanning %d advertisements\r\n", _scan_count); 00507 _event_queue.call(this, &GapDemo::end_demo_mode); 00508 } 00509 } 00510 00511 /** This is called by Gap to notify the application we disconnected, 00512 * in our case it calls next_demo_mode() to progress the demo */ 00513 virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event) 00514 { 00515 printf("Disconnected\r\n"); 00516 00517 /* we have successfully disconnected ending the demo, move to next mode */ 00518 _event_queue.call(this, &GapDemo::end_demo_mode); 00519 } 00520 00521 /** 00522 * Implementation of Gap::EventHandler::onReadPhy 00523 */ 00524 virtual void onReadPhy( 00525 ble_error_t error, 00526 ble::connection_handle_t connectionHandle, 00527 ble::phy_t txPhy, 00528 ble::phy_t rxPhy 00529 ) { 00530 if (error) { 00531 printf( 00532 "Phy read on connection %d failed with error code %s\r\n", 00533 connectionHandle, 00534 BLE::errorToString(error) 00535 ); 00536 } else { 00537 printf( 00538 "Phy read on connection %d - Tx Phy: %s, Rx Phy: %s\r\n", 00539 connectionHandle, 00540 phy_to_string(txPhy), 00541 phy_to_string(rxPhy) 00542 ); 00543 } 00544 } 00545 00546 /** 00547 * Implementation of Gap::EventHandler::onPhyUpdateComplete 00548 */ 00549 virtual void onPhyUpdateComplete( 00550 ble_error_t error, 00551 ble::connection_handle_t connectionHandle, 00552 ble::phy_t txPhy, 00553 ble::phy_t rxPhy 00554 ) { 00555 if (error) { 00556 printf( 00557 "Phy update on connection: %d failed with error code %s\r\n", 00558 connectionHandle, 00559 BLE::errorToString(error) 00560 ); 00561 } else { 00562 printf( 00563 "Phy update on connection %d - Tx Phy: %s, Rx Phy: %s\r\n", 00564 connectionHandle, 00565 phy_to_string(txPhy), 00566 phy_to_string(rxPhy) 00567 ); 00568 } 00569 } 00570 00571 /** 00572 * Implementation of Gap::EventHandler::onDataLengthChange 00573 */ 00574 virtual void onDataLengthChange( 00575 ble::connection_handle_t connectionHandle, 00576 uint16_t txSize, 00577 uint16_t rxSize 00578 ) { 00579 printf( 00580 "Data length changed on the connection %d.\r\n" 00581 "Maximum sizes for over the air packets are:\r\n" 00582 "%d octets for transmit and %d octets for receive.\r\n", 00583 connectionHandle, 00584 txSize, 00585 rxSize 00586 ); 00587 } 00588 00589 private: 00590 00591 /** Clean up internal state after last run, cycle to the next mode and launch it */ 00592 void next_demo_mode() 00593 { 00594 /* reset the demo ready for the next mode */ 00595 _scan_count = 0; 00596 _demo_duration.stop(); 00597 _demo_duration.reset(); 00598 00599 /* cycle through all demo modes */ 00600 _set_index++; 00601 00602 /* switch between advertising and scanning when we go 00603 * through all the params in the array */ 00604 if (_set_index >= (_is_in_scanning_mode ? arraysize(scanning_params) : arraysize(advertising_params))) { 00605 _set_index = 0; 00606 _is_in_scanning_mode = !_is_in_scanning_mode; 00607 } 00608 00609 _ble.shutdown(); 00610 _event_queue.cancel(_blink_event); 00611 _event_queue.break_dispatch(); 00612 } 00613 00614 /** Finish the mode by shutting down advertising or scanning and move to the next mode. */ 00615 void end_scanning_mode() 00616 { 00617 print_scanning_performance(); 00618 ble_error_t error = _gap.stopScan(); 00619 00620 if (error) { 00621 print_error(error, "Error caused by Gap::stopScan"); 00622 } 00623 } 00624 00625 void end_advertising_mode() 00626 { 00627 print_advertising_performance(); 00628 00629 _gap.stopAdvertising(ble::LEGACY_ADVERTISING_HANDLE); 00630 00631 if (is_extended_advertising_supported()) { 00632 end_extended_advertising(); 00633 } 00634 } 00635 00636 void end_extended_advertising() 00637 { 00638 /* iterate over the advertising handles */ 00639 for (uint8_t i = 0; i < arraysize(_adv_handles); ++i) { 00640 /* check if the set has been sucesfully created */ 00641 if (_adv_handles[i] == ble::INVALID_ADVERTISING_HANDLE) { 00642 continue; 00643 } 00644 00645 /* if it's still active, stop it */ 00646 if (_gap.isAdvertisingActive(_adv_handles[i])) { 00647 ble_error_t error = _gap.stopAdvertising(_adv_handles[i]); 00648 if (error) { 00649 print_error(error, "Error caused by Gap::stopAdvertising"); 00650 continue; 00651 } 00652 } 00653 00654 ble_error_t error = _gap.destroyAdvertisingSet(_adv_handles[i]); 00655 if (error) { 00656 print_error(error, "Error caused by Gap::destroyAdvertisingSet"); 00657 continue; 00658 } 00659 00660 _adv_handles[i] = ble::INVALID_ADVERTISING_HANDLE; 00661 } 00662 } 00663 00664 /** print some information about our radio activity */ 00665 void print_scanning_performance() 00666 { 00667 /* measure time from mode start, may have been stopped by timeout */ 00668 uint16_t duration_ms = _demo_duration.read_ms(); 00669 00670 /* convert ms into timeslots for accurate calculation as internally 00671 * all durations are in timeslots (0.625ms) */ 00672 uint16_t duration_ts = ble::scan_interval_t(ble::millisecond_t(duration_ms)).value(); 00673 uint16_t interval_ts = scanning_params[_set_index].interval.value(); 00674 uint16_t window_ts = scanning_params[_set_index].window.value(); 00675 /* this is how long we scanned for in timeslots */ 00676 uint16_t rx_ts = (duration_ts / interval_ts) * window_ts; 00677 /* convert to milliseconds */ 00678 uint16_t rx_ms = ble::scan_interval_t(rx_ts).valueInMs(); 00679 00680 printf("We have scanned for %dms with an interval of %d" 00681 " timeslots and a window of %d timeslots\r\n", 00682 duration_ms, interval_ts, window_ts); 00683 00684 printf("We have been listening on the radio for at least %dms\r\n", rx_ms); 00685 } 00686 00687 /** print some information about our radio activity */ 00688 void print_advertising_performance() 00689 { 00690 /* measure time from mode start, may have been stopped by timeout */ 00691 uint16_t duration_ms = _demo_duration.read_ms(); 00692 uint8_t number_of_active_sets = 0; 00693 00694 for (uint8_t i = 0; i < arraysize(_adv_handles); ++i) { 00695 if (_adv_handles[i] != ble::INVALID_ADVERTISING_HANDLE) { 00696 if (_gap.isAdvertisingActive(_adv_handles[i])) { 00697 number_of_active_sets++; 00698 } 00699 } 00700 } 00701 00702 /* convert ms into timeslots for accurate calculation as internally 00703 * all durations are in timeslots (0.625ms) */ 00704 uint16_t duration_ts = ble::adv_interval_t(ble::millisecond_t(duration_ms)).value(); 00705 uint16_t interval_ts = advertising_params[_set_index].max_interval.value(); 00706 /* this is how many times we advertised */ 00707 uint16_t events = (duration_ts / interval_ts) * number_of_active_sets; 00708 00709 printf("We have advertised for %dms with an interval of at least %d timeslots\r\n", 00710 duration_ms, interval_ts); 00711 00712 if (number_of_active_sets > 1) { 00713 printf("We had %d active advertising sets\r\n", number_of_active_sets); 00714 } 00715 00716 /* non-scannable and non-connectable advertising 00717 * skips rx events saving on power consumption */ 00718 if (advertising_params[_set_index].type == ble::advertising_type_t::NON_CONNECTABLE_UNDIRECTED) { 00719 printf("We created at least %d tx events\r\n", events); 00720 } else { 00721 printf("We created at least %d tx and rx events\r\n", events); 00722 } 00723 } 00724 00725 /** Blink LED to show we're running */ 00726 void blink(void) 00727 { 00728 _led1 = !_led1; 00729 } 00730 00731 private: 00732 BLE &_ble; 00733 ble::Gap &_gap; 00734 events::EventQueue &_event_queue; 00735 DigitalOut _led1; 00736 00737 /* Keep track of our progress through demo modes */ 00738 size_t _set_index; 00739 bool _is_in_scanning_mode; 00740 bool _is_connecting; 00741 00742 /* Remember the call id of the function on _event_queue 00743 * so we can cancel it if we need to end the mode early */ 00744 int _on_duration_end_id; 00745 00746 /* Measure performance of our advertising/scanning */ 00747 Timer _demo_duration; 00748 size_t _scan_count; 00749 00750 int _blink_event; 00751 00752 ble::advertising_handle_t _adv_handles[ADV_SET_NUMBER]; 00753 }; 00754 00755 /** Schedule processing of events from the BLE middleware in the event queue. */ 00756 void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) { 00757 event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents)); 00758 } 00759 00760 int main() 00761 { 00762 BLE &ble = BLE::Instance(); 00763 00764 /* this will inform us off all events so we can schedule their handling 00765 * using our event queue */ 00766 ble.onEventsToProcess(schedule_ble_events); 00767 00768 GapDemo demo(ble, event_queue); 00769 00770 while (1) { 00771 demo.run(); 00772 wait_ms(TIME_BETWEEN_MODES_MS); 00773 printf("\r\nStarting next GAP demo mode\r\n"); 00774 }; 00775 00776 return 0; 00777 }
Generated on Wed Jul 13 2022 03:17:06 by
1.7.2