Mistake on this page?
Report an issue in GitHub or email us

SecurityManager

SecurityManager deals with authentication and encryption for the Bluetooth Low Energy link. The pairing and optionally bonding processes provide this. The SecurityManager achieves bonding by saving the pairing information and reusing it on subsequent reconnections. This saves time because the pairing does not have to be performed again.

The pairing process may produce a set of keys to be used during current or later connections. The SecurityManager handles these, and they include the Long Term Encryption Key (LTK), the Identity Resolving Key (IRK) and the Connection Signature Resolving Key (CSRK). The SecurityManager uses the LTK to encrypt subsequent connections without having to pair again. The Link Controller uses IRK to identify peers who use random resolvable addresses. The application uses CSRK to sign and authenticate signed data.

The pairing process may provide man-in-the-middle protection (MITM). The SecurityManager achieves this through various means, including out of band communication, depending on the capabilities of the local and peer device.

The SecurityManager stores the keys, permanently if possible, to speed security requests on subsequent connections.

Security requests may come explicitly from the user application or implicitly from the GATT server based on attribute requirements.

Pairing

There are several ways to provide different levels of security during pairing depending on your requirements and the facilities the application provides. The process starts with initializing the SecurityManager with default options for new connections. You can later change some settings per link or globally.

The important settings in the init() function are the MITM requirement and IO capabilities. MITM protection prevents an attack where one device can impersonate another device by pairing with both devices at the same time. You can achieve this protection by sharing information between the devices through an independent channel. The IO capabilities of both devices dictate what algorithm is used. For details, see BLUETOOTH SPECIFICATION Version 5.0 | Vol 3, Part H - 2.3.5.1. You can change the IO capabilities after initialization with setIoCapability(). This takes effect for all subsequent pairings.

Secure Connections, which relies on elliptical curve cryptography, provides the most secure pairing. Support for Secure Connections depends on both the stack and controller on both sides supporting it. If either side doesn't support it, legacy pairing is used. This is an older standard of pairing. If you require higher security, you can disable legacy pairing by calling allowLegacyPairing(false);.

Out of band (OOB) data used in pairing

Sharing this information through IO capabilities means user interaction, which limits the degree of protection due to the limit of the amount of data that you can expect to transfer. Another solution is using out of band (OOB) communication to transfer this data. OOB communication can send more data and make MITM attacks less likely to succeed. The application must exchange OOB data and provide it to the SecurityManager. Use setOOBDataUsage() to indicate you want to use it. With this same call, you can set whether the communication channel you are using to transmit the OOB data is itself secure against MITM protection - this sets the level of the link security achieved using pairing that uses this data.

Signing

Applications may require a level of security providing confidence that data transfers are coming from a trusted source. You can achieve this by encrypting the link, which also provides added confidentiality. Encryption is a good choice when a device stays connected but introduces latency due to the need for encrypting the link if the device only connects periodically to transfer data. If you do not require confidentiality, the GATT server may allow writes to happen over an unencrypted link but authenticated by a signature present in each packet. This signature relies on having sent a signing key to the peer during pairing prior to sending any signed packets.

Persistence of security information

SecurityManager stores all the data required for its operation on active links. Depending on resources available on the device, it also stores data for disconnected devices, which have bonded to be reused when reconnected. If the application has initialized a file system and the SecurityManager has received a file path during the init() call, SecurityManager may also provide data persistence across resets. You must enable this by calling preserveBondingStateOnReset(). Persistence may fail if abnormally terminated. SecurityManager may also fall back to a nonpersistent implementation if the resources are too limited.

How to use

Call init() with your chosen settings before calling any other SecurityManager functions.

The SecurityManager communicates with your application through events. These trigger calls in the EventHandler that you must provide by calling the setSecurityManagerEventHandler() function.

The most important process is pairing. You may trigger this manually by calling requestPairing(). Pairing may also result from the application requiring encryption by calling setLinkEncryption() or the application requiring MITM protection through requestAuthentication().

You can call all of these implicitly by using setLinkSecurity() to set the required security for the link. The SecurityManager triggers the process required to achieve the set security level. You can only escalate the security level. Asking the SecurityManager for a lower security level than the existing one does not fail but results in a event informing the application through linkEncryptionResult() of the current level, (which remains unchanged).

