mbed-os-examples / Mbed OS mbed-os-example-ble-Privacy
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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 #include "SecurityManager.h"
00021 #include <algorithm>
00022 #include "pretty_printer.h"
00023 #include "ble/gap/AdvertisingDataParser.h"
00024 
00025 /** This example demonstrates privacy features in Gap. It shows how to use
00026  *  private addresses when advertising and connecting and how filtering ties
00027  *  in with these operations.
00028  *
00029  *  The application will start by repeatedly trying to connect to the same
00030  *  application running on another board. It will do this by advertising and
00031  *  scanning for random intervals waiting until the difference in intervals
00032  *  between the boards will make them meet when one is advertising and the
00033  *  other scanning.
00034  *
00035  *  Two devices will be operating using random resolvable addresses. The
00036  *  applications will connect to the peer and pair. It will attempt bonding
00037  *  to store the IRK that resolve the peer. Subsequent connections will
00038  *  turn on filtering based on stored IRKs.
00039  */
00040 
00041 static const char DEVICE_NAME[] = "Privacy";
00042 
00043 /* we have to specify the disconnect call because of ambiguous overloads */
00044 typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t);
00045 const static disconnect_call_t disconnect_call = &Gap::disconnect;
00046 
00047 /** Base class for both peripheral and central. The same class that provides
00048  *  the logic for the application also implements the SecurityManagerEventHandler
00049  *  which is the interface used by the Security Manager to communicate events
00050  *  back to the applications. You can provide overrides for a selection of events
00051  *  your application is interested in.
00052  */
00053 class PrivacyDevice : private mbed::NonCopyable<PrivacyDevice>,
00054                       public SecurityManager::EventHandler,
00055                       public ble::Gap::EventHandler
00056 {
00057 public:
00058     PrivacyDevice(BLE &ble, events::EventQueue &event_queue) :
00059         _ble(ble),
00060         _event_queue(event_queue),
00061         _handle(0),
00062         _bonded(false),
00063         _led1(LED1, 0) { };
00064 
00065     virtual ~PrivacyDevice() {
00066         _ble.onEventsToProcess(NULL);
00067     };
00068 
00069     /** Start BLE interface initialisation */
00070     void run()
00071     {
00072         /* to show we're running we'll blink every 500ms */
00073         _event_queue.call_every(500, this, &PrivacyDevice::blink);
00074 
00075         /* this will inform us off all events so we can schedule their handling
00076          * using our event queue */
00077         _ble.onEventsToProcess(
00078             makeFunctionPointer(this, &PrivacyDevice::schedule_ble_events)
00079         );
00080 
00081         /* handle gap events */
00082         _ble.gap().setEventHandler(this);
00083 
00084         if (_ble.hasInitialized()) {
00085             /* ble instance already initialised, skip init and start activity */
00086             start();
00087         } else {
00088             ble_error_t error = _ble.init(this, &PrivacyDevice::on_init_complete);
00089 
00090             if (error) {
00091                 printf("Error returned by BLE::init.\r\n");
00092                 return;
00093             }
00094         }
00095 
00096         /* this will not return until shutdown */
00097         _event_queue.dispatch_forever();
00098     };
00099 
00100     /** Override to start chosen activity when initialisation completes */
00101     virtual void start() = 0;
00102 
00103     /** Override to start chosen activity after initial bonding */
00104     virtual void start_after_bonding() = 0;
00105 
00106     /* callbacks */
00107 
00108     /** This is called when BLE interface is initialised and starts the demonstration */
00109     void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
00110     {
00111         ble_error_t error;
00112 
00113         if (event->error) {
00114             printf("Error during the initialisation\r\n");
00115             _event_queue.break_dispatch();
00116             return;
00117         }
00118 
00119         /* for use by tools we print out own address and also use it
00120          * to seed RNG as the address is unique */
00121         print_local_address();
00122 
00123         /* Privacy requires the security manager */
00124 
00125         error = _ble.securityManager().init(
00126             /* enableBonding */ true,
00127             /* requireMITM */ false,
00128             /* iocaps */ SecurityManager::IO_CAPS_NONE,
00129             /* passkey */ NULL,
00130             /* signing */ false,
00131             /* dbFilepath */ NULL
00132         );
00133 
00134         if (error) {
00135             printf("Error during security manager initialisation\r\n");
00136             _event_queue.break_dispatch();
00137             return;
00138         }
00139 
00140         /* Tell the security manager to use methods in this class to inform us
00141          * of any events. Class needs to implement SecurityManagerEventHandler. */
00142         _ble.securityManager().setSecurityManagerEventHandler(this);
00143 
00144         /* gap events also handled by this class */
00145         _ble.gap().setEventHandler(this);
00146 
00147         /* privacy */
00148 
00149         error = _ble.gap().enablePrivacy(true);
00150 
00151         if (error) {
00152             printf("Error enabling privacy.\r\n");
00153             _event_queue.break_dispatch();
00154             return;
00155         }
00156 
00157         start();
00158     };
00159 
00160     /** Schedule processing of events from the BLE in the event queue. */
00161     void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
00162     {
00163         _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents));
00164     };
00165 
00166     /** Blink LED to show we're running */
00167     void blink(void)
00168     {
00169         _led1 = !_led1;
00170     };
00171 
00172     void print_local_address()
00173     {
00174         /* show what address we are using now */
00175         Gap::AddressType_t addr_type;
00176         Gap::Address_t addr;
00177         _ble.gap().getAddress(&addr_type, addr);
00178         printf("Device address: ");
00179         print_address(addr);
00180 
00181         if (!_seeded) {
00182             _seeded = true;
00183             /* use the address as a seed */
00184             uint8_t* random_data = addr;
00185             srand(*((unsigned int*)random_data));
00186         }
00187     }
00188 
00189 private:
00190     /* Event handler */
00191 
00192     /** Inform the application of pairing */
00193     virtual void pairingResult(
00194         ble::connection_handle_t connectionHandle,
00195         SecurityManager::SecurityCompletionStatus_t result
00196     ) {
00197         if (result == SecurityManager::SEC_STATUS_SUCCESS) {
00198             printf("Pairing successful\r\n");
00199             _bonded = true;
00200         } else {
00201             printf("Pairing failed\r\n");
00202         }
00203 
00204         /* disconnect in 2s */
00205         _event_queue.call_in(
00206             2000,
00207             &_ble.gap(),
00208             disconnect_call,
00209             connectionHandle,
00210             ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
00211         );
00212     }
00213 
00214     /** This is called by Gap to notify the application we connected */
00215     virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
00216     {
00217         printf("Connected to peer: ");
00218         print_address(event.getPeerAddress().data());
00219         printf("Peer random resolvable address: ");
00220         print_address(event.getPeerResolvablePrivateAddress().data());
00221 
00222         _handle = event.getConnectionHandle();
00223 
00224         if (_bonded) {
00225             /* disconnect in 2s */
00226             _event_queue.call_in(
00227                 2000,
00228                 &_ble.gap(),
00229                 disconnect_call,
00230                 _handle,
00231                 ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
00232             );
00233         }
00234     };
00235 
00236     /** This is called by Gap to notify the application we disconnected */
00237     virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event)
00238     {
00239         if (_bonded) {
00240             /* we have connected to and bonded with the other device, from now
00241              * on we will use the second start function and stay in the same role
00242              * as peripheral or central */
00243             printf("Disconnected.\r\n");
00244             _event_queue.call_in(2000, this, &PrivacyDevice::start_after_bonding);
00245         } else {
00246             printf("Failed to bond.\r\n");
00247             _event_queue.break_dispatch();
00248         }
00249     };
00250 
00251     virtual void onScanTimeout(const ble::ScanTimeoutEvent &)
00252     {
00253         /* if we failed to find the other device, abort so that we change roles */
00254         printf("Haven't seen other device, switch modes.\r\n");
00255         _event_queue.break_dispatch();
00256     }
00257 
00258 public:
00259     static bool _seeded;
00260 
00261 protected:
00262     BLE &_ble;
00263     events::EventQueue &_event_queue;
00264     ble::connection_handle_t _handle;
00265     bool _bonded;
00266 
00267 private:
00268     DigitalOut _led1;
00269 };
00270 
00271 /** A peripheral device will advertise and accept the connections */
00272 class PrivacyPeripheral : public PrivacyDevice {
00273 public:
00274     PrivacyPeripheral(BLE &ble, events::EventQueue &event_queue)
00275         : PrivacyDevice(ble, event_queue) { }
00276 
00277     /** Set up and start advertising accepting anyone */
00278     virtual void start()
00279     {
00280         if (!set_advertising_data()) {
00281             return;
00282         }
00283 
00284         Gap::PeripheralPrivacyConfiguration_t privacy_configuration = {
00285             /* use_non_resolvable_random_address */ false,
00286             Gap::PeripheralPrivacyConfiguration_t::PERFORM_PAIRING_PROCEDURE
00287         };
00288 
00289         _ble.gap().setPeripheralPrivacyConfiguration(&privacy_configuration);
00290 
00291         start_advertising();
00292     };
00293 
00294     /** advertise and filter based on known devices */
00295     virtual void start_after_bonding()
00296     {
00297         Gap::PeripheralPrivacyConfiguration_t privacy_configuration = {
00298             /* use_non_resolvable_random_address */ false,
00299             Gap::PeripheralPrivacyConfiguration_t::REJECT_NON_RESOLVED_ADDRESS
00300         };
00301 
00302         _ble.gap().setPeripheralPrivacyConfiguration(&privacy_configuration);
00303 
00304         start_advertising();
00305     }
00306 
00307     /* helper functions */
00308 
00309 private:
00310     bool set_advertising_data()
00311     {
00312         uint8_t adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
00313         /* use the helper to build the payload */
00314         ble::AdvertisingDataBuilder adv_data_builder(
00315             adv_buffer
00316         );
00317 
00318         adv_data_builder.setFlags();
00319         adv_data_builder.setName(DEVICE_NAME);
00320 
00321         /* Set payload for the set */
00322         ble_error_t error = _ble.gap().setAdvertisingPayload(
00323             ble::LEGACY_ADVERTISING_HANDLE,
00324             adv_data_builder.getAdvertisingData()
00325         );
00326 
00327         if (error) {
00328             print_error(error, "Gap::setAdvertisingPayload() failed");
00329             _event_queue.break_dispatch();
00330             return false;
00331         }
00332 
00333         return true;
00334     }
00335 
00336     bool start_advertising()
00337     {
00338         ble::AdvertisingParameters adv_parameters(
00339             ble::advertising_type_t::CONNECTABLE_UNDIRECTED
00340         );
00341 
00342         ble_error_t error = _ble.gap().setAdvertisingParameters(
00343             ble::LEGACY_ADVERTISING_HANDLE,
00344             adv_parameters
00345         );
00346 
00347         if (error) {
00348             print_error(error, "Gap::setAdvertisingParameters() failed");
00349             return false;
00350         }
00351 
00352         if (_bonded) {
00353             /* if we bonded it means we have found the other device, from now on
00354              * wait at each step until completion */
00355             error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
00356         } else {
00357             /* since we have two boards which might start running this example at the same time
00358              * we randomise the interval of advertising to have them meet when one is advertising
00359              * and the other one is scanning (we use their random address as source of randomness) */
00360             ble::millisecond_t random_duration_ms((1 + rand() % 5) * 1000);
00361             ble::adv_duration_t random_duration(random_duration_ms);
00362             error = _ble.gap().startAdvertising(
00363                 ble::LEGACY_ADVERTISING_HANDLE,
00364                 random_duration
00365             );
00366         }
00367 
00368         if (error) {
00369             print_error(error, "Gap::startAdvertising() failed");
00370             _event_queue.break_dispatch();
00371             return false;
00372         }
00373 
00374         printf("Advertising...\r\n");
00375 
00376         return true;
00377     }
00378 
00379 };
00380 
00381 /** A central device will scan and connect to a peer. */
00382 class PrivacyCentral : public PrivacyDevice {
00383 public:
00384     PrivacyCentral(BLE &ble, events::EventQueue &event_queue)
00385         : PrivacyDevice(ble, event_queue),
00386           _is_connecting(false) { }
00387 
00388     /** start scanning and attach a callback that will handle advertisements
00389      *  and scan requests responses */
00390     virtual void start()
00391     {
00392         Gap::CentralPrivacyConfiguration_t privacy_configuration = {
00393             /* use_non_resolvable_random_address */ false,
00394             Gap::CentralPrivacyConfiguration_t::DO_NOT_RESOLVE
00395         };
00396 
00397         _ble.gap().setCentralPrivacyConfiguration(&privacy_configuration);
00398 
00399         start_scanning();
00400     }
00401 
00402     virtual void start_after_bonding()
00403     {
00404         Gap::CentralPrivacyConfiguration_t privacy_configuration = {
00405             /* use_non_resolvable_random_address */ false,
00406             Gap::CentralPrivacyConfiguration_t::RESOLVE_AND_FILTER
00407         };
00408 
00409         _ble.gap().setCentralPrivacyConfiguration(&privacy_configuration);
00410 
00411         start_scanning();
00412     }
00413 
00414     /* helper functions */
00415 private:
00416     bool start_scanning() {
00417         ble_error_t error;
00418         ble::ScanParameters scan_params;
00419         _ble.gap().setScanParameters(scan_params);
00420 
00421         _is_connecting = false;
00422 
00423         if (_bonded) {
00424             /* if we bonded it means we have found the other device, from now on
00425              * wait at each step until completion */
00426             error = _ble.gap().startScan(ble::scan_duration_t::forever());
00427         } else {
00428             /* otherwise only scan for a limited time before changing roles again
00429              * if we fail to find the other device */
00430             error = _ble.gap().startScan(
00431                 ble::scan_duration_t(ble::millisecond_t(4000))
00432             );
00433         }
00434 
00435         if (error) {
00436             printf("Error during Gap::startScan %d\r\n", error);
00437             _event_queue.break_dispatch();
00438             return false;
00439         }
00440 
00441         printf("Scanning...\r\n");
00442 
00443         return true;
00444     }
00445 
00446 private:
00447     /* Event handler */
00448 
00449     /** Look at scan payload to find a peer device and connect to it */
00450     virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event)
00451     {
00452         /* don't bother with analysing scan result if we're already connecting */
00453         if (_is_connecting) {
00454             return;
00455         }
00456 
00457         ble::AdvertisingDataParser adv_data(event.getPayload());
00458 
00459         /* parse the advertising payload, looking for a discoverable device */
00460         while (adv_data.hasNext()) {
00461             ble::AdvertisingDataParser::element_t field = adv_data.next();
00462 
00463             /* connect to a known device by name */
00464             if (field.type == ble::adv_data_type_t::COMPLETE_LOCAL_NAME &&
00465                 field.value.size() == strlen(DEVICE_NAME) &&
00466                 (memcmp(field.value.data(), DEVICE_NAME, field.value.size()) == 0)) {
00467 
00468                 printf("We found a connectable device\r\n");
00469 
00470                 ble_error_t error = _ble.gap().stopScan();
00471 
00472                 if (error) {
00473                     print_error(error, "Error caused by Gap::stopScan");
00474                     return;
00475                 }
00476 
00477                 const ble::ConnectionParameters connection_params;
00478 
00479                 error = _ble.gap().connect(
00480                     event.getPeerAddressType(),
00481                     event.getPeerAddress(),
00482                     connection_params
00483                 );
00484 
00485                 if (error) {
00486                     print_error(error, "Error caused by Gap::connect");
00487                     return;
00488                 }
00489 
00490                 /* we may have already scan events waiting
00491                  * to be processed so we need to remember
00492                  * that we are already connecting and ignore them */
00493                 _is_connecting = true;
00494 
00495                 return;
00496 
00497             }
00498         }
00499     }
00500 
00501 private:
00502     bool _is_connecting;
00503 };
00504 
00505 /* only seed the random number generation once per application run */
00506 bool PrivacyDevice::_seeded = false;
00507 
00508 int main()
00509 {
00510     BLE& ble = BLE::Instance();
00511 
00512     while(1) {
00513         {
00514             events::EventQueue queue;
00515             printf("\r\n * Device is a peripheral *\r\n\r\n");
00516             PrivacyPeripheral peripheral(ble, queue);
00517             peripheral.run();
00518         }
00519         {
00520             events::EventQueue queue;
00521             printf("\r\n * Device is a central *\r\n\r\n");
00522             PrivacyCentral central(ble, queue);
00523             central.run();
00524         }
00525     }
00526 
00527     return 0;
00528 }