ble-con-manager

Description

This library helps the user to handle multiple BLE peripheral connections at once. It requires the ble-scanner-app-mt library version 2 or higher.

The library introduces the concept of "peers". Peers are simply BLE peripheral devices which the BLE central wants to connect to.

The library provides high level functions which enable the user to configure how it should behave regarding scanning and connecting. This implies following workflow:

  • Registering the peer to the Library
  • Connect to the peer
  • Read data from or write data to a specific characteristic of a peer
  • Receiving of notification regarding a changed content of a characteristic of a peer

OVERVIEW

Abstract:

This library helps the user to handle multiple BLE peripheral connections at once. It requires the ble-scanner-app-mt library version 2 (TBD SiSo) or higher.

The library introduces the concept of "peers". Peers are simply BLE peripheral devices which the BLE central wants to connect to.

The library provides high level functions which enable the user to configure how it should behave regarding scanning and connecting. This implies following workflow when using the library:

  • Registering the peer to the Library (using BleConMngr_Register() )
  • Connect to the peer (using BleConMngr_Connect() and the peer handle retourned by BleConMngr_Register() )
    A structure (TBleConMngrPeerConfig) containing the configuration for the regarding peer must be passed to the BleConMngr_Register() function.
  • Read data from (using BleConMngr_Read()) or write data to (using BleConMngr_Write() ) a specific characteristic of a peer
  • Receiving and processing (using BleConMngr_EvtNotify()) of notification regarding a changed content of a characteristic of a peer
    In order to enable the notification for the desired BLE characteristic, the corresponding configuration characteristic must be written using the BleConMngr_Write() function.
How to use:
static iInterface = // define your interface here (e.g. BLEAPP_INTERFACE_FIRMWARE) #callback Timer1s() { /* add your code */ } #callback AppInitDone() { // Init your HW // Init BLE connection manager BleConMngr_Init(iInterface); new sPeer[TBleConMngrPeerConfig]; /* setup filter for peer scanning filter for peripherals advertising with name "Test". */ new iResult = BleConMngr_AddFilter(sPeer, BLE_CONMNGR_FILTER_TAG_NAME, 5, {"Test\0"}); if(iResult < OK) #log("!Error adding filter"); /* add another filter filter for devices with 0xFFFF in thier MSD. */ iResult = BleConMngr_AddFilter(sPeer, BLE_CONMNGR_FILTER_TAG_MSD, 2, {0xFF, 0xFF}); if(iResult < OK) #log("!Error adding filter"); /* finally, add another filter filter for device with the hihgest RSSI. */ iResult = BleConMngr_AddFilter(sPeer, BLE_CONMNGR_FILTER_TAG_RSSI); if(iResult < OK) #log("!Error adding filter"); // setup retry, scan and behaviour BleConMngr_SetupConBehaviour(sPeer, 10, 1, 1000) // register to peer manager new iPeerHandle = BleConMngr_Register(sPeer); if(iResult < OK) #log("!Error registering peer"); /* try to connect to peer with configured filters and scan intervals/retries: the library now will: - start a scan - filter all scan results for the configured filtercriteria - when a suitable peer is found: BleConMngr_EvtConnected with event BLE_CONMNGR_EVT_CONNECTED will be emitted. - if no suitable peer is found: BleConMngr_EvtConnected with event BLE_CONMNGR_EVT_ERROR_NOTFOUND will be emitted. */ iResult = BleConMngr_Connect(iPeerHandle); if(iResult < OK) #log("!Error connecting to peer"); /* enable main timer */ setInterval(Timer1s, 1000); } main() { salve(AppInitDone); } BleConMngr_EvtConnected(iEvent, iIndex, sPeer[TBleConnMngrPeer]) { #log("~BleConMngr_EvtConnected: Event %d, Peer %d (%s - %02X:%02X:%02X:%02X:%02X:%02X, State: %d, Connhandle %d)", iEvent, iIndex, sPeer.aName, sPeer.aAddr{0}, sPeer.aAddr{1}, sPeer.aAddr{2}, sPeer.aAddr{3}, sPeer.aAddr{4}, sPeer.aAddr{5}, sPeer.iState, sPeer.iConnHandle) switch(iEvent) { case BLE_CONMNGR_EVT_ERROR_NOTFOUND: { // The requested peer has not been found // TODO: add your application code } case BLE_CONMNGR_EVT_ERROR_CONNECT: { // Connecting to the requested peer has failed // TODO: add your application code } case BLE_CONMNGR_EVT_CONNECTED: { // Connected to the requested peer // TODO: add your application code } case BLE_CONMNGR_EVT_DISCONNECTED: { // Disconnected from the requested peer // TODO: add your application code } } } BleConMngr_EvtRead(iIndex, iCharHandle, iOffset, aData{}, iLen) { #log("~BleConMngr_EvtRead: Peer %d (Char: %d Offset: %d, Len: %d)", iIndex, iCharHandle, iOffset, iLen); // Received Data from a peer // TODO: add your application code } BleConMngr_EvtReadResult(iIndex, iCharHandle, iResult) { #log("~BleConMngr_EvtReadResult: Peer %d (Char: %d, iResult: %d)", iIndex, iCharHandle, iResult); // Data receive from a peer finished // TODO: add your application code } BleConMngr_EvtWriteResult(iIndex, iCharHandle, iResult, iLen) { #log("~BleConMngr_EvtWriteResult: Peer %d (Char: %d, iResult: %d, iLen: %d)", iIndex, iCharHandle, iResult, iLen); // Data write to a peer finished // TODO: add your application code } BleConMngr_EvtNotify(iIndex, iCharHandle, aData{}, iLen) { #log("~BleConMngr_EvtNotify: Peer %d (Char: %d, iLen: %d)", iIndex, iCharHandle, iLen); // Received a notification from a peer // TODO: add your application code } BleConMngr_EvtError(iError) { #log("!BleConMngr_EvtError: %d", iError); // a (BLE) error occured -> reset interface BleConMngr_Close(); // ... code to reset your application state /* init BLE connection manager */ BleConMngr_Init(iInterface); }

