2020-03-30 01:44:20 +02:00
|
|
|
/*
|
|
|
|
* NimBLEDevice.cpp
|
|
|
|
*
|
|
|
|
* Created: on Jan 24 2020
|
|
|
|
* Author H2zero
|
2020-05-14 06:03:56 +02:00
|
|
|
*
|
2020-03-30 01:44:20 +02:00
|
|
|
* Originally:
|
|
|
|
*
|
|
|
|
* BLEDevice.cpp
|
|
|
|
*
|
|
|
|
* Created on: Mar 16, 2017
|
|
|
|
* Author: kolban
|
|
|
|
*/
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#if defined(CONFIG_BT_ENABLED)
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#include "nimconfig.h"
|
2020-03-30 01:44:20 +02:00
|
|
|
#include "NimBLEDevice.h"
|
|
|
|
#include "NimBLEUtils.h"
|
|
|
|
|
|
|
|
#include "esp_err.h"
|
|
|
|
#include "esp_bt.h"
|
|
|
|
#include "nvs_flash.h"
|
|
|
|
#include "esp_nimble_hci.h"
|
|
|
|
#include "nimble/nimble_port.h"
|
|
|
|
#include "nimble/nimble_port_freertos.h"
|
|
|
|
#include "host/ble_hs.h"
|
|
|
|
#include "host/util/util.h"
|
|
|
|
#include "services/gap/ble_svc_gap.h"
|
|
|
|
#include "services/gatt/ble_svc_gatt.h"
|
|
|
|
|
|
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
|
|
#include "esp32-hal-bt.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "NimBLELog.h"
|
|
|
|
|
|
|
|
static const char* LOG_TAG = "NimBLEDevice";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Singletons for the NimBLEDevice.
|
|
|
|
*/
|
2020-05-30 02:26:41 +02:00
|
|
|
static bool initialized = false;
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEScan* NimBLEDevice::m_pScan = nullptr;
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
2020-03-30 01:44:20 +02:00
|
|
|
uint32_t NimBLEDevice::m_passkey = 123456;
|
|
|
|
bool NimBLEDevice::m_synced = false;
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr;
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
gap_event_handler NimBLEDevice::m_customGapHandler = nullptr;
|
|
|
|
ble_gap_event_listener NimBLEDevice::m_listener;
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
2020-03-30 01:44:20 +02:00
|
|
|
std::list <NimBLEClient*> NimBLEDevice::m_cList;
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
2020-03-30 01:44:20 +02:00
|
|
|
std::list <NimBLEAddress> NimBLEDevice::m_ignoreList;
|
|
|
|
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create a new instance of a server.
|
|
|
|
* @return A new instance of the server.
|
|
|
|
*/
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
2020-03-30 01:44:20 +02:00
|
|
|
/* STATIC */ NimBLEServer* NimBLEDevice::createServer() {
|
|
|
|
if(NimBLEDevice::m_pServer == nullptr) {
|
|
|
|
NimBLEDevice::m_pServer = new NimBLEServer();
|
2020-05-14 06:03:56 +02:00
|
|
|
ble_gatts_reset();
|
2020-03-30 01:44:20 +02:00
|
|
|
ble_svc_gap_init();
|
|
|
|
ble_svc_gatt_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_pServer;
|
|
|
|
} // createServer
|
2020-05-14 20:59:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the instance of the server.
|
|
|
|
* @return A pointer to the server instance.
|
|
|
|
*/
|
|
|
|
/* STATIC */ NimBLEServer* NimBLEDevice::getServer() {
|
|
|
|
return m_pServer;
|
|
|
|
} // getServer
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEAdvertising* NimBLEDevice::getAdvertising() {
|
2020-05-14 06:03:56 +02:00
|
|
|
if(m_bleAdvertising == nullptr) {
|
|
|
|
m_bleAdvertising = new NimBLEAdvertising();
|
|
|
|
}
|
|
|
|
return m_bleAdvertising;
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEDevice::startAdvertising() {
|
2020-05-14 06:03:56 +02:00
|
|
|
getAdvertising()->start();
|
2020-03-30 01:44:20 +02:00
|
|
|
} // startAdvertising
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEDevice::stopAdvertising() {
|
|
|
|
getAdvertising()->stop();
|
|
|
|
} // stopAdvertising
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Retrieve the Scan object that we use for scanning.
|
|
|
|
* @return The scanning object reference. This is a singleton object. The caller should not
|
|
|
|
* try and release/delete it.
|
|
|
|
*/
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
2020-03-30 01:44:20 +02:00
|
|
|
/* STATIC */ NimBLEScan* NimBLEDevice::getScan() {
|
|
|
|
if (m_pScan == nullptr) {
|
|
|
|
m_pScan = new NimBLEScan();
|
|
|
|
}
|
|
|
|
return m_pScan;
|
|
|
|
} // getScan
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Creates a new client object and maintains a list of all client objects
|
2020-05-14 06:03:56 +02:00
|
|
|
* each client can connect to 1 peripheral device.
|
2020-03-30 01:44:20 +02:00
|
|
|
* @return A reference to the new client object.
|
|
|
|
*/
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
2020-03-30 01:44:20 +02:00
|
|
|
/* STATIC */ NimBLEClient* NimBLEDevice::createClient() {
|
|
|
|
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
|
2020-05-14 06:03:56 +02:00
|
|
|
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
|
2020-03-30 01:44:20 +02:00
|
|
|
NIMBLE_MAX_CONNECTIONS);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEClient* pClient = new NimBLEClient();
|
|
|
|
m_cList.push_back(pClient);
|
|
|
|
|
|
|
|
return pClient;
|
|
|
|
} // createClient
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Delete the client object and remove it from the list.
|
|
|
|
* Check if it is connected or trying to connect and close/stop it first.
|
|
|
|
* @param [in] Pointer to the client object.
|
|
|
|
*/
|
|
|
|
/* STATIC */ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) {
|
|
|
|
if(pClient == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
if(pClient->m_isConnected) {
|
|
|
|
if (pClient->disconnect() != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while(pClient->m_isConnected) {
|
|
|
|
vTaskDelay(1);
|
|
|
|
}
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
if(pClient->m_waitingToConnect) {
|
|
|
|
if(ble_gap_conn_cancel() != 0){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while(pClient->m_waitingToConnect) {
|
|
|
|
vTaskDelay(1);
|
|
|
|
}
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
m_cList.remove(pClient);
|
|
|
|
delete pClient;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return true;
|
|
|
|
} // deleteClient
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief get the list of clients.
|
|
|
|
* @return a pointer to the list of clients.
|
|
|
|
*/
|
|
|
|
/* STATIC */std::list<NimBLEClient*>* NimBLEDevice::getClientList() {
|
|
|
|
return &m_cList;
|
|
|
|
} // getClientList
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief get the size of the list of clients.
|
|
|
|
* @return a pointer to the list of clients.
|
|
|
|
*/
|
|
|
|
/* STATIC */size_t NimBLEDevice::getClientListSize() {
|
|
|
|
return m_cList.size();
|
|
|
|
} // getClientList
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get a reference to a client by connection ID.
|
|
|
|
* @param [in] The client connection ID to search for.
|
|
|
|
* @return A reference pointer to the client with the spcified connection ID.
|
|
|
|
*/
|
|
|
|
/* STATIC */NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
|
|
|
|
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
|
|
|
|
if((*it)->getConnId() == conn_id) {
|
|
|
|
return (*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(0);
|
|
|
|
return nullptr;
|
|
|
|
} // getClientByID
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get a reference to a client by peer address.
|
|
|
|
* @param [in] a NimBLEAddress of the peer to search for.
|
|
|
|
* @return A reference pointer to the client with the peer address.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) {
|
2020-03-30 01:44:20 +02:00
|
|
|
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
|
|
|
|
if((*it)->getPeerAddress().equals(peer_addr)) {
|
|
|
|
return (*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
} // getClientPeerAddress
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Finds the first disconnected client in the list.
|
|
|
|
* @return A reference pointer to the first client that is not connected to a peer.
|
|
|
|
*/
|
|
|
|
/* STATIC */NimBLEClient* NimBLEDevice::getDisconnectedClient() {
|
|
|
|
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
|
|
|
|
if(!(*it)->isConnected()) {
|
|
|
|
return (*it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
} // getDisconnectedClient
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the transmission power.
|
|
|
|
* The power level can be one of:
|
2020-05-14 06:03:56 +02:00
|
|
|
* * ESP_PWR_LVL_N12 = 0, !< Corresponding to -12dbm
|
|
|
|
* * ESP_PWR_LVL_N9 = 1, !< Corresponding to -9dbm
|
|
|
|
* * ESP_PWR_LVL_N6 = 2, !< Corresponding to -6dbm
|
|
|
|
* * ESP_PWR_LVL_N3 = 3, !< Corresponding to -3dbm
|
|
|
|
* * ESP_PWR_LVL_N0 = 4, !< Corresponding to 0dbm
|
|
|
|
* * ESP_PWR_LVL_P3 = 5, !< Corresponding to +3dbm
|
|
|
|
* * ESP_PWR_LVL_P6 = 6, !< Corresponding to +6dbm
|
|
|
|
* * ESP_PWR_LVL_P9 = 7, !< Corresponding to +9dbm
|
2020-03-30 01:44:20 +02:00
|
|
|
* @param [in] powerLevel.
|
|
|
|
*/
|
2020-04-14 03:13:51 +02:00
|
|
|
/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType);
|
|
|
|
esp_err_t errRc = esp_ble_tx_power_set(powerType, powerLevel);
|
2020-03-30 01:44:20 +02:00
|
|
|
if (errRc != ESP_OK) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d", errRc);
|
2020-04-14 03:13:51 +02:00
|
|
|
}
|
2020-03-30 01:44:20 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< setPower");
|
|
|
|
} // setPower
|
|
|
|
|
|
|
|
|
2020-04-14 03:13:51 +02:00
|
|
|
/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) {
|
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
switch(esp_ble_tx_power_get(powerType)) {
|
|
|
|
case ESP_PWR_LVL_N12:
|
|
|
|
return -12;
|
|
|
|
case ESP_PWR_LVL_N9:
|
|
|
|
return -9;
|
|
|
|
case ESP_PWR_LVL_N6:
|
|
|
|
return -6;
|
|
|
|
case ESP_PWR_LVL_N3:
|
|
|
|
return -6;
|
|
|
|
case ESP_PWR_LVL_N0:
|
|
|
|
return 0;
|
|
|
|
case ESP_PWR_LVL_P3:
|
|
|
|
return 3;
|
|
|
|
case ESP_PWR_LVL_P6:
|
|
|
|
return 6;
|
|
|
|
case ESP_PWR_LVL_P9:
|
|
|
|
return 9;
|
|
|
|
default:
|
|
|
|
return BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
|
|
|
}
|
2020-04-14 03:13:51 +02:00
|
|
|
} // setPower
|
|
|
|
|
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
/**
|
|
|
|
* @brief Get our device address.
|
|
|
|
* @return A NimBLEAddress object of our public address if we have one,
|
|
|
|
* if not then our current random address.
|
|
|
|
*/
|
|
|
|
/* STATIC*/ NimBLEAddress NimBLEDevice::getAddress() {
|
|
|
|
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
|
|
|
|
//ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
|
|
|
|
addr.type = BLE_ADDR_RANDOM;
|
|
|
|
ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr.val, NULL);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return NimBLEAddress(addr);
|
|
|
|
} // getAddress
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Return a string representation of the address of this device.
|
|
|
|
* @return A string representation of this device address.
|
|
|
|
*/
|
|
|
|
/* STATIC */ std::string NimBLEDevice::toString() {
|
|
|
|
return getAddress().toString();
|
|
|
|
} // toString
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Setup local mtu that will be used to negotiate mtu during request from client peer
|
|
|
|
* @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to
|
|
|
|
* BLE_ATT_MTU_MAX = 527
|
|
|
|
*/
|
|
|
|
/* STATIC */int NimBLEDevice::setMTU(uint16_t mtu) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
int rc = ble_att_set_preferred_mtu(mtu);
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "Could not set local mtu value to: %d", mtu);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< setLocalMTU");
|
|
|
|
return rc;
|
|
|
|
} // setMTU
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get local MTU value set.
|
|
|
|
*/
|
|
|
|
/* STATIC */uint16_t NimBLEDevice::getMTU() {
|
|
|
|
return ble_att_preferred_mtu();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Host reset, we pass the message so we don't make calls until resynced.
|
|
|
|
*/
|
|
|
|
/* STATIC */ void NimBLEDevice::onReset(int reason)
|
|
|
|
{
|
|
|
|
if(!m_synced) {
|
|
|
|
return;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
m_synced = false;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
2020-03-30 01:44:20 +02:00
|
|
|
if(m_pScan != nullptr) {
|
|
|
|
m_pScan->onHostReset();
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Not needed
|
2020-03-30 01:44:20 +02:00
|
|
|
if(m_pServer != nullptr) {
|
|
|
|
m_pServer->onHostReset();
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
|
|
|
|
(*it)->onHostReset();
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
2020-03-30 01:44:20 +02:00
|
|
|
if(m_bleAdvertising != nullptr) {
|
|
|
|
m_bleAdvertising->onHostReset();
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
|
2020-03-30 01:44:20 +02:00
|
|
|
NimBLEUtils::returnCodeToString(reason));
|
|
|
|
} // onReset
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Host resynced with controller, all clear to make calls.
|
|
|
|
*/
|
|
|
|
/* STATIC */ void NimBLEDevice::onSync(void)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "NimBle host synced.");
|
|
|
|
// This check is needed due to potentially being called multiple times in succession
|
|
|
|
// If this happens, the call to scan start may get stuck or cause an advertising fault.
|
|
|
|
if(m_synced) {
|
|
|
|
return;
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
/* Make sure we have proper identity address set (public preferred) */
|
|
|
|
int rc = ble_hs_util_ensure_addr(0);
|
|
|
|
assert(rc == 0);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
m_synced = true;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-05-03 21:50:49 +02:00
|
|
|
if(initialized) {
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
|
2020-05-03 21:50:49 +02:00
|
|
|
if(m_pScan != nullptr) {
|
|
|
|
// Restart scanning with the last values sent, allow to clear results.
|
|
|
|
m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
2020-05-03 21:50:49 +02:00
|
|
|
|
2020-05-14 06:03:56 +02:00
|
|
|
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
|
2020-05-03 21:50:49 +02:00
|
|
|
if(m_bleAdvertising != nullptr) {
|
|
|
|
// Restart advertisng, parameters should already be set.
|
|
|
|
m_bleAdvertising->start();
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
#endif
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
} // onSync
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The main host task.
|
|
|
|
*/
|
|
|
|
/* STATIC */ void NimBLEDevice::host_task(void *param)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "BLE Host Task Started");
|
|
|
|
/* This function will return only when nimble_port_stop() is executed */
|
|
|
|
nimble_port_run();
|
|
|
|
|
|
|
|
nimble_port_freertos_deinit();
|
|
|
|
} // host_task
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Initialize the %BLE environment.
|
|
|
|
* @param deviceName The device name of the device.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
/* STATIC */ void NimBLEDevice::init(const std::string &deviceName) {
|
2020-03-30 01:44:20 +02:00
|
|
|
if(!initialized){
|
|
|
|
int rc=0;
|
|
|
|
esp_err_t errRc = ESP_OK;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
#ifdef ARDUINO_ARCH_ESP32
|
|
|
|
// make sure the linker includes esp32-hal-bt.c so ardruino init doesn't release BLE memory.
|
|
|
|
btStarted();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
errRc = nvs_flash_init();
|
2020-05-03 21:50:49 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
if (errRc == ESP_ERR_NVS_NO_FREE_PAGES || errRc == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
|
|
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
|
|
|
errRc = nvs_flash_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(errRc);
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
nimble_port_init();
|
2020-05-14 06:03:56 +02:00
|
|
|
|
|
|
|
// Setup callbacks for host events
|
2020-03-30 01:44:20 +02:00
|
|
|
ble_hs_cfg.reset_cb = NimBLEDevice::onReset;
|
|
|
|
ble_hs_cfg.sync_cb = NimBLEDevice::onSync;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
// Set initial security capabilities
|
2020-05-14 06:03:56 +02:00
|
|
|
ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT;
|
2020-03-30 01:44:20 +02:00
|
|
|
ble_hs_cfg.sm_bonding = 0;
|
|
|
|
ble_hs_cfg.sm_mitm = 0;
|
|
|
|
ble_hs_cfg.sm_sc = 1;
|
|
|
|
ble_hs_cfg.sm_our_key_dist = 1;
|
2020-04-23 23:17:09 +02:00
|
|
|
ble_hs_cfg.sm_their_key_dist = 3;
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /*TODO: Implement handler for this*/
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
// Set the device name.
|
|
|
|
rc = ble_svc_gap_device_name_set(deviceName.c_str());
|
|
|
|
assert(rc == 0);
|
|
|
|
|
|
|
|
ble_store_config_init();
|
2020-05-03 21:50:49 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
nimble_port_freertos_init(NimBLEDevice::host_task);
|
|
|
|
}
|
|
|
|
// Wait for host and controller to sync before returning and accepting new tasks
|
|
|
|
while(!m_synced){
|
|
|
|
vTaskDelay(1 / portTICK_PERIOD_MS);
|
|
|
|
}
|
2020-05-03 21:50:49 +02:00
|
|
|
|
|
|
|
initialized = true; // Set the initialization flag to ensure we are only initialized once.
|
2020-03-30 01:44:20 +02:00
|
|
|
} // init
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Shutdown the NimBLE stack/controller.
|
|
|
|
*/
|
|
|
|
/* STATIC */ void NimBLEDevice::deinit() {
|
|
|
|
int ret = nimble_port_stop();
|
|
|
|
if (ret == 0) {
|
|
|
|
nimble_port_deinit();
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
ret = esp_nimble_hci_and_controller_deinit();
|
|
|
|
if (ret != ESP_OK) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
initialized = false;
|
2020-05-03 21:50:49 +02:00
|
|
|
m_synced = false;
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
|
|
|
} // deinit
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Check if the initialization is complete.
|
|
|
|
*/
|
|
|
|
bool NimBLEDevice::getInitialized() {
|
|
|
|
return initialized;
|
|
|
|
} // getInitialized
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the authorization mode for this device.
|
|
|
|
* @param bonding, if true we allow bonding, false no bonding will be performed.
|
|
|
|
* @param mitm, if true we are capable of man in the middle protection, false if not.
|
|
|
|
* @param sc, if true we will perform secure connection pairing, false we will use legacy pairing.
|
|
|
|
*/
|
|
|
|
/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc);
|
|
|
|
ble_hs_cfg.sm_bonding = bonding;
|
|
|
|
ble_hs_cfg.sm_mitm = mitm;
|
|
|
|
ble_hs_cfg.sm_sc = sc;
|
|
|
|
} // setSecurityAuth
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the authorization mode for this device.
|
|
|
|
* @param A bitmap indicating what modes are supported.
|
|
|
|
* The bits are defined as follows:
|
2020-05-14 06:03:56 +02:00
|
|
|
** 0x01 BLE_SM_PAIR_AUTHREQ_BOND
|
|
|
|
** 0x04 BLE_SM_PAIR_AUTHREQ_MITM
|
|
|
|
** 0x08 BLE_SM_PAIR_AUTHREQ_SC
|
|
|
|
** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
|
2020-03-30 01:44:20 +02:00
|
|
|
** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only.
|
2020-05-14 06:03:56 +02:00
|
|
|
*/
|
2020-03-30 01:44:20 +02:00
|
|
|
/*STATIC*/void NimBLEDevice::setSecurityAuth(uint8_t auth_req) {
|
|
|
|
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
|
|
|
|
(auth_req & BLE_SM_PAIR_AUTHREQ_MITM)>0,
|
|
|
|
(auth_req & BLE_SM_PAIR_AUTHREQ_SC)>0);
|
|
|
|
} // setSecurityAuth
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the Input/Output capabilities of this device.
|
|
|
|
* @param One of the following:
|
|
|
|
** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
|
|
|
|
** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
|
2020-05-14 06:03:56 +02:00
|
|
|
** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
|
|
|
|
** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
|
2020-03-30 01:44:20 +02:00
|
|
|
** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
|
|
|
|
*/
|
|
|
|
/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) {
|
|
|
|
ble_hs_cfg.sm_io_cap = iocap;
|
|
|
|
} // setSecurityIOCap
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief If we are the initiator of the security procedure this sets the keys we will distribute.
|
|
|
|
* @param A bitmap indicating which keys to distribute during pairing.
|
2020-05-14 06:03:56 +02:00
|
|
|
* The bits are defined as follows:
|
2020-03-30 01:44:20 +02:00
|
|
|
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
|
|
|
|
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
|
|
|
|
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
|
|
|
|
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
|
|
|
|
*/
|
|
|
|
/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) {
|
|
|
|
ble_hs_cfg.sm_our_key_dist = init_key;
|
|
|
|
} // setsSecurityInitKey
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the keys we are willing to accept during pairing.
|
|
|
|
* @param A bitmap indicating which keys to accept during pairing.
|
2020-05-14 06:03:56 +02:00
|
|
|
* The bits are defined as follows:
|
2020-03-30 01:44:20 +02:00
|
|
|
** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
|
|
|
|
** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
|
|
|
|
** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
|
|
|
|
** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
|
|
|
|
*/
|
|
|
|
/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) {
|
|
|
|
ble_hs_cfg.sm_their_key_dist = init_key;
|
|
|
|
} // setsSecurityRespKey
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the passkey for pairing.
|
|
|
|
*/
|
|
|
|
/*STATIC*/void NimBLEDevice::setSecurityPasskey(uint32_t pin) {
|
|
|
|
m_passkey = pin;
|
|
|
|
} // setSecurityPasskey
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get the passkey for pairing.
|
|
|
|
*/
|
|
|
|
/*STATIC*/uint32_t NimBLEDevice::getSecurityPasskey() {
|
|
|
|
return m_passkey;
|
|
|
|
} // getSecurityPasskey
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
|
|
|
|
* @param [in] cllbacks Pointer to NimBLESecurityCallbacks class
|
|
|
|
*/
|
|
|
|
void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
|
|
|
|
NimBLEDevice::m_securityCallbacks = callbacks;
|
|
|
|
} // setSecurityCallbacks
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Start the connection securing and authorization for this connection.
|
|
|
|
* @param Connection id of the client.
|
|
|
|
* @returns host return code 0 if success.
|
|
|
|
*/
|
|
|
|
/* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) {
|
|
|
|
/* if(m_securityCallbacks != nullptr) {
|
2020-05-14 06:03:56 +02:00
|
|
|
m_securityCallbacks->onSecurityRequest();
|
2020-03-30 01:44:20 +02:00
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
*/
|
2020-03-30 01:44:20 +02:00
|
|
|
int rc = ble_gap_security_initiate(conn_id);
|
|
|
|
if(rc != 0){
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "ble_gap_security_initiate: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
|
|
|
|
}
|
2020-05-14 06:03:56 +02:00
|
|
|
|
2020-03-30 01:44:20 +02:00
|
|
|
return rc;
|
|
|
|
} // startSecurity
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Check if the device address is on our ignore list.
|
|
|
|
* @return True if ignoring.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
/*STATIC*/ bool NimBLEDevice::isIgnored(const NimBLEAddress &address) {
|
2020-03-30 01:44:20 +02:00
|
|
|
for(auto &it : m_ignoreList) {
|
|
|
|
if(it.equals(address)){
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Add a device to the ignore list.
|
|
|
|
* @param Address of the device we want to ignore.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
/*STATIC*/ void NimBLEDevice::addIgnored(const NimBLEAddress &address) {
|
2020-03-30 01:44:20 +02:00
|
|
|
m_ignoreList.push_back(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Remove a device from the ignore list.
|
|
|
|
* @param Address of the device we want to remove from the list.
|
|
|
|
*/
|
2020-05-10 15:21:46 +02:00
|
|
|
/*STATIC*/void NimBLEDevice::removeIgnored(const NimBLEAddress &address) {
|
2020-03-30 01:44:20 +02:00
|
|
|
for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) {
|
|
|
|
if((*it).equals(address)){
|
|
|
|
m_ignoreList.erase(it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set a custom callback for gap events.
|
|
|
|
*/
|
|
|
|
void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
|
|
|
|
m_customGapHandler = handler;
|
|
|
|
int rc = ble_gap_event_listener_register(&m_listener, m_customGapHandler, NULL);
|
|
|
|
if(rc == BLE_HS_EALREADY){
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events.");
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
assert(rc == 0);
|
|
|
|
}
|
|
|
|
} // setCustomGapHandler
|
|
|
|
|
|
|
|
|
|
|
|
#endif // CONFIG_BT_ENABLED
|