The chosen pairing algorithms depend on the IO capabilities and OOB use settings. They produce appropriate events, which your EventHandler must handle. If your event handler doesn't support all the calls, you must not set IO capabilities or set OOB use in such a way that would trigger them, or else the pairing fails (usually by timing out).

The simplest example is a pairing of a device with no IO capabilities and no OOB data available. This does not provide any MITM protection. The pairing (triggered implicitly or called explicitly) results in the generation of an event on the peer calling pairingRequest(). The event handler must make a decision (either in the application itself or based on user interaction) whether to accept the pairing and call acceptPairing() or cancelPairing(). An event calling pairingResult() in the EventHandler communicates te result on both peers.

SecurityManager class reference

Data Structures
class  EventHandler
Public Types
enum  Keypress_t {
  KEYPRESS_STARTED, KEYPRESS_ENTERED, KEYPRESS_ERASED, KEYPRESS_CLEARED,
  KEYPRESS_COMPLETED
}
enum  SecurityMode_t {
  SECURITY_MODE_NO_ACCESS, SECURITY_MODE_ENCRYPTION_OPEN_LINK, SECURITY_MODE_ENCRYPTION_NO_MITM, SECURITY_MODE_ENCRYPTION_WITH_MITM,
  SECURITY_MODE_SIGNED_NO_MITM, SECURITY_MODE_SIGNED_WITH_MITM
}
enum  LinkSecurityStatus_t { NOT_ENCRYPTED, ENCRYPTION_IN_PROGRESS, ENCRYPTED }
 Defines possible security status or states. More...
