Okundu Omeni
/
wifi-https-ble-sm-uart-atcmd-5-13-1
Diff: source/BleManager.cpp
- Revision:
- 74:f26e846adfe9
- Child:
- 75:08eff6258e1b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/BleManager.cpp Sun Mar 10 09:46:06 2019 +0000 @@ -0,0 +1,557 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef BLE_SECURITY_ENABLED +#include <events/mbed_events.h> +#include <mbed.h> +#include "ble/BLE.h" +#include "SecurityManager.h" +#include "pretty_printer.h" + +#if MBED_CONF_APP_FILESYSTEM_SUPPORT +#include "LittleFileSystem.h" +#include "HeapBlockDevice.h" +#endif //MBED_CONF_APP_FILESYSTEM_SUPPORT + +/** This example demonstrates all the basic setup required + * for pairing and setting up link security both as a central and peripheral + * + * The example is implemented as two classes, one for the peripheral and one + * for central inheriting from a common base. They are run in sequence and + * require a peer device to connect to. During the peripheral device demonstration + * a peer device is required to connect. In the central device demonstration + * this peer device will be scanned for and connected to - therefore it should + * be advertising with the same address as when it connected. + * + * During the test output is written on the serial connection to monitor its + * progress. + */ + +static const char DEVICE_NAME[] = "SM_device"; + +/* we have to specify the disconnect call because of ambiguous overloads */ +typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t); +const static disconnect_call_t disconnect_call = &Gap::disconnect; + +/* for demonstration purposes we will store the peer device address + * of the device that connects to us in the first demonstration + * so we can use its address to reconnect to it later */ +static BLEProtocol::AddressBytes_t peer_address; + +/** Base class for both peripheral and central. The same class that provides + * the logic for the application also implements the SecurityManagerEventHandler + * which is the interface used by the Security Manager to communicate events + * back to the applications. You can provide overrides for a selection of events + * your application is interested in. + */ +class SMDevice : private mbed::NonCopyable<SMDevice>, + public SecurityManager::EventHandler, + public ble::Gap::EventHandler +{ +public: + SMDevice(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) : + _led1(LED1, 0), + _ble(ble), + _event_queue(event_queue), + _peer_address(peer_address), + _handle(0), + _is_connecting(false) { }; + + virtual ~SMDevice() + { + if (_ble.hasInitialized()) { + _ble.shutdown(); + } + }; + + /** Start BLE interface initialisation */ + void run() + { + ble_error_t error; + + /* to show we're running we'll blink every 500ms */ + _event_queue.call_every(500, this, &SMDevice::blink); + + if (_ble.hasInitialized()) { + printf("Ble instance already initialised.\r\n"); + return; + } + + /* this will inform us off all events so we can schedule their handling + * using our event queue */ + _ble.onEventsToProcess( + makeFunctionPointer(this, &SMDevice::schedule_ble_events) + ); + + /* handle gap events */ + _ble.gap().setEventHandler(this); + + error = _ble.init(this, &SMDevice::on_init_complete); + + if (error) { + printf("Error returned by BLE::init.\r\n"); + return; + } + + /* this will not return until shutdown */ + _event_queue.dispatch_forever(); + }; + +private: + /** Override to start chosen activity when initialisation completes */ + virtual void start() = 0; + + /** This is called when BLE interface is initialised and starts the demonstration */ + void on_init_complete(BLE::InitializationCompleteCallbackContext *event) + { + ble_error_t error; + + if (event->error) { + printf("Error during the initialisation\r\n"); + return; + } + + /* This path will be used to store bonding information but will fallback + * to storing in memory if file access fails (for example due to lack of a filesystem) */ + const char* db_path = "/fs/bt_sec_db"; + /* If the security manager is required this needs to be called before any + * calls to the Security manager happen. */ + error = _ble.securityManager().init( + true, + false, + SecurityManager::IO_CAPS_NONE, + NULL, + false, + db_path + ); + + if (error) { + printf("Error during init %d\r\n", error); + return; + } + + error = _ble.securityManager().preserveBondingStateOnReset(true); + + if (error) { + printf("Error during preserveBondingStateOnReset %d\r\n", error); + } + +#if MBED_CONF_APP_FILESYSTEM_SUPPORT + /* Enable privacy so we can find the keys */ + error = _ble.gap().enablePrivacy(true); + + if (error) { + printf("Error enabling privacy\r\n"); + } + + Gap::PeripheralPrivacyConfiguration_t configuration_p = { + /* use_non_resolvable_random_address */ false, + Gap::PeripheralPrivacyConfiguration_t::REJECT_NON_RESOLVED_ADDRESS + }; + _ble.gap().setPeripheralPrivacyConfiguration(&configuration_p); + + Gap::CentralPrivacyConfiguration_t configuration_c = { + /* use_non_resolvable_random_address */ false, + Gap::CentralPrivacyConfiguration_t::RESOLVE_AND_FORWARD + }; + _ble.gap().setCentralPrivacyConfiguration(&configuration_c); + + /* this demo switches between being master and slave */ + _ble.securityManager().setHintFutureRoleReversal(true); +#endif + + /* Tell the security manager to use methods in this class to inform us + * of any events. Class needs to implement SecurityManagerEventHandler. */ + _ble.securityManager().setSecurityManagerEventHandler(this); + + /* gap events also handled by this class */ + _ble.gap().setEventHandler(this); + + /* print device address */ + Gap::AddressType_t addr_type; + Gap::Address_t addr; + _ble.gap().getAddress(&addr_type, addr); + print_address(addr); + + /* start test in 500 ms */ + _event_queue.call_in(500, this, &SMDevice::start); + }; + + /** Schedule processing of events from the BLE in the event queue. */ + void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) + { + _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents)); + }; + + /** Blink LED to show we're running */ + void blink(void) + { + _led1 = !_led1; + }; + +private: + /* Event handler */ + + /** Respond to a pairing request. This will be called by the stack + * when a pairing request arrives and expects the application to + * call acceptPairingRequest or cancelPairingRequest */ + virtual void pairingRequest( + ble::connection_handle_t connectionHandle + ) { + printf("Pairing requested - authorising\r\n"); + _ble.securityManager().acceptPairingRequest(connectionHandle); + } + + /** Inform the application of a successful pairing. Terminate the demonstration. */ + virtual void pairingResult( + ble::connection_handle_t connectionHandle, + SecurityManager::SecurityCompletionStatus_t result + ) { + if (result == SecurityManager::SEC_STATUS_SUCCESS) { + printf("Pairing successful\r\n"); + } else { + printf("Pairing failed\r\n"); + } + } + + /** Inform the application of change in encryption status. This will be + * communicated through the serial port */ + virtual void linkEncryptionResult( + ble::connection_handle_t connectionHandle, + ble::link_encryption_t result + ) { + if (result == ble::link_encryption_t::ENCRYPTED) { + printf("Link ENCRYPTED\r\n"); + } else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) { + printf("Link ENCRYPTED_WITH_MITM\r\n"); + } else if (result == ble::link_encryption_t::NOT_ENCRYPTED) { + printf("Link NOT_ENCRYPTED\r\n"); + } + + /* disconnect in 2 s */ + _event_queue.call_in( + 2000, + &_ble.gap(), + disconnect_call, + _handle, + ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION) + ); + } + + /** This is called by Gap to notify the application we disconnected, + * in our case it ends the demonstration. */ + virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &) + { + printf("Diconnected\r\n"); + _event_queue.break_dispatch(); + }; + + virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &) + { + printf("Advertising timed out - aborting\r\n"); + _event_queue.break_dispatch(); + } + + virtual void onScanTimeout(const ble::ScanTimeoutEvent &) + { + printf("Scan timed out - aborting\r\n"); + _event_queue.break_dispatch(); + } + +private: + DigitalOut _led1; + +protected: + BLE &_ble; + events::EventQueue &_event_queue; + BLEProtocol::AddressBytes_t &_peer_address; + ble::connection_handle_t _handle; + bool _is_connecting; +}; + +/** A peripheral device will advertise, accept the connection and request + * a change in link security. */ +class SMDevicePeripheral : public SMDevice { +public: + SMDevicePeripheral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) + : SMDevice(ble, event_queue, peer_address) { } + + virtual void start() + { + /* Set up and start advertising */ + uint8_t adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE]; + /* use the helper to build the payload */ + ble::AdvertisingDataBuilder adv_data_builder( + adv_buffer + ); + + adv_data_builder.setFlags(); + adv_data_builder.setName(DEVICE_NAME); + + /* Set payload for the set */ + ble_error_t error = _ble.gap().setAdvertisingPayload( + ble::LEGACY_ADVERTISING_HANDLE, + adv_data_builder.getAdvertisingData() + ); + + if (error) { + print_error(error, "Gap::setAdvertisingPayload() failed"); + _event_queue.break_dispatch(); + return; + } + + ble::AdvertisingParameters adv_parameters( + ble::advertising_type_t::CONNECTABLE_UNDIRECTED + ); + + error = _ble.gap().setAdvertisingParameters( + ble::LEGACY_ADVERTISING_HANDLE, + adv_parameters + ); + + if (error) { + print_error(error, "Gap::setAdvertisingParameters() failed"); + return; + } + + error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); + + if (error) { + print_error(error, "Gap::startAdvertising() failed"); + return; + } + + printf("Please connect to device\r\n"); + + /** This tells the stack to generate a pairingRequest event + * which will require this application to respond before pairing + * can proceed. Setting it to false will automatically accept + * pairing. */ + _ble.securityManager().setPairingRequestAuthorisation(true); + }; + + /** This is called by Gap to notify the application we connected, + * in our case it immediately requests a change in link security */ + virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) + { + ble_error_t error; + + /* remember the device that connects to us now so we can connect to it + * during the next demonstration */ + memcpy(_peer_address, event.getPeerAddress().data(), sizeof(_peer_address)); + + printf("Connected to peer: "); + print_address(event.getPeerAddress().data()); + + _handle = event.getConnectionHandle(); + + /* Request a change in link security. This will be done + * indirectly by asking the master of the connection to + * change it. Depending on circumstances different actions + * may be taken by the master which will trigger events + * which the applications should deal with. */ + error = _ble.securityManager().setLinkSecurity( + _handle, + SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM + ); + + if (error) { + printf("Error during SM::setLinkSecurity %d\r\n", error); + return; + } + }; +}; + +/** A central device will scan, connect to a peer and request pairing. */ +class SMDeviceCentral : public SMDevice { +public: + SMDeviceCentral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) + : SMDevice(ble, event_queue, peer_address) { } + + virtual void start() + { + ble::ScanParameters params; + ble_error_t error = _ble.gap().setScanParameters(params); + + if (error) { + print_error(error, "Error in Gap::startScan %d\r\n"); + return; + } + + /* start scanning, results will be handled by onAdvertisingReport */ + error = _ble.gap().startScan(); + + if (error) { + print_error(error, "Error in Gap::startScan %d\r\n"); + return; + } + + printf("Please advertise\r\n"); + + printf("Scanning for: "); + print_address(_peer_address); + } + +private: + /* Gap::EventHandler */ + + /** Look at scan payload to find a peer device and connect to it */ + virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event) + { + /* don't bother with analysing scan result if we're already connecting */ + if (_is_connecting) { + return; + } + + /* parse the advertising payload, looking for a discoverable device */ + if (memcmp(event.getPeerAddress().data(), _peer_address, sizeof(_peer_address)) == 0) { + ble_error_t error = _ble.gap().stopScan(); + + if (error) { + print_error(error, "Error caused by Gap::stopScan"); + return; + } + + ble::ConnectionParameters connection_params( + ble::phy_t::LE_1M, + ble::scan_interval_t(50), + ble::scan_window_t(50), + ble::conn_interval_t(50), + ble::conn_interval_t(100), + ble::slave_latency_t(0), + ble::supervision_timeout_t(100) + ); + connection_params.setOwnAddressType(ble::own_address_type_t::RANDOM); + + error = _ble.gap().connect( + event.getPeerAddressType(), + event.getPeerAddress(), + connection_params + ); + + if (error) { + print_error(error, "Error caused by Gap::connect"); + return; + } + + /* we may have already scan events waiting + * to be processed so we need to remember + * that we are already connecting and ignore them */ + _is_connecting = true; + + return; + } + } + + /** This is called by Gap to notify the application we connected, + * in our case it immediately request pairing */ + virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) + { + if (event.getStatus() == BLE_ERROR_NONE) { + /* store the handle for future Security Manager requests */ + _handle = event.getConnectionHandle(); + + printf("Connected\r\n"); + + /* in this example the local device is the master so we request pairing */ + ble_error_t error = _ble.securityManager().requestPairing(_handle); + + if (error) { + printf("Error during SM::requestPairing %d\r\n", error); + return; + } + + /* upon pairing success the application will disconnect */ + } + + /* failed to connect - restart scan */ + ble_error_t error = _ble.gap().startScan(); + + if (error) { + print_error(error, "Error in Gap::startScan %d\r\n"); + return; + } + }; +}; + + +#if MBED_CONF_APP_FILESYSTEM_SUPPORT +bool create_filesystem() +{ + static LittleFileSystem fs("fs"); + + /* replace this with any physical block device your board supports (like an SD card) */ + static HeapBlockDevice bd(4096, 256); + + int err = bd.init(); + + if (err) { + return false; + } + + err = bd.erase(0, bd.size()); + + if (err) { + return false; + } + + err = fs.mount(&bd); + + if (err) { + /* Reformat if we can't mount the filesystem */ + printf("No filesystem found, formatting...\r\n"); + + err = fs.reformat(&bd); + + if (err) { + return false; + } + } + + return true; +} +#endif //MBED_CONF_APP_FILESYSTEM_SUPPORT + +int main() +{ + BLE& ble = BLE::Instance(); + events::EventQueue queue; + +#if MBED_CONF_APP_FILESYSTEM_SUPPORT + /* if filesystem creation fails or there is no filesystem the security manager + * will fallback to storing the security database in memory */ + if (!create_filesystem()) { + printf("Filesystem creation failed, will use memory storage\r\n"); + } +#endif + + while(1) { + { + printf("\r\n PERIPHERAL \r\n\r\n"); + SMDevicePeripheral peripheral(ble, queue, peer_address); + peripheral.run(); + } + + { + printf("\r\n CENTRAL \r\n\r\n"); + SMDeviceCentral central(ble, queue, peer_address); + central.run(); + } + } + + return 0; +} + +#endif \ No newline at end of file