REQUIRED

Callbacks:
This library requires the user to implement a number of callbacks. Specifically these are:
  • BleConMngr_EvtConnected()
  • BleConMngr_EvtRead()
  • BleConMngr_EvtReadResult()
  • BleConMngr_EvtWriteResult()
  • BleConMngr_EvtNotify()
  • BleConMngr_EvtError ()
  • BleConMngr_EvtRSSI()
  • BleConMngr_EvtRSSIResult()
  • BleConMngr_EvtConnected(iEvent, iPeerHandle, sPeer)

    Called when:

  • the connection to a peer could be established,
  • the connection to a peer has been disconnected (intentional disconnection),
  • an existing connection was interrupted (unintentional disconnection) or
  • the peer was not found (max number of retries exceeded) or
  • an error occurred while establishing a connection.
  • iEvent : BLE_CONMNGR_EVT_X - Specifies the connection state change that led to the call of this function.
    iPeerHandle : s32 - Handle of the peer whose connection status has changed
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    sPeer : TBleConnMngrPeer - Status information about the Peer
    This function will be called after BleConMngr_Connect() or BleConMngr_Disconnect() and unsolicited if a peer was unintentional disconnected (i.e. an existing connection was interrupted).
    BleConMngr_EvtRead(iPeerHandle, iCharHandle, iOffset, aData{}, iLen)

    Called when a block of the requested data has been received.

    iPeerHandle : s32 - Handle of the peer for which a block of data has been received
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iCharHandle : u16 - Handle of the BLE characteristic from which the data are read
    iOffset : u16 - Offset of the data
    aData{} : u8 - Data buffer with available read data
    iLen : s32 - Number of bytes available within data buffer
    This function will be called after BleConMngr_Read()
    BleConMngr_EvtReadResult(iPeerHandle, iCharHandle, iResult)

    Called if all requested data has been received or if an error has occurred while reading the data (i.e. Read command has been finished)

    iPeerHandle : s32 - Handle of the peer for which the read command has been finished
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iCharHandle : u16 - Handle of the BLE characteristic from which the data are read
    iResult : s32 - Resultcode
    OK - The requested data could be read successfully
    ERROR - Error or Timeout has occurred while reading the data
    This function will be called after BleConMngr_Read()
    BleConMngr_EvtWriteResult(iPeerHandle, iCharHandle, iResult, iLen)

    Called when data has been successfully written to the characteristic of a peer or an error has occurred while writing the data.

    iPeerHandle : s32 - Handle of the peer for which the write command has been finished
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iCharHandle : u16 - Handle of the BLE characteristic to which the data are written
    iResult : s32 - Resultcode
    OK - Data has been successfully written
    ERROR - Error or Timeout has occurred while writing the data
    This function will be called after BleConMngr_Write()
    BleConMngr_EvtNotify(iPeerHandle, iCharHandle, aData{}, iLen)

    Called when the software of the peer changes the content of a characteristic for which notification was enabled previously.

    iPeerHandle : s32 - Handle of the peer whose software has modified the content of a characteristic
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iCharHandle : u16 - Handle of the BLE characteristic whose content has been modified by the peer's software
    aData{} : u8 - Data buffer with available notification data
    iLen : s32 - Number of bytes available within data buffer
    In order to enable the notification for the desired BLE characteristic, the corresponding configuration characteristic must be written using the BleConMngr_Write() function.
    BleConMngr_EvtError(iError)

    Called if an internal error has occured

    Currently these errors are transparently passed from ble-scanner-app-mt.
    iError : BLE_EVENT_ERROR_x - Errorcode

    To handle this error adequately a full reset of the library (and BLE Scanner App interface of the device) is recommended:
    BleConMngr_EvtError(iError) { #log("!BleConMngr_EvtError: %d", iError); // a (BLE) error occured -> reset interface BleConMngr_Close(); // ... code to reset your application state /* init BLE connection manager */ BleConMngr_Init(iInterface); }
    Make sure to also reset your application!
    After Closing and Re-Opening with BleConMngr_Init() all peers need to be re-registered!
    BleConMngr_EvtRSSI(iPeerHandle, iRSSI)

    Called when a BLE RSSI has received.

    iPeerHandle : s32 - Handle of the peer for which a block of data has been received
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iRSSI : s8 - BLE RSSI of a specific BLE peripheral
    This function will be called after BleConMngr_GetRSSI()
    BleConMngr_EvtRSSIResult(iPeerHandle, iResult)

    Called if BLE RSSIhas been received or if an error has occurred while reading the data (i.e. GetRSSI command has been finished)

    iPeerHandle : s32 - Handle of the peer for which the read command has been finished
    To identify the peer, compare this handle with the handles initialized by BleConMngr_Register()
    iResult : s32 - Resultcode
    OK - The requested data could be read successfully
    ERROR - Error or Timeout has occurred while reading the data
    This function will be called after BleConMngr_GetRSSI()

    BASIC

    BLE_CONMNGR_OK_x:

    BLE Connection Manager library OK Results

    BLE_CONMNGR_OK_OP_OMITTED - (OK + 1) Operation is omitted because it is alrady in progress.
    BLE_CONMNGR_ERROR_x:

    BLE Connection Manager library Errors Codes

    BLE_CONMNGR_ERROR_OP_INVALID - (ERROR - 1) Operation is invalid in this state.
    BLE_CONMNGR_EVT_X:

    Changes of the connection status of a peer that can lead to calling the "BleConMngr_EvtConnected()" function

    BLE_CONMNGR_EVT_ERROR_NOTFOUND - (-2) The peer was not found. This event is triggered if the configured max number of Scan Retries was exceeded.
    This can be due to:
  • The peer is not advertising.
  • Wrong filter criteria configured for the searched peer.
  • BLE_CONMNGR_EVT_ERROR_CONNECT - (-1) An error occured during connecting to the peer.
    BLE_CONMNGR_EVT_NONE = (0)
    BLE_CONMNGR_EVT_CONNECTED - (1) The peer is connected.
    BLE_CONMNGR_EVT_DISCONNECTED - (2) The peer is disconnected.
    BLE_CONMNGR_FILTER_TAG_X:

    Filter typs for scanning for a peer

    BLE_CONMNGR_FILTER_TAG_NONE - (0) No filter. Marks the end of the filter config.
    BLE_CONMNGR_FILTER_TAG_MAC - (1) Filters for HW address.
    BLE_CONMNGR_FILTER_TAG_NAME - (2) Filter for peripheral advertising name.
    BLE_CONMNGR_FILTER_TAG_MSD - (3) Filter for MSD (Manufacturer Specific Data)
    BLE_CONMNGR_FILTER_TAG_RSSI - (4) Filter for RSSI - use the device with the highest available RSSI.
    BLE_CONMNGR_FILTER_TAG_NUM - (5) Number of aviable filter typs.
    TBleConMngrPeerConfig:

    Configuration of a peer

    aFilterCfg{BLE_CONMNGR_SIZE_FILTERCFG} : u8 - filter config for the peer (size defined by BLE_CONMNGR_SIZE_FILTERCFG)
    iScanInterval : u32 - Scan interval (in sec.) to be used when searching for the peer (If not set 10sec. is used).
    iScanRetries : u32 - Maximum number of rescans before BLE_CONMNGR_EVT_ERROR_NOTFOUND is issued.
    iConInterval : u32 - Connection interval (in ms) to be used when connecting to peer (If not set 1000ms. is used).
    iFlags : BLE_CONMNGR_FLAG_X - Configured flags for the peer.
    TBleConnMngrPeer:

    Status information about a peer

    aName{} : u8 (32+1) - Advertising name. of the peer.
    aAddr{} : u8 (6) - HW address of the peer.
    iRssi : s8 - RSSI of the peer.
    Advanced informations (for experts only)
    iConnHandle : u8 - BLE connection handle of the peer.
    iState : BLE_CONMNGR_STATE_X - Current state of the peer
    iScanRetries : u8 - Retries executed during the last connection establishment
    iFlags : BLE_CONMNGR_FLAG_X - Currently set flags.
    BleConMngr_Init(iInterface)

    Initializes the BLE Connection Manager

    First, all slots for the peers are reset. The number of available slots depends on the rapidM2M hardware platform used (see BLE_APP_MAX_CONNECTIONS). Then the BLE Scanner App interface of the device is initialized and the timer for the peer handling is started.
    iInterface : s32 - BLE Scanner App interface of the device that should be used (use BLEAPP_INTERFACE_x)
    returns : s32
    OK - If successful
    ERROR - If an error occurs
    BleConMngr_AddFilter(sPeerConfig[TBleConMngrPeerConfig], iFilter, iLength=0, aData{}={0})

    Adds a filter to the passed peer configuration.

    At least one filter must be added to the config for a specific peer before the peer can be registered (using BleConMngr_Register() ) for management by the BLE Connection Manager.
    sPeerConfig : TBleConMngrPeerConfig - Configuration of a specific peer
    iFilter : BLE_CONMNGR_FILTER_TAG_X - Type of the filter that should be added
    iLength : u32 - Size of the filter criterion in bytes
  • Not required for BLE_CONMNGR_FILTER_TAG_NONE and BLE_CONMNGR_FILTER_TAG_RSSI
  • For BLE_CONMNGR_FILTER_TAG_MAC use BLE_CONMNGR_SIZE_ADDRESS
  • aData{} : u8 - Filter criterion. Depends on the type of filter.
    • BLE_CONMNGR_FILTER_TAG_NONE: Not required
    • BLE_CONMNGR_FILTER_TAG_MAC: HW address
    • BLE_CONMNGR_FILTER_TAG_NAME: Peripheral advertising name
    • BLE_CONMNGR_FILTER_TAG_MSD: Manufacturer specific data
    • BLE_CONMNGR_FILTER_TAG_RSSI: Not required
    returns : s32
    >= OK - Byte offset within the filter config, if successful
    ERROR - If there is not enough free memory in the passed peer configuration to add the filter
    BleConMngr_ClearFilters(sPeerConfig[TBleConMngrPeerConfig])

    Clears all filters of the passed peer configuration.

    sPeerConfig : TBleConMngrPeerConfig - Configuration of a specific peer
    BleConMngr_SetupConBehaviour(sPeerConfig[TBleConMngrPeerConfig], iScanInterval, iScanRetries, iConInterval)

    Sets the scan and connect params for the passed peer configuration.

    sPeerConfig : TBleConMngrPeerConfig - Configuration of a specific peer
    iScanInterval : u32 - Scan interval (in sec.) to be used when searching for the peer (If not set 10sec. is used).
    iScanRetries : u32 - Maximum number of rescans before BLE_CONMNGR_EVT_ERROR_NOTFOUND is issued.
    iConInterval : u32 - Connection interval (in ms) to be used when connecting to peer (If not set 1000ms. is used).
    BleConMngr_Register(sPeerConfig[TBleConMngrPeerConfig])

    Registers a peer for management by the BLE Connection Manager. The peer is assigned to a free slot for this purpose.

    sPeerConfig : TBleConMngrPeerConfig - Configuration of the peer to be registered
    Before registering the peer, the filter config for scanning for this peer must be set (using BleConMngr_AddFilter()) and the retry and scan behaviour must be configured (using BleConMngr_SetupConBehaviour())
    returns : s32
    >= OK - Handle for the registered peer, if successful
    ERROR - If all peer slots are already in use
    BleConMngr_Connect(iPeerHandle)

    Initiates the connection to a previously registered peer.

    First it is checked whether the peer is already connected. If this is the case, no further actions take place and BLE_CONMNGR_OK_OP_OMITTED" is returned. Otherwise, the connection establishment starts with the scanning for the peer.
    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    returns : s32
    BLE_CONMNGR_OK_OP_OMITTED - If peer is already connected.
    OK - If command accepted
    ERROR - If invalid peer handle provided.
    Command works in non-blocking mode. Callback function BleConMngr_EvtConnected() is called when:
  • the connection to a peer could be established,
  • the connection to a peer has been disconnected,
  • an existing connection was interrupted (unintentional disconnection) or
  • the peer was not found (max number of retries exceeded) or
  • an error occurred while establishing a connection.
  • BleConMngr_GetPeerValues(iPeerHandle, sPeerValues[TBleConnMngrPeer])

    Get status information about a peer

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    sPeerValues : TBleConnMngrPeer - Status information about the Peer
    returns : s32
    OK - If successful
    ERROR - If invalid peer handle provided.
    BleConMngr_Read(iPeerHandle, iCharHandle)

    Reads data from a specific characteristic of a peer

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    iCharHandle : u16 - Handle of a specific BLE characteristic (Must currently be determined externally)
    returns : s32
    OK - If command accepted
    ERROR - If invalid peer handle provided.
    BLE_CONMNGR_ERROR_OP_INVALID - If reading is not possible in the current state (e.g peer is not connected)
    Command works in non-blocking mode. The callback function BleConMngr_EvtRead(() is called when a block of the requested data has been received. If all requested data has been received or if an error has occurred while reading the data, the callback function BleConMngr_EvtReadResult() is called
    BleConMngr_Write(iPeerHandle, iCharHandle, aData{}, iLength)

    Writes data to a specific characteristic of a peer

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    iCharHandle : u16 - Handle of a specific BLE characteristic (Must currently be determined externally)
    aData{} : u8 - Data buffer to write
    iLength : u8 - Number of bytes to write
    returns : s32
    OK - If command accepted
    ERROR - If invalid peer handle provided.
    BLE_CONMNGR_ERROR_OP_INVALID - If writing is not possible in the current state (e.g peer is not connected)
    Command works in non-blocking mode. Callback function BleConMngr_EvtWriteResult() is called when the data has been successfully written to the characteristic of the pear or an error has occurred while writing the data.
    BleConMngr_Disconnect(iPeerHandle)

    Initiates the disconnection from a peer

    If the peer is already disconnected BLE_CONMNGR_OK_OP_OMITTED" is returned and no further actions take place. Otherwise, a started connection establishment is aborted or the disconnection of the connection is initiated.
    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    returns : s32
    BLE_CONMNGR_OK_OP_OMITTED - If peer is already disconnected.
    OK - If command accepted
    ERROR - If invalid peer handle provided.
    Command works in non-blocking mode. Callback function BleConMngr_EvtConnected() is called when:
  • the connection to a peer could be established,
  • the connection to a peer has been disconnected,
  • an existing connection was interrupted (unintentional disconnection) or
  • the peer was not found (max number of retries exceeded) or
  • an error occurred while establishing a connection.
  • BleConMngr_Unregister(iPeerHandle)

    Unregisters a previously registered peer.

    If the peer is connected, the connection is first disconnected. After that, the slot used for the peer is released.
    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    returns : s32
    OK - If successful
    ERROR - If invalid peer handle provided.
    BleConMngr_Close()

    Closes the BLE Connection Manager

    First, all slots for the peers are reset. Then the timer for the peer handling is stopped and the
    BLE Scanner App interface of the device is disabled.
    returns : s32
    OK - If successful
    ERROR - If an error occurs
    BleConMngr_GetRSSI(iPeerHandle)

    Gets the BLE RSSI of a peer

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    returns : s32
    OK - If command accepted
    ERROR - If invalid peer handle provided.
    BLE_CONMNGR_ERROR_OP_INVALID - If reading is not possible in the current state (e.g peer is not connected)
    Command works in non-blocking mode. The callback function BleConMngr_EvtRSSI(() is called when a block of the requested data has been received. If all requested data has been received or if an error has occurred while reading the data, the callback function BleConMngr_EvtRSSIResult() is called

    Expert

    Advanced Config:
    The settings mentioned in this chapter should only be changed if really required (e.g. memory saving required). To adapt the settings add the following block to the main.dde file.
    /** ---------------------------------------------------------------------------- * ble-con-manager-mt: Advanced Config * ----------------------------------------------------------------------------- * * Use this block to adapt the advanced configuration settings: * */ #define BLE_CONMNGR_SIZE_FILTERCFG 32 // Max. size of the filter config for a peer (min. 2 bytes)
    BLE_CONMNGR_SIZE_FILTERCFG -

    Max. size of the filter config for a specific peer (default 32 bytes)

    Must be set to at least 2 bytes
    BLE_CONMNGR_SIZE_ADDRESS -

    Size (in bytes) of the HW address of a peer

    BLE_CONMNGR_STATE_X:

    States of the Peer

    BLE_CONMNGR_STATE_IDLE - (0) This peer is currently idle (Not connected nor scanned for).
    BLE_CONMNGR_STATE_SCAN - (1) This peer is currently scanned for.
    BLE_CONMNGR_STATE_CONNECT - (2) Connection manager is currently in the process of connecting to this peer.
    BLE_CONMNGR_STATE_CONNECTED - (3) This peer is currently connected.
    BLE_CONMNGR_FLAG_X:

    Flags of the peer.

    BLE_CONMNGR_FLAG_DISCONNECTING - (0x00000001) Marks the peer as "currently disconnecting" (Set when the BleConMngr_Disconnect() function is called).
    BLE_CONMNGR_FLAG_NEEDS_AUTH - (0x00000002) Marks the peer as "extra authentication state is needed after connect".
    BleConMngr_ChgConItv(iPeerHandle, iConInterval = 1000)

    Change BLE connection interval for a peer.

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    iConInterval : u32 - Connection interval to be used.
    returns : s32
    OK - If successful
    ERROR - If invalid peer handle provided.
    BLE_CONMNGR_ERROR_OP_INVALID = f peer is not connected.
    BleConMngr_PeerAuthDone(iPeerHandle)

    Signalize that the authentication procedure is done.

    iPeerHandle : u32 - Handle of a specific peer (Initialized by BleConMngr_Register() )
    returns : s32
    OK - If successful
    ERROR - If invalid peer handle provided.
    BLE_CONMNGR_ERROR_OP_INVALID - If peer needs no authentication (i.e. "BLE_CONMNGR_FLAG_NEEDS_AUTH" not set in the configuration of the peer).

    On this page

    ble-con-manager