Demonstration of the GAP profile. It shows advertising, scanning and connecting. The demo will cycle through several modes and print over the serial connection information about current activity.

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers <title>main.cpp Source File</title>

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 }