enum  SecurityIOCapabilities_t {
  IO_CAPS_DISPLAY_ONLY = 0x00, IO_CAPS_DISPLAY_YESNO = 0x01, IO_CAPS_KEYBOARD_ONLY = 0x02, IO_CAPS_NONE = 0x03,
  IO_CAPS_KEYBOARD_DISPLAY = 0x04
}
enum  SecurityCompletionStatus_t {
  SEC_STATUS_SUCCESS = 0x00, SEC_STATUS_TIMEOUT = 0x01, SEC_STATUS_PDU_INVALID = 0x02, SEC_STATUS_PASSKEY_ENTRY_FAILED = 0x81,
  SEC_STATUS_OOB_NOT_AVAILABLE = 0x82, SEC_STATUS_AUTH_REQ = 0x83, SEC_STATUS_CONFIRM_VALUE = 0x84, SEC_STATUS_PAIRING_NOT_SUPP = 0x85,
  SEC_STATUS_ENC_KEY_SIZE = 0x86, SEC_STATUS_SMP_CMD_UNSUPPORTED = 0x87, SEC_STATUS_UNSPECIFIED = 0x88, SEC_STATUS_REPEATED_ATTEMPTS = 0x89,
  SEC_STATUS_INVALID_PARAMS = 0x8A, SEC_STATUS_DHKEY_CHECK_FAILED = 0x8B, SEC_STATUS_COMPARISON_FAILED = 0x8C
}
typedef uint8_t Passkey_t[PASSKEY_LEN]
typedef FunctionPointerWithContext< const SecurityManager * > SecurityManagerShutdownCallback_t
typedef CallChainOfFunctionPointersWithContext< const SecurityManager * > SecurityManagerShutdownCallbackChain_t
typedef void(* HandleSpecificEvent_t) (ble::connection_handle_t connectionHandle)
typedef void(* SecuritySetupInitiatedCallback_t) (ble::connection_handle_t, bool allowBonding, bool requireMITM, SecurityIOCapabilities_t iocaps)
typedef void(* SecuritySetupCompletedCallback_t) (ble::connection_handle_t, SecurityCompletionStatus_t status)
typedef void(* LinkSecuredCallback_t) (ble::connection_handle_t connectionHandle, SecurityMode_t securityMode)
typedef void(* PasskeyDisplayCallback_t) (ble::connection_handle_t connectionHandle, const Passkey_t passkey)
Public Member Functions
virtual ble_error_t init (bool enableBonding=true, bool requireMITM=true, SecurityIOCapabilities_t iocaps=IO_CAPS_NONE, const Passkey_t passkey=NULL, bool signing=true, const char *dbFilepath=NULL)
virtual ble_error_t setDatabaseFilepath (const char *dbFilepath=NULL)
virtual ble_error_t reset (void)
virtual ble_error_t preserveBondingStateOnReset (bool enable)
virtual ble_error_t purgeAllBondingState (void)
virtual ble_error_t generateWhitelistFromBondTable (Gap::Whitelist_t *whitelist) const
virtual ble_error_t requestPairing (ble::connection_handle_t connectionHandle)
virtual ble_error_t acceptPairingRequest (ble::connection_handle_t connectionHandle)
virtual ble_error_t cancelPairingRequest (ble::connection_handle_t connectionHandle)
virtual ble_error_t setPairingRequestAuthorisation (bool required=true)
virtual ble_error_t allowLegacyPairing (bool allow=true)
virtual ble_error_t getSecureConnectionsSupport (bool *enabled)
virtual ble_error_t setIoCapability (SecurityIOCapabilities_t iocaps)
virtual ble_error_t setDisplayPasskey (const Passkey_t passkey)
virtual ble_error_t setLinkSecurity (ble::connection_handle_t connectionHandle, SecurityMode_t securityMode)
virtual ble_error_t setKeypressNotification (bool enabled=true)
virtual ble_error_t enableSigning (ble::connection_handle_t connectionHandle, bool enabled=true)
virtual ble_error_t setHintFutureRoleReversal (bool enable=true)
virtual ble_error_t getLinkEncryption (ble::connection_handle_t connectionHandle, ble::link_encryption_t *encryption)
virtual ble_error_t setLinkEncryption (ble::connection_handle_t connectionHandle, ble::link_encryption_t encryption)
virtual ble_error_t setEncryptionKeyRequirements (uint8_t minimumByteSize, uint8_t maximumByteSize)
virtual ble_error_t requestAuthentication (ble::connection_handle_t connectionHandle)
virtual ble_error_t generateOOB (const ble::address_t *address)
virtual ble_error_t setOOBDataUsage (ble::connection_handle_t connectionHandle, bool useOOB, bool OOBProvidesMITM=true)
virtual ble_error_t confirmationEntered (ble::connection_handle_t connectionHandle, bool confirmation)
virtual ble_error_t passkeyEntered (ble::connection_handle_t connectionHandle, Passkey_t passkey)
virtual ble_error_t sendKeypressNotification (ble::connection_handle_t connectionHandle, Keypress_t keypress)
virtual ble_error_t legacyPairingOobReceived (const ble::address_t *address, const ble::oob_tk_t *tk)
virtual ble_error_t oobReceived (const ble::address_t *address, const ble::oob_lesc_value_t *random, const ble::oob_confirm_t *confirm)
virtual ble_error_t getSigningKey (ble::connection_handle_t connectionHandle, bool authenticated)
void onShutdown (const SecurityManagerShutdownCallback_t &callback)
template<typename T >
void onShutdown (T *objPtr, void(T::*memberPtr)(const SecurityManager *))
SecurityManagerShutdownCallbackChain_tonShutdown ()
virtual void setSecurityManagerEventHandler (EventHandler *handler)
virtual ble_error_t getAddressesFromBondTable (Gap::Whitelist_t &addresses) const
ble_error_t getLinkSecurity (ble::connection_handle_t connectionHandle, LinkSecurityStatus_t *securityStatus)
virtual void onSecuritySetupInitiated (SecuritySetupInitiatedCallback_t callback)
virtual void onSecuritySetupCompleted (SecuritySetupCompletedCallback_t callback)
virtual void onLinkSecured (LinkSecuredCallback_t callback)
virtual void onSecurityContextStored (HandleSpecificEvent_t callback)
virtual void onPasskeyDisplay (PasskeyDisplayCallback_t callback)
void processSecuritySetupInitiatedEvent (ble::connection_handle_t connectionHandle, bool allowBonding, bool requireMITM, SecurityIOCapabilities_t iocaps)
void processSecuritySetupCompletedEvent (ble::connection_handle_t connectionHandle, SecurityCompletionStatus_t status)
void processLinkSecuredEvent (ble::connection_handle_t connectionHandle, SecurityMode_t securityMode)
void processSecurityContextStoredEvent (ble::connection_handle_t connectionHandle)
void processPasskeyDisplayEvent (ble::connection_handle_t connectionHandle, const Passkey_t passkey)
Static Public Attributes
static const unsigned PASSKEY_LEN = 6
Protected Attributes
EventHandlereventHandler
LegacyEventHandler defaultEventHandler

SecurityManager example

The SecurityManager example demonstrates both a central and a peripheral connecting, performing basic pairing and setting up link security.

/* 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;
}

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.