mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2024-11-23 21:50:55 +01:00
efa48c0d57
* Fix typos * Update docs
921 lines
31 KiB
C++
921 lines
31 KiB
C++
/*
|
|
* NimBLEServer.cpp
|
|
*
|
|
* Created: on March 2, 2020
|
|
* Author H2zero
|
|
*
|
|
* Originally:
|
|
*
|
|
* BLEServer.cpp
|
|
*
|
|
* Created on: Apr 16, 2017
|
|
* Author: kolban
|
|
*/
|
|
|
|
#include "nimconfig.h"
|
|
#if defined(CONFIG_BT_ENABLED) && defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
|
|
|
|
#include "NimBLEServer.h"
|
|
#include "NimBLEDevice.h"
|
|
#include "NimBLELog.h"
|
|
|
|
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
|
#include "services/gap/ble_svc_gap.h"
|
|
#include "services/gatt/ble_svc_gatt.h"
|
|
#else
|
|
#include "nimble/nimble/host/services/gap/include/services/gap/ble_svc_gap.h"
|
|
#include "nimble/nimble/host/services/gatt/include/services/gatt/ble_svc_gatt.h"
|
|
#endif
|
|
|
|
static const char* LOG_TAG = "NimBLEServer";
|
|
static NimBLEServerCallbacks defaultCallbacks;
|
|
|
|
|
|
/**
|
|
* @brief Construct a %BLE Server
|
|
*
|
|
* This class is not designed to be individually instantiated. Instead one should create a server by asking
|
|
* the NimBLEDevice class.
|
|
*/
|
|
NimBLEServer::NimBLEServer() {
|
|
memset(m_indWait, BLE_HS_CONN_HANDLE_NONE, sizeof(m_indWait));
|
|
// m_svcChgChrHdl = 0xffff; // Future Use
|
|
m_pServerCallbacks = &defaultCallbacks;
|
|
m_gattsStarted = false;
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
|
m_advertiseOnDisconnect = true;
|
|
#endif
|
|
m_svcChanged = false;
|
|
m_deleteCallbacks = true;
|
|
} // NimBLEServer
|
|
|
|
|
|
/**
|
|
* @brief Destructor: frees all resources / attributes created.
|
|
*/
|
|
NimBLEServer::~NimBLEServer() {
|
|
for(auto &it : m_svcVec) {
|
|
delete it;
|
|
}
|
|
|
|
if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) {
|
|
delete m_pServerCallbacks;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Create a %BLE Service.
|
|
* @param [in] uuid The UUID of the new service.
|
|
* @return A reference to the new service object.
|
|
*/
|
|
NimBLEService* NimBLEServer::createService(const char* uuid) {
|
|
return createService(NimBLEUUID(uuid));
|
|
} // createService
|
|
|
|
|
|
/**
|
|
* @brief Create a %BLE Service.
|
|
* @param [in] uuid The UUID of the new service.
|
|
* @return A reference to the new service object.
|
|
*/
|
|
NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid) {
|
|
NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
|
|
|
|
// Check that a service with the supplied UUID does not already exist.
|
|
if(getServiceByUUID(uuid) != nullptr) {
|
|
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
|
|
std::string(uuid).c_str());
|
|
}
|
|
|
|
NimBLEService* pService = new NimBLEService(uuid);
|
|
m_svcVec.push_back(pService);
|
|
serviceChanged();
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< createService");
|
|
return pService;
|
|
} // createService
|
|
|
|
|
|
/**
|
|
* @brief Get a %BLE Service by its UUID
|
|
* @param [in] uuid The UUID of the service.
|
|
* @param instanceId The index of the service to return (used when multiple services have the same UUID).
|
|
* @return A pointer to the service object or nullptr if not found.
|
|
*/
|
|
NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid, uint16_t instanceId) {
|
|
return getServiceByUUID(NimBLEUUID(uuid), instanceId);
|
|
} // getServiceByUUID
|
|
|
|
|
|
/**
|
|
* @brief Get a %BLE Service by its UUID
|
|
* @param [in] uuid The UUID of the service.
|
|
* @param instanceId The index of the service to return (used when multiple services have the same UUID).
|
|
* @return A pointer to the service object or nullptr if not found.
|
|
*/
|
|
NimBLEService* NimBLEServer::getServiceByUUID(const NimBLEUUID &uuid, uint16_t instanceId) {
|
|
uint16_t position = 0;
|
|
for (auto &it : m_svcVec) {
|
|
if (it->getUUID() == uuid) {
|
|
if (position == instanceId){
|
|
return it;
|
|
}
|
|
position++;
|
|
}
|
|
}
|
|
return nullptr;
|
|
} // getServiceByUUID
|
|
|
|
/**
|
|
* @brief Get a %BLE Service by its handle
|
|
* @param handle The handle of the service.
|
|
* @return A pointer to the service object or nullptr if not found.
|
|
*/
|
|
NimBLEService *NimBLEServer::getServiceByHandle(uint16_t handle) {
|
|
for (auto &it : m_svcVec) {
|
|
if (it->getHandle() == handle) {
|
|
return it;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
#if CONFIG_BT_NIMBLE_EXT_ADV
|
|
/**
|
|
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
|
* @return An advertising object.
|
|
*/
|
|
NimBLEExtAdvertising* NimBLEServer::getAdvertising() {
|
|
return NimBLEDevice::getAdvertising();
|
|
} // getAdvertising
|
|
#endif
|
|
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
|
/**
|
|
* @brief Retrieve the advertising object that can be used to advertise the existence of the server.
|
|
* @return An advertising object.
|
|
*/
|
|
NimBLEAdvertising* NimBLEServer::getAdvertising() {
|
|
return NimBLEDevice::getAdvertising();
|
|
} // getAdvertising
|
|
#endif
|
|
|
|
/**
|
|
* @brief Sends a service changed notification and resets the GATT server.
|
|
*/
|
|
void NimBLEServer::serviceChanged() {
|
|
if(m_gattsStarted) {
|
|
m_svcChanged = true;
|
|
ble_svc_gatt_changed(0x0001, 0xffff);
|
|
resetGATT();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Start the GATT server. Required to be called after setup of all
|
|
* services and characteristics / descriptors for the NimBLE host to register them.
|
|
*/
|
|
void NimBLEServer::start() {
|
|
if(m_gattsStarted) {
|
|
NIMBLE_LOGW(LOG_TAG, "Gatt server already started");
|
|
return;
|
|
}
|
|
|
|
int rc = ble_gatts_start();
|
|
if (rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc,
|
|
NimBLEUtils::returnCodeToString(rc));
|
|
abort();
|
|
}
|
|
|
|
#if CONFIG_NIMBLE_CPP_LOG_LEVEL >= 4
|
|
ble_gatts_show_local();
|
|
#endif
|
|
/*** Future use ***
|
|
* TODO: implement service changed handling
|
|
|
|
ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801};
|
|
ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05};
|
|
|
|
rc = ble_gatts_find_chr(&svc.u, &chr.u, NULL, &m_svcChgChrHdl);
|
|
if(rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc,
|
|
NimBLEUtils::returnCodeToString(rc));
|
|
abort();
|
|
}
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
|
|
*/
|
|
// Get the assigned service handles and build a vector of characteristics
|
|
// with Notify / Indicate capabilities for event handling
|
|
for(auto &svc : m_svcVec) {
|
|
if(svc->m_removed == 0) {
|
|
rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
|
|
if(rc != 0) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
for(auto &chr : svc->m_chrVec) {
|
|
// if Notify / Indicate is enabled but we didn't create the descriptor
|
|
// we do it now.
|
|
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
|
|
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
|
|
m_notifyChrVec.push_back(chr);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_gattsStarted = true;
|
|
} // start
|
|
|
|
|
|
/**
|
|
* @brief Disconnect the specified client with optional reason.
|
|
* @param [in] connId Connection Id of the client to disconnect.
|
|
* @param [in] reason code for disconnecting.
|
|
* @return NimBLE host return code.
|
|
*/
|
|
int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
|
|
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
|
|
|
|
int rc = ble_gap_terminate(connId, reason);
|
|
if(rc != 0){
|
|
NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
|
|
NimBLEUtils::returnCodeToString(rc));
|
|
}
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
|
|
return rc;
|
|
} // disconnect
|
|
|
|
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV || defined(_DOXYGEN_)
|
|
/**
|
|
* @brief Set the server to automatically start advertising when a client disconnects.
|
|
* @param [in] aod true == advertise, false == don't advertise.
|
|
*/
|
|
void NimBLEServer::advertiseOnDisconnect(bool aod) {
|
|
m_advertiseOnDisconnect = aod;
|
|
} // advertiseOnDisconnect
|
|
#endif
|
|
|
|
/**
|
|
* @brief Return the number of connected clients.
|
|
* @return The number of connected clients.
|
|
*/
|
|
size_t NimBLEServer::getConnectedCount() {
|
|
return m_connectedPeersVec.size();
|
|
} // getConnectedCount
|
|
|
|
|
|
/**
|
|
* @brief Get the vector of the connected client ID's.
|
|
*/
|
|
std::vector<uint16_t> NimBLEServer::getPeerDevices() {
|
|
return m_connectedPeersVec;
|
|
} // getPeerDevices
|
|
|
|
|
|
/**
|
|
* @brief Get the connection information of a connected peer by vector index.
|
|
* @param [in] index The vector index of the peer.
|
|
*/
|
|
NimBLEConnInfo NimBLEServer::getPeerInfo(size_t index) {
|
|
if (index >= m_connectedPeersVec.size()) {
|
|
NIMBLE_LOGE(LOG_TAG, "No peer at index %u", index);
|
|
return NimBLEConnInfo();
|
|
}
|
|
|
|
return getPeerIDInfo(m_connectedPeersVec[index]);
|
|
} // getPeerInfo
|
|
|
|
|
|
/**
|
|
* @brief Get the connection information of a connected peer by address.
|
|
* @param [in] address The address of the peer.
|
|
*/
|
|
NimBLEConnInfo NimBLEServer::getPeerInfo(const NimBLEAddress& address) {
|
|
ble_addr_t peerAddr;
|
|
memcpy(&peerAddr.val, address.getNative(),6);
|
|
peerAddr.type = address.getType();
|
|
|
|
NimBLEConnInfo peerInfo;
|
|
int rc = ble_gap_conn_find_by_addr(&peerAddr, &peerInfo.m_desc);
|
|
if (rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
|
|
}
|
|
|
|
return peerInfo;
|
|
} // getPeerInfo
|
|
|
|
|
|
/**
|
|
* @brief Get the connection information of a connected peer by connection ID.
|
|
* @param [in] id The connection id of the peer.
|
|
*/
|
|
NimBLEConnInfo NimBLEServer::getPeerIDInfo(uint16_t id) {
|
|
NimBLEConnInfo peerInfo;
|
|
|
|
int rc = ble_gap_conn_find(id, &peerInfo.m_desc);
|
|
if (rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "Peer info not found");
|
|
}
|
|
|
|
return peerInfo;
|
|
} // getPeerIDInfo
|
|
|
|
|
|
/**
|
|
* @brief Handle a GATT Server Event.
|
|
*
|
|
* @param [in] event
|
|
* @param [in] gatts_if
|
|
* @param [in] param
|
|
*
|
|
*/
|
|
/*STATIC*/
|
|
int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) {
|
|
NimBLEServer* server = NimBLEDevice::getServer();
|
|
NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s",
|
|
NimBLEUtils::gapEventToString(event->type));
|
|
int rc = 0;
|
|
struct ble_gap_conn_desc desc;
|
|
|
|
switch(event->type) {
|
|
|
|
case BLE_GAP_EVENT_CONNECT: {
|
|
if (event->connect.status != 0) {
|
|
/* Connection failed; resume advertising */
|
|
NIMBLE_LOGE(LOG_TAG, "Connection failed");
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
|
NimBLEDevice::startAdvertising();
|
|
#endif
|
|
}
|
|
else {
|
|
server->m_connectedPeersVec.push_back(event->connect.conn_handle);
|
|
|
|
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
|
if (rc != 0) {
|
|
return 0;
|
|
}
|
|
|
|
server->m_pServerCallbacks->onConnect(server);
|
|
server->m_pServerCallbacks->onConnect(server, &desc);
|
|
}
|
|
|
|
return 0;
|
|
} // BLE_GAP_EVENT_CONNECT
|
|
|
|
|
|
case BLE_GAP_EVENT_DISCONNECT: {
|
|
// If Host reset tell the device now before returning to prevent
|
|
// any errors caused by calling host functions before resync.
|
|
switch(event->disconnect.reason) {
|
|
case BLE_HS_ETIMEOUT_HCI:
|
|
case BLE_HS_EOS:
|
|
case BLE_HS_ECONTROLLER:
|
|
case BLE_HS_ENOTSYNCED:
|
|
NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
|
|
NimBLEDevice::onReset(event->disconnect.reason);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
server->m_connectedPeersVec.erase(std::remove(server->m_connectedPeersVec.begin(),
|
|
server->m_connectedPeersVec.end(),
|
|
event->disconnect.conn.conn_handle),
|
|
server->m_connectedPeersVec.end());
|
|
|
|
if(server->m_svcChanged) {
|
|
server->resetGATT();
|
|
}
|
|
|
|
server->m_pServerCallbacks->onDisconnect(server);
|
|
server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
|
|
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
|
if(server->m_advertiseOnDisconnect) {
|
|
server->startAdvertising();
|
|
}
|
|
#endif
|
|
return 0;
|
|
} // BLE_GAP_EVENT_DISCONNECT
|
|
|
|
case BLE_GAP_EVENT_SUBSCRIBE: {
|
|
NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s",
|
|
event->subscribe.attr_handle,
|
|
(event->subscribe.cur_notify ? "true":"false"));
|
|
|
|
for(auto &it : server->m_notifyChrVec) {
|
|
if(it->getHandle() == event->subscribe.attr_handle) {
|
|
if((it->getProperties() & BLE_GATT_CHR_F_READ_AUTHEN) ||
|
|
(it->getProperties() & BLE_GATT_CHR_F_READ_AUTHOR) ||
|
|
(it->getProperties() & BLE_GATT_CHR_F_READ_ENC))
|
|
{
|
|
rc = ble_gap_conn_find(event->subscribe.conn_handle, &desc);
|
|
if (rc != 0) {
|
|
break;
|
|
}
|
|
|
|
if(!desc.sec_state.encrypted) {
|
|
NimBLEDevice::startSecurity(event->subscribe.conn_handle);
|
|
}
|
|
}
|
|
|
|
it->setSubscribe(event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // BLE_GAP_EVENT_SUBSCRIBE
|
|
|
|
case BLE_GAP_EVENT_MTU: {
|
|
NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d",
|
|
event->mtu.conn_handle,
|
|
event->mtu.value);
|
|
rc = ble_gap_conn_find(event->mtu.conn_handle, &desc);
|
|
if (rc != 0) {
|
|
return 0;
|
|
}
|
|
|
|
server->m_pServerCallbacks->onMTUChange(event->mtu.value, &desc);
|
|
return 0;
|
|
} // BLE_GAP_EVENT_MTU
|
|
|
|
case BLE_GAP_EVENT_NOTIFY_TX: {
|
|
NimBLECharacteristic *pChar = nullptr;
|
|
|
|
for(auto &it : server->m_notifyChrVec) {
|
|
if(it->getHandle() == event->notify_tx.attr_handle) {
|
|
pChar = it;
|
|
}
|
|
}
|
|
|
|
if(pChar == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
NimBLECharacteristicCallbacks::Status statusRC;
|
|
|
|
if(event->notify_tx.indication) {
|
|
if(event->notify_tx.status != 0) {
|
|
if(event->notify_tx.status == BLE_HS_EDONE) {
|
|
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE;
|
|
} else if(rc == BLE_HS_ETIMEOUT) {
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT;
|
|
} else {
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
server->clearIndicateWait(event->notify_tx.conn_handle);
|
|
} else {
|
|
if(event->notify_tx.status == 0) {
|
|
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
|
|
} else {
|
|
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
|
|
}
|
|
}
|
|
|
|
pChar->m_pCallbacks->onStatus(pChar, statusRC, event->notify_tx.status);
|
|
|
|
return 0;
|
|
} // BLE_GAP_EVENT_NOTIFY_TX
|
|
|
|
|
|
case BLE_GAP_EVENT_ADV_COMPLETE:
|
|
#if CONFIG_BT_NIMBLE_EXT_ADV
|
|
case BLE_GAP_EVENT_SCAN_REQ_RCVD:
|
|
return NimBLEExtAdvertising::handleGapEvent(event, arg);
|
|
#else
|
|
return NimBLEAdvertising::handleGapEvent(event, arg);
|
|
#endif
|
|
// BLE_GAP_EVENT_ADV_COMPLETE | BLE_GAP_EVENT_SCAN_REQ_RCVD
|
|
|
|
case BLE_GAP_EVENT_CONN_UPDATE: {
|
|
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
|
|
return 0;
|
|
} // BLE_GAP_EVENT_CONN_UPDATE
|
|
|
|
case BLE_GAP_EVENT_REPEAT_PAIRING: {
|
|
/* We already have a bond with the peer, but it is attempting to
|
|
* establish a new secure link. This app sacrifices security for
|
|
* convenience: just throw away the old bond and accept the new link.
|
|
*/
|
|
|
|
/* Delete the old bond. */
|
|
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
|
if (rc != 0){
|
|
return BLE_GAP_REPEAT_PAIRING_IGNORE;
|
|
}
|
|
|
|
ble_store_util_delete_peer(&desc.peer_id_addr);
|
|
|
|
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
|
* continue with the pairing operation.
|
|
*/
|
|
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
|
} // BLE_GAP_EVENT_REPEAT_PAIRING
|
|
|
|
case BLE_GAP_EVENT_ENC_CHANGE: {
|
|
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
|
if(rc != 0) {
|
|
return BLE_ATT_ERR_INVALID_HANDLE;
|
|
}
|
|
// Compatibility only - Do not use, should be removed the in future
|
|
if(NimBLEDevice::m_securityCallbacks != nullptr) {
|
|
NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc);
|
|
/////////////////////////////////////////////
|
|
} else {
|
|
server->m_pServerCallbacks->onAuthenticationComplete(&desc);
|
|
}
|
|
|
|
return 0;
|
|
} // BLE_GAP_EVENT_ENC_CHANGE
|
|
|
|
case BLE_GAP_EVENT_PASSKEY_ACTION: {
|
|
struct ble_sm_io pkey = {0,0};
|
|
|
|
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
|
pkey.action = event->passkey.params.action;
|
|
// backward compatibility
|
|
pkey.passkey = NimBLEDevice::getSecurityPasskey(); // This is the passkey to be entered on peer
|
|
// if the (static)passkey is the default, check the callback for custom value
|
|
// both values default to the same.
|
|
if(pkey.passkey == 123456) {
|
|
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
|
|
}
|
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc);
|
|
|
|
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
|
NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %" PRIu32, event->passkey.params.numcmp);
|
|
pkey.action = event->passkey.params.action;
|
|
// Compatibility only - Do not use, should be removed the in future
|
|
if(NimBLEDevice::m_securityCallbacks != nullptr) {
|
|
pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
|
/////////////////////////////////////////////
|
|
} else {
|
|
pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp);
|
|
}
|
|
|
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc);
|
|
|
|
//TODO: Handle out of band pairing
|
|
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
|
static uint8_t tem_oob[16] = {0};
|
|
pkey.action = event->passkey.params.action;
|
|
for (int i = 0; i < 16; i++) {
|
|
pkey.oob[i] = tem_oob[i];
|
|
}
|
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc);
|
|
//////////////////////////////////
|
|
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
|
NIMBLE_LOGD(LOG_TAG, "Enter the passkey");
|
|
pkey.action = event->passkey.params.action;
|
|
|
|
// Compatibility only - Do not use, should be removed the in future
|
|
if(NimBLEDevice::m_securityCallbacks != nullptr) {
|
|
pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest();
|
|
/////////////////////////////////////////////
|
|
} else {
|
|
pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest();
|
|
}
|
|
|
|
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
|
NIMBLE_LOGD(LOG_TAG, "BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc);
|
|
|
|
} else if (event->passkey.params.action == BLE_SM_IOACT_NONE) {
|
|
NIMBLE_LOGD(LOG_TAG, "No passkey action required");
|
|
}
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent");
|
|
return 0;
|
|
} // BLE_GAP_EVENT_PASSKEY_ACTION
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent");
|
|
return 0;
|
|
} // handleGapEvent
|
|
|
|
|
|
/**
|
|
* @brief Set the server callbacks.
|
|
*
|
|
* As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
|
|
* disconnecting. This function can be called to register a callback handler that will be invoked when these
|
|
* events are detected.
|
|
*
|
|
* @param [in] pCallbacks The callbacks to be invoked.
|
|
* @param [in] deleteCallbacks if true callback class will be deleted when server is destructed.
|
|
*/
|
|
void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks) {
|
|
if (pCallbacks != nullptr){
|
|
m_pServerCallbacks = pCallbacks;
|
|
m_deleteCallbacks = deleteCallbacks;
|
|
} else {
|
|
m_pServerCallbacks = &defaultCallbacks;
|
|
}
|
|
} // setCallbacks
|
|
|
|
|
|
/**
|
|
* @brief Remove a service from the server.
|
|
*
|
|
* @details Immediately removes access to the service by clients, sends a service changed indication,
|
|
* and removes the service (if applicable) from the advertisements.
|
|
* The service is not deleted unless the deleteSvc parameter is true, otherwise the service remains
|
|
* available and can be re-added in the future. If desired a removed but not deleted service can
|
|
* be deleted later by calling this method with deleteSvc set to true.
|
|
*
|
|
* @note The service will not be removed from the database until all open connections are closed
|
|
* as it requires resetting the GATT server. In the interim the service will have it's visibility disabled.
|
|
*
|
|
* @note Advertising will need to be restarted by the user after calling this as we must stop
|
|
* advertising in order to remove the service.
|
|
*
|
|
* @param [in] service The service object to remove.
|
|
* @param [in] deleteSvc true if the service should be deleted.
|
|
*/
|
|
void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
|
|
// Check if the service was already removed and if so check if this
|
|
// is being called to delete the object and do so if requested.
|
|
// Otherwise, ignore the call and return.
|
|
if(service->m_removed > 0) {
|
|
if(deleteSvc) {
|
|
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
|
|
if ((*it) == service) {
|
|
delete *it;
|
|
m_svcVec.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0);
|
|
if(rc !=0) {
|
|
return;
|
|
}
|
|
|
|
service->m_removed = deleteSvc ? NIMBLE_ATT_REMOVE_DELETE : NIMBLE_ATT_REMOVE_HIDE;
|
|
serviceChanged();
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV
|
|
NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Adds a service which was either already created but removed from availability,\n
|
|
* or created and later added to services list.
|
|
* @param [in] service The service object to add.
|
|
* @note If it is desired to advertise the service it must be added by
|
|
* calling NimBLEAdvertising::addServiceUUID.
|
|
*/
|
|
void NimBLEServer::addService(NimBLEService* service) {
|
|
// Check that a service with the supplied UUID does not already exist.
|
|
if(getServiceByUUID(service->getUUID()) != nullptr) {
|
|
NIMBLE_LOGW(LOG_TAG, "Warning creating a duplicate service UUID: %s",
|
|
std::string(service->getUUID()).c_str());
|
|
}
|
|
|
|
// If adding a service that was not removed add it and return.
|
|
// Else reset GATT and send service changed notification.
|
|
if(service->m_removed == 0) {
|
|
m_svcVec.push_back(service);
|
|
return;
|
|
}
|
|
|
|
service->m_removed = 0;
|
|
serviceChanged();
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Resets the GATT server, used when services are added/removed after initialization.
|
|
*/
|
|
void NimBLEServer::resetGATT() {
|
|
if(getConnectedCount() > 0) {
|
|
return;
|
|
}
|
|
|
|
NimBLEDevice::stopAdvertising();
|
|
ble_gatts_reset();
|
|
ble_svc_gap_init();
|
|
ble_svc_gatt_init();
|
|
|
|
for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
|
|
if ((*it)->m_removed > 0) {
|
|
if ((*it)->m_removed == NIMBLE_ATT_REMOVE_DELETE) {
|
|
delete *it;
|
|
it = m_svcVec.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
(*it)->start();
|
|
++it;
|
|
}
|
|
|
|
m_svcChanged = false;
|
|
m_gattsStarted = false;
|
|
}
|
|
|
|
|
|
#if CONFIG_BT_NIMBLE_EXT_ADV
|
|
/**
|
|
* @brief Start advertising.
|
|
* @param [in] inst_id The extended advertisement instance ID to start.
|
|
* @param [in] duration How long to advertise for in milliseconds, 0 = forever (default).
|
|
* @param [in] max_events Maximum number of advertisement events to send, 0 = no limit (default).
|
|
* @return True if advertising started successfully.
|
|
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
|
|
* retrieving the advertising object and invoking start upon it.
|
|
*/
|
|
bool NimBLEServer::startAdvertising(uint8_t inst_id,
|
|
int duration,
|
|
int max_events) {
|
|
return getAdvertising()->start(inst_id, duration, max_events);
|
|
} // startAdvertising
|
|
|
|
|
|
/**
|
|
* @brief Convenience function to stop advertising a data set.
|
|
* @param [in] inst_id The extended advertisement instance ID to stop advertising.
|
|
* @return True if advertising stopped successfully.
|
|
*/
|
|
bool NimBLEServer::stopAdvertising(uint8_t inst_id) {
|
|
return getAdvertising()->stop(inst_id);
|
|
} // stopAdvertising
|
|
#endif
|
|
|
|
#if !CONFIG_BT_NIMBLE_EXT_ADV|| defined(_DOXYGEN_)
|
|
/**
|
|
* @brief Start advertising.
|
|
* @return True if advertising started successfully.
|
|
* @details Start the server advertising its existence. This is a convenience function and is equivalent to
|
|
* retrieving the advertising object and invoking start upon it.
|
|
*/
|
|
bool NimBLEServer::startAdvertising() {
|
|
return getAdvertising()->start();
|
|
} // startAdvertising
|
|
#endif
|
|
|
|
|
|
/**
|
|
* @brief Stop advertising.
|
|
* @return True if advertising stopped successfully.
|
|
*/
|
|
bool NimBLEServer::stopAdvertising() {
|
|
return getAdvertising()->stop();
|
|
} // stopAdvertising
|
|
|
|
|
|
/**
|
|
* @brief Get the MTU of the client.
|
|
* @returns The client MTU or 0 if not found/connected.
|
|
*/
|
|
uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
|
|
return ble_att_mtu(conn_id);
|
|
} //getPeerMTU
|
|
|
|
|
|
/**
|
|
* @brief Request an Update the connection parameters:
|
|
* * Can only be used after a connection has been established.
|
|
* @param [in] conn_handle The connection handle of the peer to send the request to.
|
|
* @param [in] minInterval The minimum connection interval in 1.25ms units.
|
|
* @param [in] maxInterval The maximum connection interval in 1.25ms units.
|
|
* @param [in] latency The number of packets allowed to skip (extends max interval).
|
|
* @param [in] timeout The timeout time in 10ms units before disconnecting.
|
|
*/
|
|
void NimBLEServer::updateConnParams(uint16_t conn_handle,
|
|
uint16_t minInterval, uint16_t maxInterval,
|
|
uint16_t latency, uint16_t timeout)
|
|
{
|
|
ble_gap_upd_params params;
|
|
|
|
params.latency = latency;
|
|
params.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
|
|
params.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
|
|
params.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
|
|
params.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
|
|
params.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
|
|
|
|
int rc = ble_gap_update_params(conn_handle, ¶ms);
|
|
if(rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
|
}
|
|
} // updateConnParams
|
|
|
|
|
|
/**
|
|
* @brief Request an update of the data packet length.
|
|
* * Can only be used after a connection has been established.
|
|
* @details Sends a data length update request to the peer.
|
|
* The Data Length Extension (DLE) allows to increase the Data Channel Payload from 27 bytes to up to 251 bytes.
|
|
* The peer needs to support the Bluetooth 4.2 specifications, to be capable of DLE.
|
|
* @param [in] conn_handle The connection handle of the peer to send the request to.
|
|
* @param [in] tx_octets The preferred number of payload octets to use (Range 0x001B-0x00FB).
|
|
*/
|
|
void NimBLEServer::setDataLen(uint16_t conn_handle, uint16_t tx_octets) {
|
|
#if defined(CONFIG_NIMBLE_CPP_IDF) && !defined(ESP_IDF_VERSION) || \
|
|
(ESP_IDF_VERSION_MAJOR * 100 + ESP_IDF_VERSION_MINOR * 10 + ESP_IDF_VERSION_PATCH) < 432
|
|
return;
|
|
#else
|
|
uint16_t tx_time = (tx_octets + 14) * 8;
|
|
|
|
int rc = ble_gap_set_data_len(conn_handle, tx_octets, tx_time);
|
|
if(rc != 0) {
|
|
NIMBLE_LOGE(LOG_TAG, "Set data length error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
|
|
}
|
|
#endif
|
|
} // setDataLen
|
|
|
|
|
|
bool NimBLEServer::setIndicateWait(uint16_t conn_handle) {
|
|
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
|
|
if(m_indWait[i] == conn_handle) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void NimBLEServer::clearIndicateWait(uint16_t conn_handle) {
|
|
for(auto i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) {
|
|
if(m_indWait[i] == conn_handle) {
|
|
m_indWait[i] = BLE_HS_CONN_HANDLE_NONE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Default callback handlers */
|
|
|
|
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) {
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
|
} // onConnect
|
|
|
|
|
|
void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default");
|
|
} // onConnect
|
|
|
|
|
|
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
|
} // onDisconnect
|
|
|
|
void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
|
|
} // onDisconnect
|
|
|
|
void NimBLEServerCallbacks::onMTUChange(uint16_t MTU, ble_gap_conn_desc* desc) {
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onMTUChange(): Default");
|
|
} // onMTUChange
|
|
|
|
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
|
|
return 123456;
|
|
}
|
|
/*
|
|
void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key);
|
|
}
|
|
|
|
bool NimBLEServerCallbacks::onSecurityRequest(){
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true");
|
|
return true;
|
|
}
|
|
*/
|
|
void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
|
|
}
|
|
bool NimBLEServerCallbacks::onConfirmPIN(uint32_t pin){
|
|
NIMBLE_LOGD("NimBLEServerCallbacks", "onConfirmPIN: default: true");
|
|
return true;
|
|
}
|
|
|
|
#endif /* CONFIG_BT_ENABLED && CONFIG_BT_NIMBLE_ROLE_PERIPHERAL */
|