Kris Scholte Lubberink
/
bleswitcher
test
Diff: source/main.cpp
- Revision:
- 0:fcb1e0b995a9
- Child:
- 1:916188eae2bb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/main.cpp Thu Mar 08 18:08:23 2018 +0000 @@ -0,0 +1,417 @@ +/* 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. + */ + +#include <events/mbed_events.h> +#include <mbed.h> +#include "ble/BLE.h" +#include "SecurityManager.h" + +/** 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 uint8_t DEVICE_NAME[] = "SM_device"; + +/* 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: + 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 timeouts, for example when connection attempts fail */ + _ble.gap().onTimeout( + makeFunctionPointer(this, &SMDevice::on_timeout) + ); + + 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(); + }; + + /* event handler functions */ + + /** 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"); + } + + /* disconnect in 500 ms */ + _event_queue.call_in( + 500, &_ble.gap(), + &Gap::disconnect, _handle, Gap::REMOTE_USER_TERMINATED_CONNECTION + ); + } + + /** 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"); + } + } + +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; + } + + /* If the security manager is required this needs to be called before any + * calls to the Security manager happen. */ + error = _ble.securityManager().init(); + + if (error) { + printf("Error during init %d\r\n", error); + return; + } + + /* 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); + + /* print device address */ + Gap::AddressType_t addr_type; + Gap::Address_t addr; + _ble.gap().getAddress(&addr_type, addr); + printf("Device address: %02x:%02x:%02x:%02x:%02x:%02x\r\n", + addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); + + /* when scanning we want to connect to a peer device so we need to + * attach callbacks that are used by Gap to notify us of events */ + _ble.gap().onConnection(this, &SMDevice::on_connect); + _ble.gap().onDisconnection(this, &SMDevice::on_disconnect); + + /* start test in 500 ms */ + _event_queue.call_in(500, this, &SMDevice::start); + }; + + /** This is called by Gap to notify the application we connected */ + virtual void on_connect(const Gap::ConnectionCallbackParams_t *connection_event) = 0; + + /** This is called by Gap to notify the application we disconnected, + * in our case it ends the demonstration. */ + void on_disconnect(const Gap::DisconnectionCallbackParams_t *event) + { + printf("Disconnected - demonstration ended \r\n"); + _event_queue.break_dispatch(); + }; + + /** End demonstration unexpectedly. Called if timeout is reached during advertising, + * scanning or connection initiation */ + void on_timeout(const Gap::TimeoutSource_t source) + { + printf("Unexpected timeout - aborting \r\n"); + _event_queue.break_dispatch(); + }; + + /** 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: + 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 */ + + ble_error_t error; + GapAdvertisingData advertising_data; + + /* add advertising flags */ + advertising_data.addFlags(GapAdvertisingData::LE_GENERAL_DISCOVERABLE + | GapAdvertisingData::BREDR_NOT_SUPPORTED); + + /* add device name */ + advertising_data.addData( + GapAdvertisingData::COMPLETE_LOCAL_NAME, + DEVICE_NAME, + sizeof(DEVICE_NAME) + ); + + error = _ble.gap().setAdvertisingPayload(advertising_data); + + if (error) { + printf("Error during Gap::setAdvertisingPayload\r\n"); + return; + } + + /* advertise to everyone */ + _ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + /* how many milliseconds between advertisements, lower interval + * increases the chances of being seen at the cost of more power */ + _ble.gap().setAdvertisingInterval(20); + _ble.gap().setAdvertisingTimeout(0); + + error = _ble.gap().startAdvertising(); + + if (error) { + printf("Error during Gap::startAdvertising.\r\n"); + return; + } + + /** 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 on_connect(const Gap::ConnectionCallbackParams_t *connection_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, connection_event->peerAddr, sizeof(_peer_address)); + + /* store the handle for future Security Manager requests */ + _handle = connection_event->handle; + + /* 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() + { + /* start scanning and attach a callback that will handle advertisements + * and scan requests responses */ + ble_error_t error = _ble.gap().startScan(this, &SMDeviceCentral::on_scan); + + if (error) { + printf("Error during Gap::startScan %d\r\n", error); + return; + } + } + + /** Look at scan payload to find a peer device and connect to it */ + void on_scan(const Gap::AdvertisementCallbackParams_t *params) + { + /* 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 */ + for (uint8_t i = 0; i < params->advertisingDataLen; ++i) { + /* The advertising payload is a collection of key/value records where + * byte 0: length of the record excluding this byte + * byte 1: The key, it is the type of the data + * byte [2..N] The value. N is equal to byte0 - 1 */ + const uint8_t record_length = params->advertisingData[i]; + if (record_length == 0) { + continue; + } + + /* connect to the same device that connected to us */ + if (memcmp(params->peerAddr, _peer_address, sizeof(_peer_address)) == 0) { + + ble_error_t error = _ble.gap().connect( + params->peerAddr, params->addressType, + NULL, NULL + ); + + if (error) { + printf("Error during Gap::connect %d\r\n", error); + 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; + } + + i += record_length; + } + }; + + /** This is called by Gap to notify the application we connected, + * in our case it immediately request pairing */ + virtual void on_connect(const Gap::ConnectionCallbackParams_t *connection_event) + { + ble_error_t error; + + /* store the handle for future Security Manager requests */ + _handle = connection_event->handle; + + /* in this example the local device is the master so we request pairing */ + error = _ble.securityManager().requestPairing(_handle); + + if (error) { + printf("Error during SM::requestPairing %d\r\n", error); + return; + } + + /* upon pairing success the application will disconnect */ + }; +}; + +int main() +{ + BLE& ble = BLE::Instance(); + events::EventQueue queue; + + { + 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; +}