From bf3342d11599675f9a562b77a76ae90194318b70 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 2 Mar 2020 14:14:58 -0700 Subject: [PATCH 01/62] Initial ServerDev commit. --- src/NimBLEDevice.cpp | 16 +- src/NimBLEDevice.h | 3 + src/NimBLEServer.cpp | 439 +++++++++++++++++++++++++++++++++++++++++++ src/NimBLEServer.h | 150 +++++++++++++++ 4 files changed, 607 insertions(+), 1 deletion(-) create mode 100644 src/NimBLEServer.cpp create mode 100644 src/NimBLEServer.h diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 691a0489..11adf61a 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -48,9 +48,9 @@ static const char* LOG_TAG = "NimBLEDevice"; /** * Singletons for the NimBLEDevice. */ -//BLEServer* BLEDevice::m_pServer = nullptr; bool initialized = false; NimBLEScan* NimBLEDevice::m_pScan = nullptr; +NimBLEServer* NimBLEDevice::m_pServer = nullptr; uint32_t NimBLEDevice::m_passkey = 123456; bool NimBLEDevice::m_synced = false; @@ -71,6 +71,20 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; //gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; +/** + * @brief Create a new instance of a server. + * @return A new instance of the server. + */ +/* STATIC */ NimBLEServer* NimBLEDevice::createServer() { +#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig + NIMBLE_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); + abort(); +#endif // CONFIG_GATTS_ENABLE + NimBLEDevice::m_pServer = new NimBLEServer(); + return m_pServer; +} // createServer + + /** * @brief Retrieve the Scan object that we use for scanning. * @return The scanning object reference. This is a singleton object. The caller should not diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 336e17d9..118e82de 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -20,6 +20,7 @@ #include "NimbleScan.h" #include "NimBLEUtils.h" #include "NimBLEClient.h" +#include "NimBLEServer.h" #include "NimBLESecurity.h" #include "esp_bt.h" @@ -65,6 +66,7 @@ class NimBLEDevice { static std::string toString(); static NimBLEScan* getScan(); // Get the scan object static NimBLEClient* createClient(); + static NimBLEServer* createServer(); static bool deleteClient(NimBLEClient* pClient); static void setPower(esp_power_level_t powerLevel); static void setCustomGapHandler(gap_event_handler handler); @@ -97,6 +99,7 @@ class NimBLEDevice { static bool m_synced; static NimBLEScan* m_pScan; + static NimBLEServer* m_pServer; static ble_gap_event_listener m_listener; static uint32_t m_passkey; static std::list m_cList; diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp new file mode 100644 index 00000000..266b531c --- /dev/null +++ b/src/NimBLEServer.cpp @@ -0,0 +1,439 @@ +/* + * NimBLEServer.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.cpp + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEServer.h" +#include "NimBLEUtils.h" +#include "NimBLEDevice.h" +#include "NimBLELog.h" + +static const char* LOG_TAG = "NimBLEServer"; + + +/** + * @brief Construct a %BLE Server + * + * This class is not designed to be individually instantiated. Instead one should create a server by asking + * the BLEDevice class. + */ +NimBLEServer::NimBLEServer() { +// m_appId = ESP_GATT_IF_NONE; +// m_gatts_if = ESP_GATT_IF_NONE; + m_connectedCount = 0; + m_connId = BLE_HS_CONN_HANDLE_NONE; + m_pServerCallbacks = nullptr; +} // BLEServer + +/* +void BLEServer::createApp(uint16_t appId) { + m_appId = appId; + registerApp(appId); +} // createApp +*/ + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @return A reference to the new service object. + */ + /* +BLEService* BLEServer::createService(const char* uuid) { + return createService(BLEUUID(uuid)); +} +*/ + +/** + * @brief Create a %BLE Service. + * + * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition + * of a new service. Every service must have a unique UUID. + * @param [in] uuid The UUID of the new service. + * @param [in] numHandles The maximum number of handles associated with this service. + * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. + * @return A reference to the new service object. + */ + /* +BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { + ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); + m_semaphoreCreateEvt.take("createService"); + + // Check that a service with the supplied UUID does not already exist. + if (m_serviceMap.getByUUID(uuid) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + uuid.toString().c_str()); + } + + BLEService* pService = new BLEService(uuid, numHandles); + pService->m_instId = inst_id; + m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. + pService->executeCreate(this); // Perform the API calls to actually create the service. + + m_semaphoreCreateEvt.wait("createService"); + + ESP_LOGD(LOG_TAG, "<< createService"); + return pService; +} // createService + +*/ +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ + /* +BLEService* BLEServer::getServiceByUUID(const char* uuid) { + return m_serviceMap.getByUUID(uuid); +} +*/ +/** + * @brief Get a %BLE Service by its UUID + * @param [in] uuid The UUID of the new service. + * @return A reference to the service object. + */ + /* +BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { + return m_serviceMap.getByUUID(uuid); +} +*/ +/** + * @brief Retrieve the advertising object that can be used to advertise the existence of the server. + * + * @return An advertising object. + */ + /* +BLEAdvertising* BLEServer::getAdvertising() { + return BLEDevice::getAdvertising(); +} +*/ + +uint16_t NimBLEServer::getConnId() { + return m_connId; +} + + +/** + * @brief Return the number of connected clients. + * @return The number of connected clients. + */ +uint32_t NimBLEServer::getConnectedCount() { + return m_connectedCount; +} // getConnectedCount + +/* +uint16_t BLEServer::getGattsIf() { + return m_gatts_if; +} +*/ + +/** + * @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 = (NimBLEServer*)arg; + NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", + NimBLEUtils::gapEventToString(event->type)); +/* + switch(event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + // + case ESP_GATTS_ADD_CHAR_EVT: { + break; + } // ESP_GATTS_ADD_CHAR_EVT + + case ESP_GATTS_MTU_EVT: + updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); + break; + + // ESP_GATTS_CONNECT_EVT + // connect: + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // + case ESP_GATTS_CONNECT_EVT: { + m_connId = param->connect.conn_id; + addPeerDevice((void*)this, false, m_connId); + if (m_pServerCallbacks != nullptr) { + m_pServerCallbacks->onConnect(this); + m_pServerCallbacks->onConnect(this, param); + } + m_connectedCount++; // Increment the number of connected devices count. + break; + } // ESP_GATTS_CONNECT_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // + case ESP_GATTS_CREATE_EVT: { + BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid + m_serviceMap.setByHandle(param->create.service_handle, pService); + m_semaphoreCreateEvt.give(); + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DISCONNECT_EVT + // + // disconnect + // - uint16_t conn_id + // - esp_bd_addr_t remote_bda + // - esp_gatt_conn_reason_t reason + // + // If we receive a disconnect event then invoke the callback for disconnects (if one is present). + // we also want to start advertising again. + case ESP_GATTS_DISCONNECT_EVT: { + m_connectedCount--; // Decrement the number of connected devices count. + if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. + m_pServerCallbacks->onDisconnect(this); + } + startAdvertising(); //- do this with some delay from the loop() + removePeerDevice(param->disconnect.conn_id, false); + break; + } // ESP_GATTS_DISCONNECT_EVT + + + // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + break; + } // ESP_GATTS_READ_EVT + + + // ESP_GATTS_REG_EVT + // reg: + // - esp_gatt_status_t status + // - uint16_t app_id + // + case ESP_GATTS_REG_EVT: { + m_gatts_if = gatts_if; + m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. + break; + } // ESP_GATTS_REG_EVT + + + // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t* value + // + case ESP_GATTS_WRITE_EVT: { + break; + } + + case ESP_GATTS_OPEN_EVT: + m_semaphoreOpenEvt.give(param->open.status); + break; + + default: + break; + } + + // Invoke the handler for every Service we have. + m_serviceMap.handleGATTServerEvent(event, gatts_if, param); +*/ + return 0; + NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); +} // handleGATTServerEvent + + +/** + * @brief Register the app. + * + * @return N/A + */ + /* +void BLEServer::registerApp(uint16_t m_appId) { + ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); + m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. + ::esp_ble_gatts_app_register(m_appId); + m_semaphoreRegisterAppEvt.wait("registerApp"); + ESP_LOGD(LOG_TAG, "<< registerApp"); +} // registerApp +*/ + +/** + * @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. + */ +void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) { + m_pServerCallbacks = pCallbacks; +} // setCallbacks + +/* + * Remove service + */ +/* +void BLEServer::removeService(BLEService* service) { + service->stop(); + service->executeDelete(); + m_serviceMap.removeService(service); +} +*/ +/** + * @brief Start advertising. + * + * Start the server advertising its existence. This is a convenience function and is equivalent to + * retrieving the advertising object and invoking start upon it. + */ + /* +void BLEServer::startAdvertising() { + ESP_LOGD(LOG_TAG, ">> startAdvertising"); + BLEDevice::startAdvertising(); + ESP_LOGD(LOG_TAG, "<< startAdvertising"); +} // startAdvertising +*/ +/** + * Allow to connect GATT server to peer device + * Probably can be used in ANCS for iPhone + */ + /* +bool BLEServer::connect(BLEAddress address) { + esp_bd_addr_t addr; + memcpy(&addr, address.getNative(), 6); + // Perform the open connection request against the target BLE Server. + m_semaphoreOpenEvt.take("connect"); + esp_err_t errRc = ::esp_ble_gatts_open( + getGattsIf(), + addr, // address + 1 // direct connection + ); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return false; + } + + uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete. + ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK); + return rc == ESP_GATT_OK; +} // connect +*/ + + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { + NIMBLE_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); + NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect + +/* +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, esp_ble_gatts_cb_param_t* param) { + NIMBLE_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); + NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); + NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); +} // onConnect +*/ + +void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { + NIMBLE_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); + NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); + NIMBLE_LOGD("BLEServerCallbacks", "<< onDisconnect()"); +} // onDisconnect + +/* multi connect support */ +/* TODO do some more tweaks */ +void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { + // set mtu in conn_status_t + const std::map::iterator it = m_connectedServersMap.find(conn_id); + if (it != m_connectedServersMap.end()) { + it->second.mtu = mtu; + std::swap(m_connectedServersMap[conn_id], it->second); + } +} + +std::map NimBLEServer::getPeerDevices(bool _client) { + return m_connectedServersMap; +} + + +uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) { + return m_connectedServersMap.find(conn_id)->second.mtu; +} + +void NimBLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { + conn_status_t status = { + .peer_device = peer, + .connected = true, + .mtu = 23 + }; + + m_connectedServersMap.insert(std::pair(conn_id, status)); +} + +void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) { + m_connectedServersMap.erase(conn_id); +} +/* multi connect support */ + +/** + * Update connection parameters can be called only after connection has been established + */ + /* +void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { + esp_ble_conn_update_params_t conn_params; + memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); + conn_params.latency = latency; + conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms + conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms + conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms + esp_ble_gap_update_conn_params(&conn_params); +} +*/ +/* +void BLEServer::disconnect(uint16_t connId){ + esp_ble_gatts_close(m_gatts_if, connId); +} +*/ +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h new file mode 100644 index 00000000..7b33faf1 --- /dev/null +++ b/src/NimBLEServer.h @@ -0,0 +1,150 @@ +/* + * NimBLEServer.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEServer.h + * + * Created on: Apr 16, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVER_H_ +#define MAIN_NIMBLESERVER_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +//#include + +// #include "BLEDevice.h" +#include "NimBLEAddress.h" +#include "NimBLEUUID.h" +//#include "BLEAdvertising.h" +//#include "BLECharacteristic.h" +//#include "BLEService.h" +#include "NimBLESecurity.h" +#include "FreeRTOS.h" + + +#include + +class NimBLEServerCallbacks; +/* TODO possibly refactor this struct */ +typedef struct { + void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here + bool connected; // do we need it? + uint16_t mtu; // every peer device negotiate own mtu +} conn_status_t; + + +/** + * @brief A data structure that manages the %BLE servers owned by a BLE server. + */ + /* +class BLEServiceMap { +public: + BLEService* getByHandle(uint16_t handle); + BLEService* getByUUID(const char* uuid); + BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void setByHandle(uint16_t handle, BLEService* service); + void setByUUID(const char* uuid, BLEService* service); + void setByUUID(BLEUUID uuid, BLEService* service); + std::string toString(); + BLEService* getFirst(); + BLEService* getNext(); + void removeService(BLEService *service); + int getRegisteredServiceCount(); + +private: + std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; +}; +*/ + +/** + * @brief The model of a %BLE server. + */ +class NimBLEServer { +public: + uint32_t getConnectedCount(); +// BLEService* createService(const char* uuid); +// BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); +// BLEAdvertising* getAdvertising(); + void setCallbacks(NimBLEServerCallbacks* pCallbacks); +// void startAdvertising(); +// void removeService(BLEService* service); +// BLEService* getServiceByUUID(const char* uuid); +// BLEService* getServiceByUUID(BLEUUID uuid); +// bool connect(BLEAddress address); +// void disconnect(uint16_t connId); +// uint16_t m_appId; +// void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + + /* multi connection support */ + std::map getPeerDevices(bool client); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); + NimBLEServer* getServerByConnId(uint16_t conn_id); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); + + +private: + NimBLEServer(); + //friend class BLEService; + //friend class BLECharacteristic; + friend class NimBLEDevice; + //esp_ble_adv_data_t m_adv_data; + // BLEAdvertising m_bleAdvertising; + uint16_t m_connId; + uint32_t m_connectedCount; + uint16_t m_gatts_if; + std::map m_connectedServersMap; + + //FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); + //FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + //FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); + //BLEServiceMap m_serviceMap; + NimBLEServerCallbacks* m_pServerCallbacks = nullptr; + + //void createApp(uint16_t appId); + //uint16_t getGattsIf(); + //void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); + static int handleGapEvent(struct ble_gap_event *event, void *arg); + //void registerApp(uint16_t); +}; // BLEServer + + +/** + * @brief Callbacks associated with the operation of a %BLE server. + */ +class NimBLEServerCallbacks { +public: + virtual ~NimBLEServerCallbacks() {}; + /** + * @brief Handle a new client connection. + * + * When a new client connects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the client connection. + */ + virtual void onConnect(NimBLEServer* pServer); + //virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); + /** + * @brief Handle an existing client disconnection. + * + * When an existing client disconnects, we are invoked. + * + * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. + */ + virtual void onDisconnect(NimBLEServer* pServer); +}; // BLEServerCallbacks + + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLESERVER_H_ */ \ No newline at end of file From 692bfefd4125379565f3a18b560b660fa651f4c4 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 2 Mar 2020 15:26:38 -0700 Subject: [PATCH 02/62] Add services files --- src/NimBLEServer.cpp | 32 ++-- src/NimBLEServer.h | 7 +- src/NimBLEService.cpp | 431 ++++++++++++++++++++++++++++++++++++++++++ src/NimBLEService.h | 105 ++++++++++ 4 files changed, 556 insertions(+), 19 deletions(-) create mode 100644 src/NimBLEService.cpp create mode 100644 src/NimBLEService.h diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 266b531c..413381bf 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -52,11 +52,11 @@ void BLEServer::createApp(uint16_t appId) { * @param [in] uuid The UUID of the new service. * @return A reference to the new service object. */ - /* -BLEService* BLEServer::createService(const char* uuid) { - return createService(BLEUUID(uuid)); + +NimBLEService* NimBLEServer::createService(const char* uuid) { + return createService(NimBLEUUID(uuid)); } -*/ + /** * @brief Create a %BLE Service. @@ -68,29 +68,29 @@ BLEService* BLEServer::createService(const char* uuid) { * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. * @return A reference to the new service object. */ - /* -BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { - ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); - m_semaphoreCreateEvt.take("createService"); + +NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { + NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); + //m_semaphoreCreateEvt.take("createService"); // Check that a service with the supplied UUID does not already exist. - if (m_serviceMap.getByUUID(uuid) != nullptr) { +/* if (m_serviceMap.getByUUID(uuid) != nullptr) { ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); } - - BLEService* pService = new BLEService(uuid, numHandles); - pService->m_instId = inst_id; - m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. +*/ + NimBLEService* pService = new NimBLEService(uuid, numHandles); +// pService->m_instId = inst_id; +// m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. pService->executeCreate(this); // Perform the API calls to actually create the service. - m_semaphoreCreateEvt.wait("createService"); +// m_semaphoreCreateEvt.wait("createService"); - ESP_LOGD(LOG_TAG, "<< createService"); + NIMBLE_LOGD(LOG_TAG, "<< createService"); return pService; } // createService -*/ + /** * @brief Get a %BLE Service by its UUID * @param [in] uuid The UUID of the new service. diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 7b33faf1..1d525ae8 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -23,13 +23,14 @@ #include "NimBLEUUID.h" //#include "BLEAdvertising.h" //#include "BLECharacteristic.h" -//#include "BLEService.h" +#include "NimBLEService.h" #include "NimBLESecurity.h" #include "FreeRTOS.h" #include +class NimBLEService; class NimBLEServerCallbacks; /* TODO possibly refactor this struct */ typedef struct { @@ -71,8 +72,8 @@ class BLEServiceMap { class NimBLEServer { public: uint32_t getConnectedCount(); -// BLEService* createService(const char* uuid); -// BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); + NimBLEService* createService(const char* uuid); + NimBLEService* createService(NimBLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); // BLEAdvertising* getAdvertising(); void setCallbacks(NimBLEServerCallbacks* pCallbacks); // void startAdvertising(); diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp new file mode 100644 index 00000000..9e36930f --- /dev/null +++ b/src/NimBLEService.cpp @@ -0,0 +1,431 @@ +/* + * NimBLEService.cpp + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.cpp + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +// A service is identified by a UUID. A service is also the container for one or more characteristics. + +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +//#include +//#include + +#include "NimBLEService.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" + +//#include +//#include +#include + +//#include "BLEServer.h" +//#include "GeneralUtils.h" + +static const char* LOG_TAG = "NimBLEService"; // Tag for logging. + +#define NULL_HANDLE (0xffff) + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles) : NimBLEService(NimBLEUUID(uuid), numHandles) { +} + + +/** + * @brief Construct an instance of the BLEService + * @param [in] uuid The UUID of the service. + * @param [in] numHandles The maximum number of handles associated with the service. + */ +NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles) { + m_uuid = uuid; + m_handle = NULL_HANDLE; + m_pServer = nullptr; + //m_serializeMutex.setName("BLEService"); + //m_lastCreatedCharacteristic = nullptr; + m_numHandles = numHandles; +} // NimBLEService + + +/** + * @brief Create the service. + * Create the service. + * @param [in] gatts_if The handle of the GATT server interface. + * @return N/A. + */ + +void NimBLEService::executeCreate(NimBLEServer* pServer) { + NIMBLE_LOGD(LOG_TAG, ">> executeCreate() - Creating service service uuid: %s", getUUID().toString().c_str()); + m_pServer = pServer; +// m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT + +// esp_gatt_srvc_id_t srvc_id; +// srvc_id.is_primary = true; +// srvc_id.id.inst_id = m_instId; +// srvc_id.id.uuid = *m_uuid.getNative(); +// esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. + +// if (errRc != ESP_OK) { +// ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); +// return; +// } + +// m_semaphoreCreateEvt.wait("executeCreate"); + NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Delete the service. + * Delete the service. + * @return N/A. + */ + +void NimBLEService::executeDelete() { + NIMBLE_LOGD(LOG_TAG, ">> executeDelete()"); +// m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT + +// esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle()); + +// if (errRc != ESP_OK) { +// ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); +// return; +// } + +// m_semaphoreDeleteEvt.wait("executeDelete"); + NIMBLE_LOGD(LOG_TAG, "<< executeDelete"); +} // executeDelete + + +/** + * @brief Dump details of this BLE GATT service. + * @return N/A. + */ +void NimBLEService::dump() { + NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", + m_uuid.toString().c_str(), + m_handle); +// NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); +} // dump + + +/** + * @brief Get the UUID of the service. + * @return the UUID of the service. + */ +NimBLEUUID NimBLEService::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Start the service. + * Here we wish to start the service which means that we will respond to partner requests about it. + * Starting a service also means that we can create the corresponding characteristics. + * @return Start the service. + */ + /* +void BLEService::start() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). +// + ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); + return; + } + + BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); + + while (pCharacteristic != nullptr) { + m_lastCreatedCharacteristic = pCharacteristic; + pCharacteristic->executeCreate(this); + + pCharacteristic = m_characteristicMap.getNext(); + } + // Start each of the characteristics ... these are found in the m_characteristicMap. + + m_semaphoreStartEvt.take("start"); + esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStartEvt.wait("start"); + + ESP_LOGD(LOG_TAG, "<< start()"); +} // start +*/ + +/** + * @brief Stop the service. + */ + /* +void BLEService::stop() { +// We ask the BLE runtime to start the service and then create each of the characteristics. +// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event +// obtained as a result of calling esp_ble_gatts_create_service(). + ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); + if (m_handle == NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); + return; + } + + m_semaphoreStopEvt.take("stop"); + esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreStopEvt.wait("stop"); + + ESP_LOGD(LOG_TAG, "<< stop()"); +} // start +*/ + +/** + * @brief Set the handle associated with this service. + * @param [in] handle The handle associated with the service. + */ +void NimBLEService::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str()); + if (m_handle != NULL_HANDLE) { + NIMBLE_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle); + return; + } + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Get the handle associated with this service. + * @return The handle associated with this service. + */ +uint16_t NimBLEService::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Add a characteristic to the service. + * @param [in] pCharacteristic A pointer to the characteristic to be added. + */ + /* +void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { + // We maintain a mapping of characteristics owned by this service. These are managed by the + // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic + // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). + + ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); + ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + pCharacteristic->getUUID().toString().c_str(), + toString().c_str()); + + // Check that we don't add the same characteristic twice. + if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { + ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + //return; + } + + // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID + // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. + m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); + + ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); +} // addCharacteristic +*/ + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ + /* +BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(BLEUUID(uuid), properties); +} +*/ + +/** + * @brief Create a new BLE Characteristic associated with this service. + * @param [in] uuid - The UUID of the characteristic. + * @param [in] properties - The properties of the characteristic. + * @return The new BLE characteristic. + */ + /* +BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { + BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties); + addCharacteristic(pCharacteristic); + return pCharacteristic; +} // createCharacteristic +*/ + +/** + * @brief Handle a GATTS server event. + */ + /* +void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + switch (event) { + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + + // If we have reached the correct service, then locate the characteristic and remember the handle + // for that characteristic. + case ESP_GATTS_ADD_CHAR_EVT: { + if (m_handle == param->add_char.service_handle) { + BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); + if (pCharacteristic == nullptr) { + ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", + BLEUUID(param->add_char.char_uuid).toString().c_str()); + dump(); + break; + } + pCharacteristic->setHandle(param->add_char.attr_handle); + m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); + break; + } // Reached the correct service. + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_START_EVT + // + // start: + // esp_gatt_status_t status + // uint16_t service_handle + case ESP_GATTS_START_EVT: { + if (param->start.service_handle == getHandle()) { + m_semaphoreStartEvt.give(); + } + break; + } // ESP_GATTS_START_EVT + + // ESP_GATTS_STOP_EVT + // + // stop: + // esp_gatt_status_t status + // uint16_t service_handle + // + case ESP_GATTS_STOP_EVT: { + if (param->stop.service_handle == getHandle()) { + m_semaphoreStopEvt.give(); + } + break; + } // ESP_GATTS_STOP_EVT + + + // ESP_GATTS_CREATE_EVT + // Called when a new service is registered as having been created. + // + // create: + // * esp_gatt_status_t status + // * uint16_t service_handle + // * esp_gatt_srvc_id_t service_id + // * - esp_gatt_id id + // * - esp_bt_uuid uuid + // * - uint8_t inst_id + // * - bool is_primary + // + case ESP_GATTS_CREATE_EVT: { + if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) { + setHandle(param->create.service_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_CREATE_EVT + + + // ESP_GATTS_DELETE_EVT + // Called when a service is deleted. + // + // delete: + // * esp_gatt_status_t status + // * uint16_t service_handle + // + case ESP_GATTS_DELETE_EVT: { + if (param->del.service_handle == getHandle()) { + m_semaphoreDeleteEvt.give(); + } + break; + } // ESP_GATTS_DELETE_EVT + + default: + break; + } // Switch + + // Invoke the GATTS handler in each of the associated characteristics. + m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); +} // handleGATTServerEvent +*/ + +/* +BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(BLEUUID(uuid)); +} + + +BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { + return m_characteristicMap.getByUUID(uuid); +} +*/ + +/** + * @brief Return a string representation of this service. + * A service is defined by: + * * Its UUID + * * Its handle + * @return A string representation of this service. + */ +std::string NimBLEService::toString() { + std::string res = "UUID: " + getUUID().toString(); + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", getHandle()); + res += ", handle: 0x"; + res += hex; + return res; +} // toString + + +/** + * @brief Get the last created characteristic. + * It is lamentable that this function has to exist. It returns the last created characteristic. + * We need this because the descriptor API is built around the notion that a new descriptor, when created, + * is associated with the last characteristics created and we need that information. + * @return The last created characteristic. + */ + /* +BLECharacteristic* BLEService::getLastCreatedCharacteristic() { + return m_lastCreatedCharacteristic; +} // getLastCreatedCharacteristic +*/ + +/** + * @brief Get the BLE server associated with this service. + * @return The BLEServer associated with this service. + */ +NimBLEServer* NimBLEService::getServer() { + return m_pServer; +} // getServer + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEService.h b/src/NimBLEService.h new file mode 100644 index 00000000..fab8d17a --- /dev/null +++ b/src/NimBLEService.h @@ -0,0 +1,105 @@ +/* + * NimBLEService.h + * + * Created: on March 2, 2020 + * Author H2zero + * + * Originally: + * + * BLEService.h + * + * Created on: Mar 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLESERVICE_H_ +#define MAIN_NIMBLESERVICE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +//#include + +//#include "BLECharacteristic.h" +#include "NimBLEServer.h" +#include "NimBLEUUID.h" +#include "FreeRTOS.h" + +class NimBLEServer; + +/** + * @brief A data mapping used to manage the set of %BLE characteristics known to the server. + */ + /* +class BLECharacteristicMap { +public: + void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); + void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); + BLECharacteristic* getByUUID(const char* uuid); + BLECharacteristic* getByUUID(BLEUUID uuid); + BLECharacteristic* getByHandle(uint16_t handle); + BLECharacteristic* getFirst(); + BLECharacteristic* getNext(); + std::string toString(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; +*/ + +/** + * @brief The model of a %BLE service. + * + */ +class NimBLEService { +public: + //void addCharacteristic(BLECharacteristic* pCharacteristic); + //BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); + //BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); + void dump(); + void executeCreate(NimBLEServer* pServer); + void executeDelete(); + //BLECharacteristic* getCharacteristic(const char* uuid); + //BLECharacteristic* getCharacteristic(BLEUUID uuid); + NimBLEUUID getUUID(); + NimBLEServer* getServer(); +// void start(); +// void stop(); + std::string toString(); + uint16_t getHandle(); +// uint8_t m_instId = 0; + +private: + NimBLEService(const char* uuid, uint16_t numHandles); + NimBLEService(NimBLEUUID uuid, uint16_t numHandles); + friend class NimBLEServer; +// friend class BLEServiceMap; +// friend class BLEDescriptor; +// friend class BLECharacteristic; + friend class NimBLEDevice; + +// BLECharacteristicMap m_characteristicMap; + uint16_t m_handle; +// BLECharacteristic* m_lastCreatedCharacteristic = nullptr; + NimBLEServer* m_pServer = nullptr; + NimBLEUUID m_uuid; + +// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); +// FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); +// FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); +// FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); + + uint16_t m_numHandles; + +// BLECharacteristic* getLastCreatedCharacteristic(); +// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + void setHandle(uint16_t handle); + //void setService(esp_gatt_srvc_id_t srvc_id); +}; // BLEService + + +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_NIMBLESERVICE_H_ */ \ No newline at end of file From 3ee3bb98cfbe1d85da7e247c068abb543c0d8acc Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 2 Mar 2020 22:23:28 -0700 Subject: [PATCH 03/62] Test implementation of start service. --- src/NimBLEDevice.cpp | 11 +++++++++-- src/NimBLEDevice.h | 4 ++-- src/NimBLEService.cpp | 35 ++++++++++++++++++++++++++++------- src/NimBLEService.h | 2 +- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 11adf61a..b3c46e5b 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -26,6 +26,7 @@ #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" @@ -76,11 +77,17 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; * @return A new instance of the server. */ /* STATIC */ NimBLEServer* NimBLEDevice::createServer() { -#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig +/*#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig NIMBLE_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined"); abort(); #endif // CONFIG_GATTS_ENABLE - NimBLEDevice::m_pServer = new NimBLEServer(); +*/ + if(NimBLEDevice::m_pServer == nullptr) { + NimBLEDevice::m_pServer = new NimBLEServer(); + ble_svc_gap_init(); + ble_svc_gatt_init(); + } + return m_pServer; } // createServer diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 118e82de..cab51538 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -44,8 +44,8 @@ #define BLEClientCallbacks NimBLEClientCallbacks #define BLEAdvertisedDeviceCallbacks NimBLEAdvertisedDeviceCallbacks #define BLEScanResults NimBLEScanResults - - +#define BLEServer NimBLEServer +#define BLEService NimBLEService /** diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 9e36930f..bbbe434f 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -136,14 +136,35 @@ NimBLEUUID NimBLEService::getUUID() { * Starting a service also means that we can create the corresponding characteristics. * @return Start the service. */ - /* -void BLEService::start() { + +void NimBLEService::start() { // We ask the BLE runtime to start the service and then create each of the characteristics. // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event // obtained as a result of calling esp_ble_gatts_create_service(). // - ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { + NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + + ble_gatt_svc_def svc; + int rc = 0; + + svc.type = BLE_GATT_SVC_TYPE_PRIMARY; + svc.uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; + svc.characteristics = NULL; + + rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)&svc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d", rc); + return; + } + + rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)&svc); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d", rc); + return; + } + + +/* if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); return; } @@ -166,10 +187,10 @@ void BLEService::start() { return; } m_semaphoreStartEvt.wait("start"); - - ESP_LOGD(LOG_TAG, "<< start()"); -} // start */ + NIMBLE_LOGD(LOG_TAG, "<< start()"); +} // start + /** * @brief Stop the service. diff --git a/src/NimBLEService.h b/src/NimBLEService.h index fab8d17a..bdb73bc6 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -66,7 +66,7 @@ class NimBLEService { //BLECharacteristic* getCharacteristic(BLEUUID uuid); NimBLEUUID getUUID(); NimBLEServer* getServer(); -// void start(); + void start(); // void stop(); std::string toString(); uint16_t getHandle(); From 6973a57e9237cd763d87b3951067a8d7e15b0998 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 3 Mar 2020 15:41:49 -0700 Subject: [PATCH 04/62] Added Characteristic files --- src/NimBLECharacteristic.cpp | 771 ++++++++++++++++++++++++++++++++ src/NimBLECharacteristic.h | 149 ++++++ src/NimBLECharacteristicMap.cpp | 143 ++++++ src/NimBLEDevice.h | 1 + src/NimBLEServer.h | 2 +- src/NimBLEService.cpp | 95 ++-- src/NimBLEService.h | 53 +-- 7 files changed, 1152 insertions(+), 62 deletions(-) create mode 100644 src/NimBLECharacteristic.cpp create mode 100644 src/NimBLECharacteristic.h create mode 100644 src/NimBLECharacteristicMap.cpp diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp new file mode 100644 index 00000000..a6deadef --- /dev/null +++ b/src/NimBLECharacteristic.cpp @@ -0,0 +1,771 @@ +/* + * NimBLECharacteristic.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * BLECharacteristic.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLECharacteristic.h" +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" +#include "NimBLELog.h" +//#include "BLE2902.h" +//#include "GeneralUtils.h" + +#include + +#define NULL_HANDLE (0xffff) + +static const char* LOG_TAG = "NimBLECharacteristic"; + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID (const char*) for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties) : NimBLECharacteristic(NimBLEUUID(uuid), properties) { +} + +/** + * @brief Construct a characteristic + * @param [in] uuid - UUID for the characteristic. + * @param [in] properties - Properties for the characteristic. + */ +NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties) { + m_bleUUID = uuid; + m_handle = NULL_HANDLE; + m_properties = (uint8_t)0; + m_pCallbacks = nullptr; + + setBroadcastProperty((properties & BLE_GATT_CHR_PROP_BROADCAST) != 0); + setReadProperty((properties & BLE_GATT_CHR_PROP_READ) != 0); + setWriteProperty((properties & BLE_GATT_CHR_PROP_WRITE) != 0); + setNotifyProperty((properties & BLE_GATT_CHR_PROP_NOTIFY) != 0); + setIndicateProperty((properties & BLE_GATT_CHR_PROP_INDICATE) != 0); + setWriteNoResponseProperty((properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0); +} // NimBLECharacteristic + +/** + * @brief Destructor. + */ +NimBLECharacteristic::~NimBLECharacteristic() { + //free(m_value.attr_value); // Release the storage for the value. +} // ~NimBLECharacteristic + + +/** + * @brief Associate a descriptor with this characteristic. + * @param [in] pDescriptor + * @return N/A. + */ + /* +void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) { + ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); + m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); + ESP_LOGD(LOG_TAG, "<< addDescriptor()"); +} // addDescriptor +*/ + +/** + * @brief Register a new characteristic with the ESP runtime. + * @param [in] pService The service with which to associate this characteristic. + */ +void NimBLECharacteristic::executeCreate(NimBLEService* pService) { + NIMBLE_LOGD(LOG_TAG, ">> executeCreate()"); +/* + if (m_handle != NULL_HANDLE) { + ESP_LOGE(LOG_TAG, "Characteristic already has a handle."); + return; + } +*/ + m_pService = pService; // Save the service to which this characteristic belongs. +/* + ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", + getUUID().toString().c_str(), + m_pService->toString().c_str()); + + esp_attr_control_t control; + control.auto_rsp = ESP_GATT_RSP_BY_APP; + + m_semaphoreCreateEvt.take("executeCreate"); + esp_err_t errRc = ::esp_ble_gatts_add_char( + m_pService->getHandle(), + getUUID().getNative(), + static_cast(m_permissions), + getProperties(), + nullptr, + &control); // Whether to auto respond or not. + + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + m_semaphoreCreateEvt.wait("executeCreate"); + + BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + while (pDescriptor != nullptr) { + pDescriptor->executeCreate(this); + pDescriptor = m_descriptorMap.getNext(); + } // End while +*/ + NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate + + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ + /* +BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { + return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); +} // getDescriptorByUUID +*/ + +/** + * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. + * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. + * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. + */ + /* +BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { + return m_descriptorMap.getByUUID(descriptorUUID); +} // getDescriptorByUUID +*/ + +/** + * @brief Get the handle of the characteristic. + * @return The handle of the characteristic. + */ +uint16_t NimBLECharacteristic::getHandle() { + return m_handle; +} // getHandle + +void NimBLECharacteristic::setAccessPermissions(uint16_t perm) { + m_permissions = perm; +} + +uint8_t NimBLECharacteristic::getProperties() { + return m_properties; +} // getProperties + + +/** + * @brief Get the service associated with this characteristic. + */ +NimBLEService* NimBLECharacteristic::getService() { + return m_pService; +} // getService + + +/** + * @brief Get the UUID of the characteristic. + * @return The UUID of the characteristic. + */ +NimBLEUUID NimBLECharacteristic::getUUID() { + return m_bleUUID; +} // getUUID + + +/** + * @brief Retrieve the current value of the characteristic. + * @return A pointer to storage containing the current characteristic value. + */ + /* +std::string BLECharacteristic::getValue() { + return m_value.getValue(); +} // getValue +*/ + +/** + * @brief Retrieve the current raw data of the characteristic. + * @return A pointer to storage containing the current characteristic data. + */ + /* +uint8_t* BLECharacteristic::getData() { + return m_value.getData(); +} // getData +*/ + +/** + * Handle a GATT server event. + */ +/* +void BLECharacteristic::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); + + switch(event) { + // Events handled: + // + // ESP_GATTS_ADD_CHAR_EVT + // ESP_GATTS_CONF_EVT + // ESP_GATTS_CONNECT_EVT + // ESP_GATTS_DISCONNECT_EVT + // ESP_GATTS_EXEC_WRITE_EVT + // ESP_GATTS_READ_EVT + // ESP_GATTS_WRITE_EVT + + // + // ESP_GATTS_EXEC_WRITE_EVT + // When we receive this event it is an indication that a previous write long needs to be committed. + // + // exec_write: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL + // + case ESP_GATTS_EXEC_WRITE_EVT: { + if(m_writeEvt){ + m_writeEvt = false; + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + m_value.commit(); + if (m_pCallbacks != nullptr) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } else { + m_value.cancel(); + } + // ??? + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, + param->write.conn_id, + param->write.trans_id, ESP_GATT_OK, nullptr); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + break; + } // ESP_GATTS_EXEC_WRITE_EVT + + + // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. + // add_char: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + case ESP_GATTS_ADD_CHAR_EVT: { + if (getHandle() == param->add_char.attr_handle) { + // we have created characteristic, now we can create descriptors + // BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); + // while (pDescriptor != nullptr) { + // pDescriptor->executeCreate(this); + // pDescriptor = m_descriptorMap.getNext(); + // } // End while + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_ADD_CHAR_EVT + + + // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t *value + // + case ESP_GATTS_WRITE_EVT: { +// We check if this write request is for us by comparing the handles in the event. If it is for us +// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need +// to send a response. If we do, then we formulate a response and send it. + if (param->write.handle == m_handle) { + if (param->write.is_prep) { + m_value.addPart(param->write.value, param->write.len); + m_writeEvt = true; + } else { + setValue(param->write.value, param->write.len); + if (m_pCallbacks != nullptr && param->write.is_prep != true) { + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } + + ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", + getHandle(), getUUID().toString().c_str()); + + char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); + ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData); + free(pHexData); + + if (param->write.need_rsp) { + esp_gatt_rsp_t rsp; + + rsp.attr_value.len = param->write.len; + rsp.attr_value.handle = m_handle; + rsp.attr_value.offset = param->write.offset; + rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + memcpy(rsp.attr_value.value, param->write.value, param->write.len); + + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, + param->write.conn_id, + param->write.trans_id, ESP_GATT_OK, &rsp); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // Response needed + + } // Match on handles. + break; + } // ESP_GATTS_WRITE_EVT + + + // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + if (param->read.handle == m_handle) { + + + +// Here's an interesting thing. The read request has the option of saying whether we need a response +// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like +// a very strange read. +// +// We have to handle the case where the data we wish to send back to the client is greater than the maximum +// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes. +// The apparent algorithm is as follows: +// +// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. +// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than +// 22 bytes, then we "just" send it and thats the end of the story. +// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. +// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. +// Because of follow on request processing, we need to maintain an offset of how much data we have already sent +// so that when a follow on request arrives, we know where to start in the data to send the next sequence. +// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response. +// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length. +// +// The following code has deliberately not been factored to make it fewer statements because this would cloud the +// the logic flow comprehension. +// + + // get mtu for peer device that we are sending read request to + uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1; + ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset); + if (param->read.need_rsp) { + ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); + esp_gatt_rsp_t rsp; + + if (param->read.is_long) { + std::string value = m_value.getValue(); + + if (value.length() - m_value.getReadOffset() < maxOffset) { + // This is the last in the chain + rsp.attr_value.len = value.length() - m_value.getReadOffset(); + rsp.attr_value.offset = m_value.getReadOffset(); + memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); + m_value.setReadOffset(0); + } else { + // There will be more to come. + rsp.attr_value.len = maxOffset; + rsp.attr_value.offset = m_value.getReadOffset(); + memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); + m_value.setReadOffset(rsp.attr_value.offset + maxOffset); + } + } else { // read.is_long == false + + if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + m_pCallbacks->onRead(this); // Invoke the read callback. + } + std::string value = m_value.getValue(); + + if (value.length() + 1 > maxOffset) { + // Too big for a single shot entry. + m_value.setReadOffset(maxOffset); + rsp.attr_value.len = maxOffset; + rsp.attr_value.offset = 0; + memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); + } else { + // Will fit in a single packet with no callbacks required. + rsp.attr_value.len = value.length(); + rsp.attr_value.offset = 0; + memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); + } + + // if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback + // m_pCallbacks->onRead(this); // Invoke the read callback. + // } + } + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; + + char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len); + ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset); + free(pHexData); + + esp_err_t errRc = ::esp_ble_gatts_send_response( + gatts_if, param->read.conn_id, + param->read.trans_id, + ESP_GATT_OK, + &rsp); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } // Response needed + } // Handle matches this characteristic. + break; + } // ESP_GATTS_READ_EVT + + + // ESP_GATTS_CONF_EVT + // + // conf: + // - esp_gatt_status_t status – The status code. + // - uint16_t conn_id – The connection used. + // + case ESP_GATTS_CONF_EVT: { + // ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); + if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet + m_semaphoreConfEvt.give(param->conf.status); + break; + } + + case ESP_GATTS_CONNECT_EVT: { + break; + } + + case ESP_GATTS_DISCONNECT_EVT: { + m_semaphoreConfEvt.give(); + break; + } + + default: { + break; + } // default + + } // switch event + + // Give each of the descriptors associated with this characteristic the opportunity to handle the + // event. + + m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); + ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); +} // handleGATTServerEvent +*/ + +/** + * @brief Send an indication. + * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication + * will block waiting a positive confirmation from the client. + * @return N/A + */ + /* +void BLECharacteristic::indicate() { + + ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); + notify(false); + ESP_LOGD(LOG_TAG, "<< indicate"); +} // indicate +*/ + +/** + * @brief Send a notify. + * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification + * will not block; it is a fire and forget. + * @return N/A. + */ + /* +void BLECharacteristic::notify(bool is_notification) { + ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); + + assert(getService() != nullptr); + assert(getService()->getServer() != nullptr); + + GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); + + if (getService()->getServer()->getConnectedCount() == 0) { + ESP_LOGD(LOG_TAG, "<< notify: No connected clients."); + return; + } + + // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled + // and, if not, prevent the notification. + + BLE2902* p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(p2902 == nullptr){ + ESP_LOGE(LOG_TAG, "Characteristic without 0x2902 descriptor"); + return; + } + if(is_notification) { + if (p2902 != nullptr && !p2902->getNotifications()) { + ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); + return; + } + } + else{ + if (p2902 != nullptr && !p2902->getIndications()) { + ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); + return; + } + } + for (auto &myPair : getService()->getServer()->getPeerDevices(false)) { + uint16_t _mtu = (myPair.second.mtu); + if (m_value.getValue().length() > _mtu - 3) { + ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); + } + + size_t length = m_value.getValue().length(); + if(!is_notification) + m_semaphoreConfEvt.take("indicate"); + esp_err_t errRc = ::esp_ble_gatts_send_indicate( + getService()->getServer()->getGattsIf(), + myPair.first, + getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify. + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); + m_semaphoreConfEvt.give(); + return; + } + if(!is_notification) + m_semaphoreConfEvt.wait("indicate"); + } + ESP_LOGD(LOG_TAG, "<< notify"); +} // Notify +*/ + +/** + * @brief Set the permission to broadcast. + * A characteristics has properties associated with it which define what it is capable of doing. + * One of these is the broadcast flag. + * @param [in] value The flag value of the property. + * @return N/A + */ +void NimBLECharacteristic::setBroadcastProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_BROADCAST); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_BROADCAST); + } +} // setBroadcastProperty + + +/** + * @brief Set the callback handlers for this characteristic. + * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. + */ +void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { + m_pCallbacks = pCallbacks; +} // setCallbacks + + +/** + * @brief Set the BLE handle associated with this characteristic. + * A user program will request that a characteristic be created against a service. When the characteristic has been + * registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the + * server/service but it is told to the service, not the characteristic associated with the service. This internally + * exposed function can be invoked by the service against this model of the characteristic to allow the characteristic + * to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events + * that will be propagated down to it which contain a handle value and now know that the event is destined for it. + * @param [in] handle The handle associated with this characteristic. + */ +void NimBLECharacteristic::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str()); + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle"); +} // setHandle + + +/** + * @brief Set the Indicate property value. + * @param [in] value Set to true if we are to allow indicate messages. + */ +void NimBLECharacteristic::setIndicateProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_INDICATE); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_INDICATE); + } +} // setIndicateProperty + + +/** + * @brief Set the Notify property value. + * @param [in] value Set to true if we are to allow notification messages. + */ +void NimBLECharacteristic::setNotifyProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_NOTIFY); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_NOTIFY); + } +} // setNotifyProperty + + +/** + * @brief Set the Read property value. + * @param [in] value Set to true if we are to allow reads. + */ +void NimBLECharacteristic::setReadProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_READ ); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_READ ); + } +} // setReadProperty + + +/** + * @brief Set the value of the characteristic. + * @param [in] data The data to set for the characteristic. + * @param [in] length The length of the data in bytes. + */ + /* +void BLECharacteristic::setValue(uint8_t* data, size_t length) { + char* pHex = BLEUtils::buildHexData(nullptr, data, length); + ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); + free(pHex); + if (length > ESP_GATT_MAX_ATTR_LEN) { + ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); + return; + } + m_value.setValue(data, length); + ESP_LOGD(LOG_TAG, "<< setValue"); +} // setValue +*/ + +/** + * @brief Set the value of the characteristic from string data. + * We set the value of the characteristic from the bytes contained in the + * string. + * @param [in] Set the value of the characteristic. + * @return N/A. + */ + /* +void BLECharacteristic::setValue(std::string value) { + setValue((uint8_t*)(value.data()), value.length()); +} // setValue + +void BLECharacteristic::setValue(uint16_t& data16) { + uint8_t temp[2]; + temp[0] = data16; + temp[1] = data16 >> 8; + setValue(temp, 2); +} // setValue + +void BLECharacteristic::setValue(uint32_t& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(int& data32) { + uint8_t temp[4]; + temp[0] = data32; + temp[1] = data32 >> 8; + temp[2] = data32 >> 16; + temp[3] = data32 >> 24; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(float& data32) { + uint8_t temp[4]; + *((float*)temp) = data32; + setValue(temp, 4); +} // setValue + +void BLECharacteristic::setValue(double& data64) { + uint8_t temp[8]; + *((double*)temp) = data64; + setValue(temp, 8); +} // setValue +*/ + +/** + * @brief Set the Write No Response property value. + * @param [in] value Set to true if we are to allow writes with no response. + */ +void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_WRITE_NO_RSP); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_WRITE_NO_RSP); + } +} // setWriteNoResponseProperty + + +/** + * @brief Set the Write property value. + * @param [in] value Set to true if we are to allow writes. + */ +void NimBLECharacteristic::setWriteProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_PROP_WRITE ); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_PROP_WRITE ); + } +} // setWriteProperty + + +/** + * @brief Return a string representation of the characteristic. + * @return A string representation of the characteristic. + */ +std::string NimBLECharacteristic::toString() { + std::string res = "UUID: " + m_bleUUID.toString() + ", handle : 0x"; + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + res += hex; + res += " "; + if (m_properties & BLE_GATT_CHR_PROP_READ ) res += "Read "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE) res += "Write "; + if (m_properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) res += "WriteNoResponse "; + if (m_properties & BLE_GATT_CHR_PROP_BROADCAST) res += "Broadcast "; + if (m_properties & BLE_GATT_CHR_PROP_NOTIFY) res += "Notify "; + if (m_properties & BLE_GATT_CHR_PROP_INDICATE) res += "Indicate "; + return res; +} // toString + + +NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {} + + +/** + * @brief Callback function to support a read request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); + NIMBLE_LOGD("BLECharacteristicCallbacks", "<< onRead"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); + NIMBLE_LOGD("BLECharacteristicCallbacks", "<< onWrite"); +} // onWrite + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h new file mode 100644 index 00000000..b39b9541 --- /dev/null +++ b/src/NimBLECharacteristic.h @@ -0,0 +1,149 @@ +/* + * NimBLECharacteristic.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * BLECharacteristic.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLECHARACTERISTIC_H_ +#define MAIN_NIMBLECHARACTERISTIC_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "NimBLEService.h" +#include "NimBLEUUID.h" +//#include +//#include +//#include "BLEDescriptor.h" +//#include "BLEValue.h" +#include "FreeRTOS.h" + +#include "host/ble_hs.h" + +#include +#include + +class NimBLEService; +//class NimBLEDescriptor; +class NimBLECharacteristicCallbacks; + +/** + * @brief A management structure for %BLE descriptors. + */ + /* +class BLEDescriptorMap { +public: + void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); + void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); + void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); + BLEDescriptor* getByUUID(const char* uuid); + BLEDescriptor* getByUUID(BLEUUID uuid); + BLEDescriptor* getByHandle(uint16_t handle); + std::string toString(); + void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + BLEDescriptor* getFirst(); + BLEDescriptor* getNext(); +private: + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; +}; +*/ + +/** + * @brief The model of a %BLE Characteristic. + * + * A BLE Characteristic is an identified value container that manages a value. It is exposed by a BLE server and + * can be read and written to by a %BLE client. + */ +class NimBLECharacteristic { +public: + NimBLECharacteristic(const char* uuid, uint32_t properties = 0); + NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties = 0); + virtual ~NimBLECharacteristic(); + +// void addDescriptor(BLEDescriptor* pDescriptor); +// BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); +// BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); + NimBLEUUID getUUID(); +// std::string getValue(); +// uint8_t* getData(); + +// void indicate(); +// void notify(bool is_notification = true); + void setBroadcastProperty(bool value); + void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); + void setIndicateProperty(bool value); + void setNotifyProperty(bool value); + void setReadProperty(bool value); +// void setValue(uint8_t* data, size_t size); +// void setValue(std::string value); +// void setValue(uint16_t& data16); +// void setValue(uint32_t& data32); +// void setValue(int& data32); +// void setValue(float& data32); +// void setValue(double& data64); + void setWriteProperty(bool value); + void setWriteNoResponseProperty(bool value); + std::string toString(); + uint16_t getHandle(); + void setAccessPermissions(uint16_t perm); + + static const uint32_t PROPERTY_READ = 1<<0; + static const uint32_t PROPERTY_WRITE = 1<<1; + static const uint32_t PROPERTY_NOTIFY = 1<<2; + static const uint32_t PROPERTY_BROADCAST = 1<<3; + static const uint32_t PROPERTY_INDICATE = 1<<4; + static const uint32_t PROPERTY_WRITE_NR = 1<<5; + +private: + + friend class NimBLEServer; + friend class NimBLEService; +// friend class BLEDescriptor; +// friend class BLECharacteristicMap; + + NimBLEUUID m_bleUUID; +// BLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + uint8_t m_properties; + NimBLECharacteristicCallbacks* m_pCallbacks; + NimBLEService* m_pService; +// BLEValue m_value; + uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; + bool m_writeEvt = false; +/* + void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); +*/ + void executeCreate(NimBLEService* pService); + uint8_t getProperties(); + NimBLEService* getService(); + void setHandle(uint16_t handle); +// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); +// FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); +}; // NimBLECharacteristic + + +/** + * @brief Callbacks that can be associated with a %BLE characteristic to inform of events. + * + * When a server application creates a %BLE characteristic, we may wish to be informed when there is either + * a read or write request to the characteristic's value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLECharacteristicCallbacks { +public: + virtual ~NimBLECharacteristicCallbacks(); + virtual void onRead(NimBLECharacteristic* pCharacteristic); + virtual void onWrite(NimBLECharacteristic* pCharacteristic); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ \ No newline at end of file diff --git a/src/NimBLECharacteristicMap.cpp b/src/NimBLECharacteristicMap.cpp new file mode 100644 index 00000000..285f0015 --- /dev/null +++ b/src/NimBLECharacteristicMap.cpp @@ -0,0 +1,143 @@ +/* + * NimBLECharacteristicMap.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * BLECharacteristicMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" +#include "NimBLELog.h" + + +/** + * @brief Return the characteristic by handle. + * @param [in] handle The handle to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + + +/** + * @brief Return the characteristic by UUID. + * @param [in] UUID The UUID to look up the characteristic. + * @return The characteristic. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(NimBLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + +/** + * @brief Get the number of characteristics in the map. + */ +uint8_t NimBLECharacteristicMap::getSize() { + return (uint8_t)m_uuidMap.size(); +} // getFirst + +/** + * @brief Get the first characteristic in the map. + * @return The first characteristic in the map. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next characteristic in the map. + * @return The next characteristic in the map. + */ +NimBLECharacteristic* NimBLECharacteristicMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLECharacteristic* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + + +/** + * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ +/* +void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto& myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent +*/ + +/** + * @brief Set the characteristic by handle. + * @param [in] handle The handle of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void NimBLECharacteristicMap::setByHandle(uint16_t handle, NimBLECharacteristic* characteristic) { + m_handleMap.insert(std::pair(handle, characteristic)); +} // setByHandle + + +/** + * @brief Set the characteristic by UUID. + * @param [in] uuid The uuid of the characteristic. + * @param [in] characteristic The characteristic to cache. + * @return N/A. + */ +void NimBLECharacteristicMap::setByUUID(NimBLECharacteristic* pCharacteristic, NimBLEUUID uuid) { + m_uuidMap.insert(std::pair(pCharacteristic, uuid.toString())); +} // setByUUID + + +/** + * @brief Return a string representation of the characteristic map. + * @return A string representation of the characteristic map. + */ +std::string NimBLECharacteristicMap::toString() { + std::string res; + int count = 0; + char hex[5]; + for (auto &myPair: m_uuidMap) { + if (count > 0) {res += "\n";} + snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle()); + count++; + res += "handle: 0x"; + res += hex; + res += ", uuid: " + myPair.first->getUUID().toString(); + } + return res; +} // toString + + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index cab51538..06ca3f04 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -46,6 +46,7 @@ #define BLEScanResults NimBLEScanResults #define BLEServer NimBLEServer #define BLEService NimBLEService +#define BLECharacteristic NimBLECharacteristic /** diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 1d525ae8..b0ca3c8f 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -118,7 +118,7 @@ class NimBLEServer { //void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); static int handleGapEvent(struct ble_gap_event *event, void *arg); //void registerApp(uint16_t); -}; // BLEServer +}; // NimBLEServer /** diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index bbbe434f..b507c00e 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -54,7 +54,7 @@ NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles) { m_handle = NULL_HANDLE; m_pServer = nullptr; //m_serializeMutex.setName("BLEService"); - //m_lastCreatedCharacteristic = nullptr; + m_lastCreatedCharacteristic = nullptr; m_numHandles = numHandles; } // NimBLEService @@ -137,33 +137,62 @@ NimBLEUUID NimBLEService::getUUID() { * @return Start the service. */ -void NimBLEService::start() { +bool NimBLEService::start() { // We ask the BLE runtime to start the service and then create each of the characteristics. // We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event // obtained as a result of calling esp_ble_gatts_create_service(). // NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); - ble_gatt_svc_def svc; - int rc = 0; + int rc = 0; + // Nimble requires an array of services to be sent to the api + // Since we are adding 1 at a time we create an array of 2 and set the type + // of the second service to 0 to indicate the end of the array. + ble_gatt_svc_def svc[2]; + ble_gatt_chr_def* pChtr_a = nullptr; - svc.type = BLE_GATT_SVC_TYPE_PRIMARY; - svc.uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; - svc.characteristics = NULL; + svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; + svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; + uint8_t numChtrs = m_characteristicMap.getSize(); + if(numChtrs < 1){ + svc[0].characteristics = NULL; + }else{ + pChtr_a = new ble_gatt_chr_def[numChtrs+1]; + NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); + for(int i=0; i < numChtrs; i++) { + pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative()->u; + pCharacteristic = m_characteristicMap.getNext(); + } + + pChtr_a[numChtrs].uuid = NULL; + svc[0].characteristics = (const ble_gatt_chr_def*)pChtr_a; + } + + svc[1].type = 0; rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)&svc); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d", rc); - return; + NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + if(pChtr_a != nullptr) { + delete[] pChtr_a; + } + return false; } rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)&svc); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d", rc); - return; + NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + if(pChtr_a != nullptr) { + delete[] pChtr_a; + } + return false; } - + if(pChtr_a != nullptr) { + delete[] pChtr_a; + } + + return true; /* if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); return; @@ -247,20 +276,19 @@ uint16_t NimBLEService::getHandle() { * @brief Add a characteristic to the service. * @param [in] pCharacteristic A pointer to the characteristic to be added. */ - /* -void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { +void NimBLEService::addCharacteristic(NimBLECharacteristic* pCharacteristic) { // We maintain a mapping of characteristics owned by this service. These are managed by the // BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic // to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF). - ESP_LOGD(LOG_TAG, ">> addCharacteristic()"); - ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", + NIMBLE_LOGD(LOG_TAG, ">> addCharacteristic()"); + NIMBLE_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s", pCharacteristic->getUUID().toString().c_str(), toString().c_str()); // Check that we don't add the same characteristic twice. if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) { - ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); + NIMBLE_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one"); //return; } @@ -268,9 +296,9 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT. m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID()); - ESP_LOGD(LOG_TAG, "<< addCharacteristic()"); + NIMBLE_LOGD(LOG_TAG, "<< addCharacteristic()"); } // addCharacteristic -*/ + /** * @brief Create a new BLE Characteristic associated with this service. @@ -278,11 +306,10 @@ void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) { * @param [in] properties - The properties of the characteristic. * @return The new BLE characteristic. */ - /* -BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) { - return createCharacteristic(BLEUUID(uuid), properties); +NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint32_t properties) { + return createCharacteristic(NimBLEUUID(uuid), properties); } -*/ + /** * @brief Create a new BLE Characteristic associated with this service. @@ -290,13 +317,12 @@ BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t p * @param [in] properties - The properties of the characteristic. * @return The new BLE characteristic. */ - /* -BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) { - BLECharacteristic* pCharacteristic = new BLECharacteristic(uuid, properties); +NimBLECharacteristic* NimBLEService::createCharacteristic(NimBLEUUID uuid, uint32_t properties) { + NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties); addCharacteristic(pCharacteristic); return pCharacteristic; } // createCharacteristic -*/ + /** * @brief Handle a GATTS server event. @@ -400,16 +426,16 @@ void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t } // handleGATTServerEvent */ -/* -BLECharacteristic* BLEService::getCharacteristic(const char* uuid) { - return getCharacteristic(BLEUUID(uuid)); + +NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) { + return getCharacteristic(NimBLEUUID(uuid)); } -BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) { +NimBLECharacteristic* NimBLEService::getCharacteristic(NimBLEUUID uuid) { return m_characteristicMap.getByUUID(uuid); } -*/ + /** * @brief Return a string representation of this service. @@ -435,11 +461,10 @@ std::string NimBLEService::toString() { * is associated with the last characteristics created and we need that information. * @return The last created characteristic. */ - /* -BLECharacteristic* BLEService::getLastCreatedCharacteristic() { +NimBLECharacteristic* NimBLEService::getLastCreatedCharacteristic() { return m_lastCreatedCharacteristic; } // getLastCreatedCharacteristic -*/ + /** * @brief Get the BLE server associated with this service. diff --git a/src/NimBLEService.h b/src/NimBLEService.h index bdb73bc6..6b031ff9 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -18,37 +18,38 @@ #if defined(CONFIG_BT_ENABLED) //#include - -//#include "BLECharacteristic.h" +#include "NimBLECharacteristic.h" #include "NimBLEServer.h" #include "NimBLEUUID.h" #include "FreeRTOS.h" + class NimBLEServer; +class NimBLECharacteristic; /** * @brief A data mapping used to manage the set of %BLE characteristics known to the server. */ - /* -class BLECharacteristicMap { +class NimBLECharacteristicMap { public: - void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid); - void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid); - void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic); - BLECharacteristic* getByUUID(const char* uuid); - BLECharacteristic* getByUUID(BLEUUID uuid); - BLECharacteristic* getByHandle(uint16_t handle); - BLECharacteristic* getFirst(); - BLECharacteristic* getNext(); + void setByUUID(NimBLECharacteristic* pCharacteristic, const char* uuid); + void setByUUID(NimBLECharacteristic* pCharacteristic, NimBLEUUID uuid); + void setByHandle(uint16_t handle, NimBLECharacteristic* pCharacteristic); + NimBLECharacteristic* getByUUID(const char* uuid); + NimBLECharacteristic* getByUUID(NimBLEUUID uuid); + NimBLECharacteristic* getByHandle(uint16_t handle); + NimBLECharacteristic* getFirst(); + NimBLECharacteristic* getNext(); + uint8_t getSize(); std::string toString(); - void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); +// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); private: - std::map m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; + std::map m_uuidMap; + std::map m_handleMap; + std::map::iterator m_iterator; }; -*/ + /** * @brief The model of a %BLE service. @@ -56,17 +57,17 @@ class BLECharacteristicMap { */ class NimBLEService { public: - //void addCharacteristic(BLECharacteristic* pCharacteristic); - //BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); - //BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties); + void addCharacteristic(NimBLECharacteristic* pCharacteristic); + NimBLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); + NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, uint32_t properties); void dump(); void executeCreate(NimBLEServer* pServer); void executeDelete(); - //BLECharacteristic* getCharacteristic(const char* uuid); - //BLECharacteristic* getCharacteristic(BLEUUID uuid); + NimBLECharacteristic* getCharacteristic(const char* uuid); + NimBLECharacteristic* getCharacteristic(NimBLEUUID uuid); NimBLEUUID getUUID(); NimBLEServer* getServer(); - void start(); + bool start(); // void stop(); std::string toString(); uint16_t getHandle(); @@ -81,9 +82,9 @@ class NimBLEService { // friend class BLECharacteristic; friend class NimBLEDevice; -// BLECharacteristicMap m_characteristicMap; + NimBLECharacteristicMap m_characteristicMap; uint16_t m_handle; -// BLECharacteristic* m_lastCreatedCharacteristic = nullptr; + NimBLECharacteristic* m_lastCreatedCharacteristic = nullptr; NimBLEServer* m_pServer = nullptr; NimBLEUUID m_uuid; @@ -94,7 +95,7 @@ class NimBLEService { uint16_t m_numHandles; -// BLECharacteristic* getLastCreatedCharacteristic(); + NimBLECharacteristic* getLastCreatedCharacteristic(); // void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setHandle(uint16_t handle); //void setService(esp_gatt_srvc_id_t srvc_id); From 52b279380792f310ecf6417797bb252aa66b1575 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 3 Mar 2020 21:58:13 -0700 Subject: [PATCH 05/62] Add characteristic data to service start(), add advertising. --- src/NimBLEAdvertising.cpp | 503 ++++++++++++++++++++++++++++++++++++++ src/NimBLEAdvertising.h | 87 +++++++ src/NimBLEDevice.cpp | 16 ++ src/NimBLEDevice.h | 3 + src/NimBLEServer.h | 2 +- src/NimBLEService.cpp | 9 +- 6 files changed, 618 insertions(+), 2 deletions(-) create mode 100644 src/NimBLEAdvertising.cpp create mode 100644 src/NimBLEAdvertising.h diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp new file mode 100644 index 00000000..be0b950f --- /dev/null +++ b/src/NimBLEAdvertising.cpp @@ -0,0 +1,503 @@ +/* + * NimBLEAdvertising.cpp + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.cpp + * + * This class encapsulates advertising a BLE Server. + * Created on: Jun 21, 2017 + * Author: kolban + * + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "NimBLEAdvertising.h" +//#include +#include "NimBLEUtils.h" +#include "NimBLELog.h" +//#include "GeneralUtils.h" + +static const char* LOG_TAG = "NimBLEAdvertising"; + + +/** + * @brief Construct a default advertising object. + * + */ +NimBLEAdvertising::NimBLEAdvertising() { + memset(&m_advData, 0, sizeof m_advData); + const char *name = ble_svc_gap_device_name(); + + m_advData.set_scan_rsp = false; + m_advData.name = (uint8_t *)name; + m_advData.name_len = strlen(name); + m_advData.name_is_complete = 1; + m_advData.tx_pwr_lvl_is_present = 1; + m_advData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); +/* m_advData.min_interval = 0x20; + m_advData.max_interval = 0x40; + m_advData.appearance = 0x00; + m_advData.manufacturer_len = 0; + m_advData.p_manufacturer_data = nullptr; + m_advData.service_data_len = 0; + m_advData.p_service_data = nullptr; + m_advData.service_uuid_len = 0; + m_advData.p_service_uuid = nullptr; + + m_advParams.adv_int_min = 0x20; + m_advParams.adv_int_max = 0x40; + m_advParams.adv_type = ADV_TYPE_IND; + m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + m_advParams.channel_map = ADV_CHNL_ALL; + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC; + + m_customAdvData = false; // No custom advertising data + m_customScanResponseData = false; // No custom scan response data + */ +} // BLEAdvertising + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The UUID of the service to expose. + */ +void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { + m_serviceUUIDs.push_back(serviceUUID); +} // addServiceUUID + + +/** + * @brief Add a service uuid to exposed list of services. + * @param [in] serviceUUID The string representation of the service to expose. + */ +void BLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(BLEUUID(serviceUUID)); +} // addServiceUUID + + +/** + * @brief Set the device appearance in the advertising data. + * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml. + * @param [in] appearance The appearance of the device in the advertising data. + * @return N/A. + */ +void BLEAdvertising::setAppearance(uint16_t appearance) { + m_advData.appearance = appearance; +} // setAppearance + +void BLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advParams.adv_int_min = mininterval; +} // setMinInterval + +void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advParams.adv_int_max = maxinterval; +} // setMaxInterval + +void BLEAdvertising::setMinPreferred(uint16_t mininterval) { + m_advData.min_interval = mininterval; +} // + +void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + m_advData.max_interval = maxinterval; +} // + +void BLEAdvertising::setScanResponse(bool set) { + m_scanResp = set; +} + +/** + * @brief Set the filtering for the scan filter. + * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. + * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. + */ +void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); + if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && !connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (!scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } + if (scanRequestWhitelistOnly && connectWhitelistOnly) { + m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST; + ESP_LOGD(LOG_TAG, "<< setScanFilter"); + return; + } +} // setScanFilter + + +/** + * @brief Set the advertisement data that is to be published in a regular advertisement. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setAdvertisementData"); + esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. + ESP_LOGD(LOG_TAG, "<< setAdvertisementData"); +} // setAdvertisementData + + +/** + * @brief Set the advertisement data that is to be published in a scan response. + * @param [in] advertisementData The data to be advertised. + */ +void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { + ESP_LOGD(LOG_TAG, ">> setScanResponseData"); + esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( + (uint8_t*)advertisementData.getPayload().data(), + advertisementData.getPayload().length()); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + } + m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. + ESP_LOGD(LOG_TAG, "<< setScanResponseData"); +} // setScanResponseData + +/** + * @brief Start advertising. + * Start advertising. + * @return N/A. + */ +void BLEAdvertising::start() { + ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + + // We have a vector of service UUIDs that we wish to advertise. In order to use the + // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) + // representations. If we have 1 or more services to advertise then we allocate enough + // storage to host them and then copy them in one at a time into the contiguous storage. + int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { + m_advData.service_uuid_len = 16 * numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + uint8_t* p = m_advData.p_service_uuid; + for (int i = 0; i < numServices; i++) { + ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); + BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); + memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); + p += 16; + } + } else { + m_advData.service_uuid_len = 0; + ESP_LOGD(LOG_TAG, "- no services advertised"); + } + + esp_err_t errRc; + + if (!m_customAdvData) { + // Set the configuration for advertising. + m_advData.set_scan_rsp = false; + m_advData.include_name = !m_scanResp; + m_advData.include_txpower = !m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + if (!m_customScanResponseData && m_scanResp) { + m_advData.set_scan_rsp = true; + m_advData.include_name = m_scanResp; + m_advData.include_txpower = m_scanResp; + errRc = ::esp_ble_gap_config_adv_data(&m_advData); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + } + + // If we had services to advertise then we previously allocated some storage for them. + // Here we release that storage. + if (m_advData.service_uuid_len > 0) { + delete[] m_advData.p_service_uuid; + m_advData.p_service_uuid = nullptr; + } + + // Start advertising. + errRc = ::esp_ble_gap_start_advertising(&m_advParams); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + ESP_LOGD(LOG_TAG, "<< start"); +} // start + + +/** + * @brief Stop advertising. + * Stop advertising. + * @return N/A. + */ +void BLEAdvertising::stop() { + ESP_LOGD(LOG_TAG, ">> stop"); + esp_err_t errRc = ::esp_ble_gap_stop_advertising(); + if (errRc != ESP_OK) { + ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + ESP_LOGD(LOG_TAG, "<< stop"); +} // stop + +/** + * @brief Add data to the payload to be advertised. + * @param [in] data The data to be added to the payload. + */ +void BLEAdvertisementData::addData(std::string data) { + if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { + return; + } + m_payload.append(data); +} // addData + + +/** + * @brief Set the appearance. + * @param [in] appearance The appearance code value. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml + */ +void BLEAdvertisementData::setAppearance(uint16_t appearance) { + char cdata[2]; + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 + addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); +} // setAppearance + + +/** + * @brief Set the complete services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); + break; + } + + default: + return; + } +} // setCompleteServices + + +/** + * @brief Set the advertisement flags. + * @param [in] The flags to be set in the advertisement. + * + * * ESP_BLE_ADV_FLAG_LIMIT_DISC + * * ESP_BLE_ADV_FLAG_GEN_DISC + * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT + * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT + * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT + * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC + */ +void BLEAdvertisementData::setFlags(uint8_t flag) { + char cdata[3]; + cdata[0] = 2; + cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 + cdata[2] = flag; + addData(std::string(cdata, 3)); +} // setFlag + + + +/** + * @brief Set manufacturer specific data. + * @param [in] data Manufacturer data. + */ +void BLEAdvertisementData::setManufacturerData(std::string data) { + ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); + char cdata[2]; + cdata[0] = data.length() + 1; + cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff + addData(std::string(cdata, 2) + data); + ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); +} // setManufacturerData + + +/** + * @brief Set the name. + * @param [in] The complete name of the device. + */ +void BLEAdvertisementData::setName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setName"); +} // setName + + +/** + * @brief Set the partial services. + * @param [in] uuid The single service to advertise. + */ +void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x02] [LL] [HH] + cdata[0] = 3; + cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); + break; + } + + case 32: { + // [Len] [0x04] [LL] [LL] [HH] [HH] + cdata[0] = 5; + cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); + break; + } + + case 128: { + // [Len] [0x04] [0] [1] ... [15] + cdata[0] = 17; + cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); + break; + } + + default: + return; + } +} // setPartialServices + + +/** + * @brief Set the service data (UUID + data) + * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. + * @param [in] data The data to be associated with the service data advert. + */ +void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { + char cdata[2]; + switch (uuid.bitSize()) { + case 16: { + // [Len] [0x16] [UUID16] data + cdata[0] = data.length() + 3; + cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); + break; + } + + case 32: { + // [Len] [0x20] [UUID32] data + cdata[0] = data.length() + 5; + cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); + break; + } + + case 128: { + // [Len] [0x21] [UUID128] data + cdata[0] = data.length() + 17; + cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); + break; + } + + default: + return; + } +} // setServiceData + + +/** + * @brief Set the short name. + * @param [in] The short name of the device. + */ +void BLEAdvertisementData::setShortName(std::string name) { + ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); + char cdata[2]; + cdata[0] = name.length() + 1; + cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 + addData(std::string(cdata, 2) + name); + ESP_LOGD("BLEAdvertisementData", "<< setShortName"); +} // setShortName + + +/** + * @brief Retrieve the payload that is to be advertised. + * @return The payload that is to be advertised. + */ +std::string BLEAdvertisementData::getPayload() { + return m_payload; +} // getPayload + +void BLEAdvertising::handleGAPEvent( + esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t* param) { + + ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event); + + switch(event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { + // m_semaphoreSetAdv.give(); + break; + } + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { + ESP_LOGI(LOG_TAG, "STOP advertising"); + start(); + break; + } + default: + break; + } +} + + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h new file mode 100644 index 00000000..b8cfa876 --- /dev/null +++ b/src/NimBLEAdvertising.h @@ -0,0 +1,87 @@ +/* + * NimBLEAdvertising.h + * + * Created: on March 3, 2020 + * Author H2zero + * + * Originally: + * + * BLEAdvertising.h + * + * Created on: Jun 21, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEADVERTISING_H_ +#define MAIN_BLEADVERTISING_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +//#include +#include "NimBLEUUID.h" +#include "FreeRTOS.h" + +#include + + +/** + * @brief Advertisement data set by the programmer to be published by the %BLE server. + */ +class NimBLEAdvertisementData { + // Only a subset of the possible BLE architected advertisement fields are currently exposed. Others will + // be exposed on demand/request or as time permits. + // +public: + void setAppearance(uint16_t appearance); + void setCompleteServices(NimBLEUUID uuid); + void setFlags(uint8_t); + void setManufacturerData(std::string data); + void setName(std::string name); + void setPartialServices(NimBLEUUID uuid); + void setServiceData(NimBLEUUID uuid, std::string data); + void setShortName(std::string name); + void addData(std::string data); // Add data to the payload. + std::string getPayload(); // Retrieve the current advert payload. + +private: + friend class NimBLEAdvertising; + std::string m_payload; // The payload of the advertisement. +}; // NimBLEAdvertisementData + + +/** + * @brief Perform and manage %BLE advertising. + * + * A %BLE server will want to perform advertising in order to make itself known to %BLE clients. + */ +class NimBLEAdvertising { +public: + NimBLEAdvertising(); + void addServiceUUID(NimBLEUUID serviceUUID); + void addServiceUUID(const char* serviceUUID); + void start(); + void stop(); + void setAppearance(uint16_t appearance); + void setMaxInterval(uint16_t maxinterval); + void setMinInterval(uint16_t mininterval); + void setAdvertisementData(NimBLEAdvertisementData& advertisementData); + void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); + void setScanResponseData(NimBLEAdvertisementData& advertisementData); + void setPrivateAddress(uint8_t type = BLE_ADDR_TYPE_RANDOM); + + //void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); + void setScanResponse(bool); + +private: + ble_hs_adv_fields m_advData; + ble_gap_adv_params m_advParams; + std::vector m_serviceUUIDs; + bool m_customAdvData = false; // Are we using custom advertising data? + bool m_customScanResponseData = false; // Are we using custom scan response data? +// FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); + bool m_scanResp = true; + +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_BLEADVERTISING_H_ */ \ No newline at end of file diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index b3c46e5b..bed21980 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -92,6 +92,22 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; } // createServer +NimBLEAdvertising* NimBLEDevice::getAdvertising() { + if(m_bleAdvertising == nullptr) { + m_bleAdvertising = new NimBLEAdvertising(); + } + return m_bleAdvertising; +} + +void NimBLEDevice::startAdvertising() { + getAdvertising()->start(); +} // startAdvertising + +void NimBLEDevice::stopAdvertising() { + getAdvertising()->stop(); +} // stopAdvertising + + /** * @brief Retrieve the Scan object that we use for scanning. * @return The scanning object reference. This is a singleton object. The caller should not diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 06ca3f04..9f9f9b2d 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -84,6 +84,9 @@ class NimBLEDevice { static bool isIgnored(NimBLEAddress address); static void addIgnored(NimBLEAddress address); static void removeIgnored(NimBLEAddress address); + static NimBLEAdvertising* getAdvertising(); + static void startAdvertising(); + static void stopAdvertising(); static std::list* getClientList(); diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index b0ca3c8f..5eed9ba6 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -21,7 +21,7 @@ // #include "BLEDevice.h" #include "NimBLEAddress.h" #include "NimBLEUUID.h" -//#include "BLEAdvertising.h" +#include "NimBLEAdvertising.h" //#include "BLECharacteristic.h" #include "NimBLEService.h" #include "NimBLESecurity.h" diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index b507c00e..9c16e1e5 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -161,6 +161,12 @@ bool NimBLEService::start() { NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); for(int i=0; i < numChtrs; i++) { pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative()->u; + // pChtr_a[i].access_cb = NULL; + // pChtr_a[i].arg = NULL; + pChtr_a[i].descriptors = NULL; + pChtr_a[i].flags = pCharacteristic->m_properties; + pChtr_a[i].min_key_size = 0; + pChtr_a[i].val_handle = &pCharacteristic->m_handle; pCharacteristic = m_characteristicMap.getNext(); } @@ -192,7 +198,7 @@ bool NimBLEService::start() { delete[] pChtr_a; } - return true; + /* if (m_handle == NULL_HANDLE) { ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); return; @@ -218,6 +224,7 @@ bool NimBLEService::start() { m_semaphoreStartEvt.wait("start"); */ NIMBLE_LOGD(LOG_TAG, "<< start()"); + return true; } // start From e3b8205e79e7fa767a066607699a1d7f7778d2ba Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 4 Mar 2020 15:39:41 -0700 Subject: [PATCH 06/62] Starting work on advertising. --- src/NimBLEAdvertising.cpp | 91 ++++++++++++++++++++++++++++----------- src/NimBLEAdvertising.h | 8 ++-- src/NimBLEService.cpp | 4 +- 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index be0b950f..d1c75829 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -30,18 +30,26 @@ static const char* LOG_TAG = "NimBLEAdvertising"; */ NimBLEAdvertising::NimBLEAdvertising() { memset(&m_advData, 0, sizeof m_advData); + memset(&m_advParams, 0, sizeof m_advParams); const char *name = ble_svc_gap_device_name(); - m_advData.set_scan_rsp = false; m_advData.name = (uint8_t *)name; m_advData.name_len = strlen(name); m_advData.name_is_complete = 1; m_advData.tx_pwr_lvl_is_present = 1; m_advData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); -/* m_advData.min_interval = 0x20; - m_advData.max_interval = 0x40; m_advData.appearance = 0x00; + m_advData.appearance_is_present = 0; + m_advData.mfg_data_len = 0; + m_advData.mfg_data = nullptr; + + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; + m_advParams.itvl_min = 0x20; + m_advParams.itvl_max = 0x40; + +/* m_advData.set_scan_rsp = false; m_advData.manufacturer_len = 0; m_advData.p_manufacturer_data = nullptr; m_advData.service_data_len = 0; @@ -60,14 +68,14 @@ NimBLEAdvertising::NimBLEAdvertising() { m_customAdvData = false; // No custom advertising data m_customScanResponseData = false; // No custom scan response data */ -} // BLEAdvertising +} // NimBLEAdvertising /** * @brief Add a service uuid to exposed list of services. * @param [in] serviceUUID The UUID of the service to expose. */ -void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { +void NimBLEAdvertising::addServiceUUID(NimBLEUUID serviceUUID) { m_serviceUUIDs.push_back(serviceUUID); } // addServiceUUID @@ -76,8 +84,8 @@ void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) { * @brief Add a service uuid to exposed list of services. * @param [in] serviceUUID The string representation of the service to expose. */ -void BLEAdvertising::addServiceUUID(const char* serviceUUID) { - addServiceUUID(BLEUUID(serviceUUID)); +void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) { + addServiceUUID(NimBLEUUID(serviceUUID)); } // addServiceUUID @@ -88,8 +96,9 @@ void BLEAdvertising::addServiceUUID(const char* serviceUUID) { * @param [in] appearance The appearance of the device in the advertising data. * @return N/A. */ -void BLEAdvertising::setAppearance(uint16_t appearance) { +void NimBLEAdvertising::setAppearance(uint16_t appearance) { m_advData.appearance = appearance; + m_advData.appearance_is_present = 1; } // setAppearance void BLEAdvertising::setMinInterval(uint16_t mininterval) { @@ -99,7 +108,7 @@ void BLEAdvertising::setMinInterval(uint16_t mininterval) { void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { m_advParams.adv_int_max = maxinterval; } // setMaxInterval - +/* void BLEAdvertising::setMinPreferred(uint16_t mininterval) { m_advData.min_interval = mininterval; } // @@ -107,8 +116,8 @@ void BLEAdvertising::setMinPreferred(uint16_t mininterval) { void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { m_advData.max_interval = maxinterval; } // - -void BLEAdvertising::setScanResponse(bool set) { +*/ +void NimBLEAdvertising::setScanResponse(bool set) { m_scanResp = set; } @@ -117,6 +126,7 @@ void BLEAdvertising::setScanResponse(bool set) { * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ + /* void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { @@ -140,22 +150,23 @@ void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWh return; } } // setScanFilter - +*/ /** * @brief Set the advertisement data that is to be published in a regular advertisement. * @param [in] advertisementData The data to be advertised. */ -void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) { - ESP_LOGD(LOG_TAG, ">> setAdvertisementData"); - esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw( + +void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setAdvertisementData"); + int rc = ble_gap_adv_set_data( (uint8_t*)advertisementData.getPayload().data(), advertisementData.getPayload().length()); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } m_customAdvData = true; // Set the flag that indicates we are using custom advertising data. - ESP_LOGD(LOG_TAG, "<< setAdvertisementData"); + NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData"); } // setAdvertisementData @@ -163,6 +174,7 @@ void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementDat * @brief Set the advertisement data that is to be published in a scan response. * @param [in] advertisementData The data to be advertised. */ + /* void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { ESP_LOGD(LOG_TAG, ">> setScanResponseData"); esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( @@ -174,15 +186,37 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. ESP_LOGD(LOG_TAG, "<< setScanResponseData"); } // setScanResponseData - +*/ /** * @brief Start advertising. * Start advertising. * @return N/A. */ -void BLEAdvertising::start() { - ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); - +void NimBLEAdvertising::start() { + NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + + int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { + for (int i = 0; i < numServices; i++) { + if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_16){ + m_advData.uuids16 + } + } + + m_advData.service_uuid_len = 16 * numServices; + m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + uint8_t* p = m_advData.p_service_uuid; + for (int i = 0; i < numServices; i++) { + ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); + BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); + memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); + p += 16; + } + } else { + m_advData.service_uuid_len = 0; + ESP_LOGD(LOG_TAG, "- no services advertised"); + } +/* // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) // representations. If we have 1 or more services to advertise then we allocate enough @@ -241,7 +275,8 @@ void BLEAdvertising::start() { ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } - ESP_LOGD(LOG_TAG, "<< start"); +*/ + NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); } // start @@ -250,20 +285,23 @@ void BLEAdvertising::start() { * Stop advertising. * @return N/A. */ -void BLEAdvertising::stop() { - ESP_LOGD(LOG_TAG, ">> stop"); +void NimBLEAdvertising::stop() { + NIMBLE_LOGD(LOG_TAG, ">> stop"); + /* esp_err_t errRc = ::esp_ble_gap_stop_advertising(); if (errRc != ESP_OK) { ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); return; } - ESP_LOGD(LOG_TAG, "<< stop"); + */ + NIMBLE_LOGD(LOG_TAG, "<< stop"); } // stop /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. */ + void BLEAdvertisementData::addData(std::string data) { if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { return; @@ -279,6 +317,7 @@ void BLEAdvertisementData::addData(std::string data) { * See also: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml */ + void BLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index b8cfa876..678fb239 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -64,13 +64,13 @@ class NimBLEAdvertising { void setMaxInterval(uint16_t maxinterval); void setMinInterval(uint16_t mininterval); void setAdvertisementData(NimBLEAdvertisementData& advertisementData); - void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); - void setScanResponseData(NimBLEAdvertisementData& advertisementData); +// void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); +// void setScanResponseData(NimBLEAdvertisementData& advertisementData); void setPrivateAddress(uint8_t type = BLE_ADDR_TYPE_RANDOM); //void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); - void setMinPreferred(uint16_t); - void setMaxPreferred(uint16_t); +// void setMinPreferred(uint16_t); +// void setMaxPreferred(uint16_t); void setScanResponse(bool); private: diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 9c16e1e5..08073628 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -152,7 +152,7 @@ bool NimBLEService::start() { ble_gatt_chr_def* pChtr_a = nullptr; svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; - svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; + svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative(); uint8_t numChtrs = m_characteristicMap.getSize(); if(numChtrs < 1){ svc[0].characteristics = NULL; @@ -160,7 +160,7 @@ bool NimBLEService::start() { pChtr_a = new ble_gatt_chr_def[numChtrs+1]; NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); for(int i=0; i < numChtrs; i++) { - pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative()->u; + pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative(); // pChtr_a[i].access_cb = NULL; // pChtr_a[i].arg = NULL; pChtr_a[i].descriptors = NULL; From 7de064b13ca053df12da45c3c25073aab22553d3 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 5 Mar 2020 16:10:46 -0700 Subject: [PATCH 07/62] A bit more work on advertising. --- src/NimBLEAdvertising.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index d1c75829..70e49e7b 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -194,15 +194,25 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData */ void NimBLEAdvertising::start() { NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); - + ble_uuid16_t* uuids16 = nullptr; + ble_uuid32_t* uuids32 = nullptr; + ble_uuid128_t* uuids128 = nullptr; int numServices = m_serviceUUIDs.size(); + if (numServices > 0) { for (int i = 0; i < numServices; i++) { if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_16){ - m_advData.uuids16 + m_advData.num_uuids16++; + } + if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_32){ + m_advData.num_uuids32++; + } + if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_128){ + m_advData.num_uuids128++; } } - + +/* m_advData.service_uuid_len = 16 * numServices; m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; uint8_t* p = m_advData.p_service_uuid; From 8eeef210ddb75875dfda00c26495691ebf328edb Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 5 Mar 2020 23:03:06 -0700 Subject: [PATCH 08/62] More Advertising work. --- src/NimBLEAdvertising.cpp | 126 +++++++++++++++++++++++++++----------- src/NimBLEAdvertising.h | 24 +++++--- src/NimBLEDevice.cpp | 1 + src/NimBLEDevice.h | 1 + src/NimBLEService.cpp | 4 +- src/NimBLEService.h | 4 +- 6 files changed, 110 insertions(+), 50 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 70e49e7b..d35592d5 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -15,6 +15,7 @@ */ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "services/gap/ble_svc_gap.h" #include "NimBLEAdvertising.h" //#include #include "NimBLEUtils.h" @@ -101,12 +102,12 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) { m_advData.appearance_is_present = 1; } // setAppearance -void BLEAdvertising::setMinInterval(uint16_t mininterval) { - m_advParams.adv_int_min = mininterval; +void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { + m_advParams.itvl_min = mininterval; } // setMinInterval -void BLEAdvertising::setMaxInterval(uint16_t maxinterval) { - m_advParams.adv_int_max = maxinterval; +void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { + m_advParams.itvl_max = maxinterval; } // setMaxInterval /* void BLEAdvertising::setMinPreferred(uint16_t mininterval) { @@ -198,20 +199,65 @@ void NimBLEAdvertising::start() { ble_uuid32_t* uuids32 = nullptr; ble_uuid128_t* uuids128 = nullptr; int numServices = m_serviceUUIDs.size(); + int rc = 0; + uint8_t addressType; if (numServices > 0) { for (int i = 0; i < numServices; i++) { - if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_16){ + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16){ m_advData.num_uuids16++; + if(nullptr == (uuids16 = (ble_uuid16_t*)realloc(uuids16, m_advData.num_uuids16 * sizeof(ble_uuid16_t)))) { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&uuids16[m_advData.num_uuids16].value, &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); + uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; + //m_advData.uuids16_is_complete = 1; + m_advData.uuids16 = uuids16; } - if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_32){ + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32){ m_advData.num_uuids32++; + if(nullptr == (uuids32 = (ble_uuid32_t*)realloc(uuids32, m_advData.num_uuids32 * sizeof(ble_uuid32_t)))) { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&uuids32[m_advData.num_uuids32].value, &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); + uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; + //m_advData.uuids32_is_complete = 1; + m_advData.uuids32 = uuids32; } - if(m_serviceUUIDs[i].getNative().u.type == BLE_UUID_TYPE_128){ + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){ m_advData.num_uuids128++; + if(nullptr == (uuids128 = (ble_uuid128_t*)realloc(uuids128, m_advData.num_uuids128 * sizeof(ble_uuid128_t)))) { + NIMBLE_LOGE(LOG_TAG, "Error, no mem"); + abort(); + } + memcpy(&uuids128[m_advData.num_uuids128].value, &m_serviceUUIDs[i].getNative()->u128.value, 16); + uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; + //m_advData.uuids128_is_complete = 1; + m_advData.uuids128 = uuids128; } } - + + rc = ble_gap_adv_set_fields(&m_advData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + rc = ble_hs_id_infer_auto(0, &addressType); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, + &m_advParams, NULL, NULL); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error enabling advertisement; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + } /* m_advData.service_uuid_len = 16 * numServices; m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; @@ -226,6 +272,7 @@ void NimBLEAdvertising::start() { m_advData.service_uuid_len = 0; ESP_LOGD(LOG_TAG, "- no services advertised"); } +*/ /* // We have a vector of service UUIDs that we wish to advertise. In order to use the // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) @@ -311,14 +358,14 @@ void NimBLEAdvertising::stop() { * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. */ - -void BLEAdvertisementData::addData(std::string data) { + /* +void NimBLEAdvertisementData::addData(std::string data) { if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { return; } m_payload.append(data); } // addData - +*/ /** * @brief Set the appearance. @@ -327,20 +374,21 @@ void BLEAdvertisementData::addData(std::string data) { * See also: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml */ - -void BLEAdvertisementData::setAppearance(uint16_t appearance) { + /* +void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); } // setAppearance - +*/ /** * @brief Set the complete services. * @param [in] uuid The single service to advertise. */ -void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { + /* +void NimBLEAdvertisementData::setCompleteServices(BLEUUID uuid) { char cdata[2]; switch (uuid.bitSize()) { case 16: { @@ -371,7 +419,7 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { return; } } // setCompleteServices - +*/ /** * @brief Set the advertisement flags. @@ -384,21 +432,23 @@ void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) { * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC */ -void BLEAdvertisementData::setFlags(uint8_t flag) { + /* +void NimBLEAdvertisementData::setFlags(uint8_t flag) { char cdata[3]; cdata[0] = 2; cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 cdata[2] = flag; addData(std::string(cdata, 3)); } // setFlag - +*/ /** * @brief Set manufacturer specific data. * @param [in] data Manufacturer data. */ -void BLEAdvertisementData::setManufacturerData(std::string data) { + /* +void NimBLEAdvertisementData::setManufacturerData(std::string data) { ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); char cdata[2]; cdata[0] = data.length() + 1; @@ -406,27 +456,29 @@ void BLEAdvertisementData::setManufacturerData(std::string data) { addData(std::string(cdata, 2) + data); ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData - +*/ /** * @brief Set the name. * @param [in] The complete name of the device. */ -void BLEAdvertisementData::setName(std::string name) { - ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str()); + /* +void NimBLEAdvertisementData::setName(std::string name) { + ESP_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 addData(std::string(cdata, 2) + name); - ESP_LOGD("BLEAdvertisementData", "<< setName"); + ESP_LOGD("NimBLEAdvertisementData", "<< setName"); } // setName - +*/ /** * @brief Set the partial services. * @param [in] uuid The single service to advertise. */ -void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { + /* +void NimBLEAdvertisementData::setPartialServices(NimBLEUUID uuid) { char cdata[2]; switch (uuid.bitSize()) { case 16: { @@ -457,14 +509,15 @@ void BLEAdvertisementData::setPartialServices(BLEUUID uuid) { return; } } // setPartialServices - +*/ /** * @brief Set the service data (UUID + data) * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. * @param [in] data The data to be associated with the service data advert. */ -void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { + /* +void NimBLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { char cdata[2]; switch (uuid.bitSize()) { case 16: { @@ -495,31 +548,32 @@ void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { return; } } // setServiceData - +*/ /** * @brief Set the short name. * @param [in] The short name of the device. */ -void BLEAdvertisementData::setShortName(std::string name) { - ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str()); + /* +void NimBLEAdvertisementData::setShortName(std::string name) { + ESP_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 addData(std::string(cdata, 2) + name); - ESP_LOGD("BLEAdvertisementData", "<< setShortName"); + ESP_LOGD("NimBLEAdvertisementData", "<< setShortName"); } // setShortName - +*/ /** * @brief Retrieve the payload that is to be advertised. * @return The payload that is to be advertised. */ -std::string BLEAdvertisementData::getPayload() { +std::string NimBLEAdvertisementData::getPayload() { return m_payload; } // getPayload - -void BLEAdvertising::handleGAPEvent( +/* +void NimBLEAdvertising::handleGAPEvent( esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param) { @@ -547,6 +601,6 @@ void BLEAdvertising::handleGAPEvent( break; } } - +*/ #endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 678fb239..3f32b7a3 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -16,10 +16,14 @@ #define MAIN_BLEADVERTISING_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "host/ble_hs.h" +#include "host/ble_gap.h" //#include #include "NimBLEUUID.h" #include "FreeRTOS.h" + + #include @@ -31,15 +35,15 @@ class NimBLEAdvertisementData { // be exposed on demand/request or as time permits. // public: - void setAppearance(uint16_t appearance); - void setCompleteServices(NimBLEUUID uuid); - void setFlags(uint8_t); - void setManufacturerData(std::string data); - void setName(std::string name); - void setPartialServices(NimBLEUUID uuid); - void setServiceData(NimBLEUUID uuid, std::string data); - void setShortName(std::string name); - void addData(std::string data); // Add data to the payload. +// void setAppearance(uint16_t appearance); +// void setCompleteServices(NimBLEUUID uuid); +// void setFlags(uint8_t); +// void setManufacturerData(std::string data); +// void setName(std::string name); +// void setPartialServices(NimBLEUUID uuid); +// void setServiceData(NimBLEUUID uuid, std::string data); +// void setShortName(std::string name); +// void addData(std::string data); // Add data to the payload. std::string getPayload(); // Retrieve the current advert payload. private: @@ -66,7 +70,7 @@ class NimBLEAdvertising { void setAdvertisementData(NimBLEAdvertisementData& advertisementData); // void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); // void setScanResponseData(NimBLEAdvertisementData& advertisementData); - void setPrivateAddress(uint8_t type = BLE_ADDR_TYPE_RANDOM); + void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM); //void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); // void setMinPreferred(uint16_t); diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index bed21980..16c7b500 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -54,6 +54,7 @@ NimBLEScan* NimBLEDevice::m_pScan = nullptr; NimBLEServer* NimBLEDevice::m_pServer = nullptr; uint32_t NimBLEDevice::m_passkey = 123456; bool NimBLEDevice::m_synced = false; +NimBLEAdvertising* NimBLEDevice::m_bleAdvertising = nullptr; gap_event_handler NimBLEDevice::m_customGapHandler = nullptr; ble_gap_event_listener NimBLEDevice::m_listener; diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 9f9f9b2d..15a3eb8b 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -104,6 +104,7 @@ class NimBLEDevice { static bool m_synced; static NimBLEScan* m_pScan; static NimBLEServer* m_pServer; + static NimBLEAdvertising* m_bleAdvertising; static ble_gap_event_listener m_listener; static uint32_t m_passkey; static std::list m_cList; diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 08073628..9c16e1e5 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -152,7 +152,7 @@ bool NimBLEService::start() { ble_gatt_chr_def* pChtr_a = nullptr; svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; - svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative(); + svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; uint8_t numChtrs = m_characteristicMap.getSize(); if(numChtrs < 1){ svc[0].characteristics = NULL; @@ -160,7 +160,7 @@ bool NimBLEService::start() { pChtr_a = new ble_gatt_chr_def[numChtrs+1]; NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); for(int i=0; i < numChtrs; i++) { - pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative(); + pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative()->u; // pChtr_a[i].access_cb = NULL; // pChtr_a[i].arg = NULL; pChtr_a[i].descriptors = NULL; diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 6b031ff9..106b548d 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -18,11 +18,11 @@ #if defined(CONFIG_BT_ENABLED) //#include -#include "NimBLECharacteristic.h" + #include "NimBLEServer.h" #include "NimBLEUUID.h" #include "FreeRTOS.h" - +#include "NimBLECharacteristic.h" class NimBLEServer; class NimBLECharacteristic; From 1b6504749d95409fc5f05babdd6919ef65a8238f Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 6 Mar 2020 15:29:44 -0700 Subject: [PATCH 09/62] More work on advertising. --- src/NimBLEAdvertising.cpp | 22 +++++++++++-- src/NimBLECharacteristic.cpp | 60 ++++++++++++++++++++++++------------ src/NimBLECharacteristic.h | 5 ++- src/NimBLEDevice.cpp | 2 ++ src/NimBLEDevice.h | 1 + src/NimBLEService.cpp | 60 +++++++++--------------------------- 6 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index d35592d5..8e3c22a4 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -212,7 +212,10 @@ void NimBLEAdvertising::start() { } memcpy(&uuids16[m_advData.num_uuids16].value, &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; - //m_advData.uuids16_is_complete = 1; + char buf[BLE_UUID_STR_LEN]; + ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); + NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + m_advData.uuids16_is_complete = 1; m_advData.uuids16 = uuids16; } if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32){ @@ -223,7 +226,10 @@ void NimBLEAdvertising::start() { } memcpy(&uuids32[m_advData.num_uuids32].value, &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; - //m_advData.uuids32_is_complete = 1; + char buf[BLE_UUID_STR_LEN]; + ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); + NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + m_advData.uuids32_is_complete = 1; m_advData.uuids32 = uuids32; } if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){ @@ -234,7 +240,10 @@ void NimBLEAdvertising::start() { } memcpy(&uuids128[m_advData.num_uuids128].value, &m_serviceUUIDs[i].getNative()->u128.value, 16); uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; - //m_advData.uuids128_is_complete = 1; + char buf[BLE_UUID_STR_LEN]; + ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); + NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + m_advData.uuids128_is_complete = 1; m_advData.uuids128 = uuids128; } } @@ -251,6 +260,12 @@ void NimBLEAdvertising::start() { abort(); } + rc = ble_gatts_start(); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, &m_advParams, NULL, NULL); if (rc != 0) { @@ -334,6 +349,7 @@ void NimBLEAdvertising::start() { } */ NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); + ble_gatts_show_local(); } // start diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index a6deadef..cd4675ed 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -41,15 +41,15 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties) { m_bleUUID = uuid; m_handle = NULL_HANDLE; - m_properties = (uint8_t)0; + m_properties = (uint16_t) 0; m_pCallbacks = nullptr; - setBroadcastProperty((properties & BLE_GATT_CHR_PROP_BROADCAST) != 0); - setReadProperty((properties & BLE_GATT_CHR_PROP_READ) != 0); - setWriteProperty((properties & BLE_GATT_CHR_PROP_WRITE) != 0); - setNotifyProperty((properties & BLE_GATT_CHR_PROP_NOTIFY) != 0); - setIndicateProperty((properties & BLE_GATT_CHR_PROP_INDICATE) != 0); - setWriteNoResponseProperty((properties & BLE_GATT_CHR_PROP_WRITE_NO_RSP) != 0); + setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); + setReadProperty((properties & PROPERTY_READ) != 0); + setWriteProperty((properties & PROPERTY_WRITE) != 0); + setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); + setIndicateProperty((properties & PROPERTY_INDICATE) != 0); + setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); } // NimBLECharacteristic /** @@ -195,6 +195,28 @@ uint8_t* BLECharacteristic::getData() { } // getData */ + +int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rand_num; + int rc; + NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg; + + NIMBLE_LOGE(LOG_TAG, "Characteristic gap event for %s", pCharacteristic->getUUID().toString().c_str()); + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ + assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); + rand_num = rand(); + rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + assert(0); + return BLE_ATT_ERR_UNLIKELY; +} + /** * Handle a GATT server event. */ @@ -559,9 +581,9 @@ void BLECharacteristic::notify(bool is_notification) { */ void NimBLECharacteristic::setBroadcastProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_BROADCAST); + m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_BROADCAST); + m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST); } } // setBroadcastProperty @@ -598,9 +620,9 @@ void NimBLECharacteristic::setHandle(uint16_t handle) { */ void NimBLECharacteristic::setIndicateProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_INDICATE); + m_properties = (m_properties | BLE_GATT_CHR_F_INDICATE); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_INDICATE); + m_properties = (m_properties & ~BLE_GATT_CHR_F_INDICATE); } } // setIndicateProperty @@ -611,9 +633,9 @@ void NimBLECharacteristic::setIndicateProperty(bool value) { */ void NimBLECharacteristic::setNotifyProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_NOTIFY); + m_properties = (m_properties | BLE_GATT_CHR_F_NOTIFY); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_NOTIFY); + m_properties = (m_properties & ~BLE_GATT_CHR_F_NOTIFY); } } // setNotifyProperty @@ -624,9 +646,9 @@ void NimBLECharacteristic::setNotifyProperty(bool value) { */ void NimBLECharacteristic::setReadProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_READ ); + m_properties = (m_properties | BLE_GATT_CHR_F_READ ); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_READ ); + m_properties = (m_properties & ~BLE_GATT_CHR_F_READ ); } } // setReadProperty @@ -706,9 +728,9 @@ void BLECharacteristic::setValue(double& data64) { */ void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_WRITE_NO_RSP); + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_WRITE_NO_RSP); + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP); } } // setWriteNoResponseProperty @@ -719,9 +741,9 @@ void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { */ void NimBLECharacteristic::setWriteProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_PROP_WRITE ); + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE ); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_PROP_WRITE ); + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE ); } } // setWriteProperty diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index b39b9541..c62d2f47 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -111,12 +111,15 @@ class NimBLECharacteristic { NimBLEUUID m_bleUUID; // BLEDescriptorMap m_descriptorMap; uint16_t m_handle; - uint8_t m_properties; + uint16_t m_properties; NimBLECharacteristicCallbacks* m_pCallbacks; NimBLEService* m_pService; // BLEValue m_value; uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; bool m_writeEvt = false; + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg); /* void handleGATTServerEvent( esp_gatts_cb_event_t event, diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 16c7b500..f9890b4c 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -85,8 +85,10 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; */ if(NimBLEDevice::m_pServer == nullptr) { NimBLEDevice::m_pServer = new NimBLEServer(); + ble_gatts_reset(); ble_svc_gap_init(); ble_svc_gatt_init(); + vTaskDelay(1); } return m_pServer; diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 15a3eb8b..96bd51bf 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -47,6 +47,7 @@ #define BLEServer NimBLEServer #define BLEService NimBLEService #define BLECharacteristic NimBLECharacteristic +#define BLEAdvertising NimBLEAdvertising /** diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 9c16e1e5..be830696 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -143,86 +143,56 @@ bool NimBLEService::start() { // obtained as a result of calling esp_ble_gatts_create_service(). // NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); - int rc = 0; // Nimble requires an array of services to be sent to the api // Since we are adding 1 at a time we create an array of 2 and set the type // of the second service to 0 to indicate the end of the array. - ble_gatt_svc_def svc[2]; + ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; ble_gatt_chr_def* pChtr_a = nullptr; svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; - svc[0].uuid = (const ble_uuid_t*)&m_uuid.getNative()->u; + svc[0].uuid = &m_uuid.getNative()->u; uint8_t numChtrs = m_characteristicMap.getSize(); + NIMBLE_LOGE(LOG_TAG,"adding %d characteristics", numChtrs); if(numChtrs < 1){ svc[0].characteristics = NULL; }else{ pChtr_a = new ble_gatt_chr_def[numChtrs+1]; NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); - for(int i=0; i < numChtrs; i++) { - pChtr_a[i].uuid = (const ble_uuid_t*)&pCharacteristic->getUUID().getNative()->u; - // pChtr_a[i].access_cb = NULL; - // pChtr_a[i].arg = NULL; + for(uint8_t i=0; i < numChtrs; i++) { + char buf[50]; + NIMBLE_LOGI(LOG_TAG,"adding char uuid: %s", ble_uuid_to_str(&pCharacteristic->getUUID().getNative()->u, buf)); + pChtr_a[i].uuid = &pCharacteristic->getUUID().getNative()->u; + NIMBLE_LOGI(LOG_TAG, "pchtr_a[%d] uuid: %s", i, ble_uuid_to_str(pChtr_a[i].uuid, buf)); + pChtr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; + pChtr_a[i].arg = pCharacteristic; pChtr_a[i].descriptors = NULL; pChtr_a[i].flags = pCharacteristic->m_properties; + NIMBLE_LOGI(LOG_TAG, "pchtr_a props: %04x", pChtr_a[i].flags); pChtr_a[i].min_key_size = 0; pChtr_a[i].val_handle = &pCharacteristic->m_handle; pCharacteristic = m_characteristicMap.getNext(); } pChtr_a[numChtrs].uuid = NULL; - svc[0].characteristics = (const ble_gatt_chr_def*)pChtr_a; + svc[0].characteristics = pChtr_a; } svc[1].type = 0; - rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)&svc); + rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)svc); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - if(pChtr_a != nullptr) { - delete[] pChtr_a; - } return false; } - rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)&svc); + rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)svc); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - if(pChtr_a != nullptr) { - delete[] pChtr_a; - } return false; + } - - if(pChtr_a != nullptr) { - delete[] pChtr_a; - } - - -/* if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!"); - return; - } - BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst(); - - while (pCharacteristic != nullptr) { - m_lastCreatedCharacteristic = pCharacteristic; - pCharacteristic->executeCreate(this); - - pCharacteristic = m_characteristicMap.getNext(); - } - // Start each of the characteristics ... these are found in the m_characteristicMap. - - m_semaphoreStartEvt.take("start"); - esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStartEvt.wait("start"); -*/ NIMBLE_LOGD(LOG_TAG, "<< start()"); return true; } // start From 2136596ba29241dc4c8f178f25ba0cf600f3d5a7 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 8 Mar 2020 22:02:50 -0600 Subject: [PATCH 10/62] Cleaned up and implemented advertising functions. Implemented characteristic values and notify/indicate. Implemented helper functions in server class. --- src/NimBLEAdvertising.cpp | 246 ++++++++++----------- src/NimBLEAdvertising.h | 35 +-- src/NimBLECharacteristic.cpp | 332 +++++++++++++++++++---------- src/NimBLECharacteristic.h | 85 ++++---- src/NimBLEDevice.cpp | 3 +- src/NimBLEDevice.h | 3 +- src/NimBLELog.h | 17 +- src/NimBLEServer.cpp | 401 ++++++++++++++++++++--------------- src/NimBLEServer.h | 89 ++++---- src/NimBLEService.cpp | 68 +++--- src/NimBLEService.h | 10 +- src/NimBLEServiceMap.cpp | 148 +++++++++++++ src/NimBLEValue.cpp | 140 ++++++++++++ src/NimBLEValue.h | 46 ++++ 14 files changed, 1076 insertions(+), 547 deletions(-) create mode 100644 src/NimBLEServiceMap.cpp create mode 100644 src/NimBLEValue.cpp create mode 100644 src/NimBLEValue.h diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 8e3c22a4..37fc7ff5 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -17,6 +17,8 @@ #if defined(CONFIG_BT_ENABLED) #include "services/gap/ble_svc_gap.h" #include "NimBLEAdvertising.h" +#include "NimBLEDevice.h" +#include "NimBLEServer.h" //#include #include "NimBLEUtils.h" #include "NimBLELog.h" @@ -118,6 +120,7 @@ void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { m_advData.max_interval = maxinterval; } // */ + void NimBLEAdvertising::setScanResponse(bool set) { m_scanResp = set; } @@ -195,56 +198,68 @@ void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData */ void NimBLEAdvertising::start() { NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); - ble_uuid16_t* uuids16 = nullptr; - ble_uuid32_t* uuids32 = nullptr; - ble_uuid128_t* uuids128 = nullptr; + int numServices = m_serviceUUIDs.size(); int rc = 0; uint8_t addressType; - - if (numServices > 0) { + + if (!m_advSvcsSet && numServices > 0) { for (int i = 0; i < numServices; i++) { - if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16){ - m_advData.num_uuids16++; - if(nullptr == (uuids16 = (ble_uuid16_t*)realloc(uuids16, m_advData.num_uuids16 * sizeof(ble_uuid16_t)))) { + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { + if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16, + (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) + { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); abort(); } - memcpy(&uuids16[m_advData.num_uuids16].value, &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); - uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; + memcpy(&m_advData.uuids16[m_advData.num_uuids16].value, + &m_serviceUUIDs[i].getNative()->u16.value, sizeof(uint16_t)); + + m_advData.uuids16[m_advData.num_uuids16].u.type = BLE_UUID_TYPE_16; + /* char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); - NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + ble_uuid_to_str(&m_advData.uuids16[m_advData.num_uuids16].u, buf); + NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); + */ m_advData.uuids16_is_complete = 1; - m_advData.uuids16 = uuids16; + m_advData.num_uuids16++; } - if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32){ - m_advData.num_uuids32++; - if(nullptr == (uuids32 = (ble_uuid32_t*)realloc(uuids32, m_advData.num_uuids32 * sizeof(ble_uuid32_t)))) { + if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32) { + if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32, + (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) + { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); abort(); } - memcpy(&uuids32[m_advData.num_uuids32].value, &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); - uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; + memcpy(&m_advData.uuids32[m_advData.num_uuids32].value, + &m_serviceUUIDs[i].getNative()->u32.value, sizeof(uint32_t)); + + m_advData.uuids32[m_advData.num_uuids32].u.type = BLE_UUID_TYPE_32; + /* char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); - NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + ble_uuid_to_str(&m_advData.uuids32[m_advData.num_uuids32].u, buf); + NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); + */ m_advData.uuids32_is_complete = 1; - m_advData.uuids32 = uuids32; + m_advData.num_uuids32++; } if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){ - m_advData.num_uuids128++; - if(nullptr == (uuids128 = (ble_uuid128_t*)realloc(uuids128, m_advData.num_uuids128 * sizeof(ble_uuid128_t)))) { + if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128, + (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); abort(); } - memcpy(&uuids128[m_advData.num_uuids128].value, &m_serviceUUIDs[i].getNative()->u128.value, 16); - uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; + memcpy(&m_advData.uuids128[m_advData.num_uuids128].value, + &m_serviceUUIDs[i].getNative()->u128.value, 16); + + m_advData.uuids128[m_advData.num_uuids128].u.type = BLE_UUID_TYPE_128; + /* char buf[BLE_UUID_STR_LEN]; - ble_uuid_to_str(&uuids128[m_advData.num_uuids128].u, buf); - NIMBLE_LOGE(LOG_TAG, "Advertising UUID: %s", buf); + ble_uuid_to_str(&m_advData.uuids128[m_advData.num_uuids128].u, buf); + NIMBLE_LOGI(LOG_TAG, "Advertising UUID: %s", buf); + */ m_advData.uuids128_is_complete = 1; - m_advData.uuids128 = uuids128; + m_advData.num_uuids128++; } } @@ -254,25 +269,40 @@ void NimBLEAdvertising::start() { abort(); } - rc = ble_hs_id_infer_auto(0, &addressType); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - abort(); + if(m_advData.num_uuids128 > 0) { + free(m_advData.uuids128); + m_advData.uuids128 = nullptr; + m_advData.num_uuids128 = 0; } - rc = ble_gatts_start(); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - abort(); - } - - rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, - &m_advParams, NULL, NULL); - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "error enabling advertisement; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - abort(); + if(m_advData.num_uuids32 > 0) { + free(m_advData.uuids32); + m_advData.uuids32 = nullptr; + m_advData.num_uuids32 = 0; } + + if(m_advData.num_uuids16 > 0) { + free(m_advData.uuids16); + m_advData.uuids16 = nullptr; + m_advData.num_uuids16 = 0; + } + + m_advSvcsSet = true; } + + rc = ble_hs_id_infer_auto(0, &addressType); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, + &m_advParams, NimBLEServer::handleGapEvent, NimBLEDevice::createServer()); //get a reference to the server (does not create a new one) + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error enabling advertisement; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + /* m_advData.service_uuid_len = 16 * numServices; m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; @@ -289,27 +319,7 @@ void NimBLEAdvertising::start() { } */ /* - // We have a vector of service UUIDs that we wish to advertise. In order to use the - // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte) - // representations. If we have 1 or more services to advertise then we allocate enough - // storage to host them and then copy them in one at a time into the contiguous storage. - int numServices = m_serviceUUIDs.size(); - if (numServices > 0) { - m_advData.service_uuid_len = 16 * numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; - uint8_t* p = m_advData.p_service_uuid; - for (int i = 0; i < numServices; i++) { - ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); - BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); - memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); - p += 16; - } - } else { - m_advData.service_uuid_len = 0; - ESP_LOGD(LOG_TAG, "- no services advertised"); - } - esp_err_t errRc; if (!m_customAdvData) { // Set the configuration for advertising. @@ -349,7 +359,6 @@ void NimBLEAdvertising::start() { } */ NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); - ble_gatts_show_local(); } // start @@ -360,28 +369,27 @@ void NimBLEAdvertising::start() { */ void NimBLEAdvertising::stop() { NIMBLE_LOGD(LOG_TAG, ">> stop"); - /* - esp_err_t errRc = ::esp_ble_gap_stop_advertising(); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + int rc = ble_gap_adv_stop(); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); return; } - */ + NIMBLE_LOGD(LOG_TAG, "<< stop"); } // stop + /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. */ - /* void NimBLEAdvertisementData::addData(std::string data) { - if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) { + if ((m_payload.length() + data.length()) > BLE_HS_ADV_MAX_SZ) { return; } m_payload.append(data); } // addData -*/ + /** * @brief Set the appearance. @@ -390,44 +398,42 @@ void NimBLEAdvertisementData::addData(std::string data) { * See also: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml */ - /* void NimBLEAdvertisementData::setAppearance(uint16_t appearance) { char cdata[2]; cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19 + cdata[1] = BLE_HS_ADV_TYPE_APPEARANCE; // 0x19 addData(std::string(cdata, 2) + std::string((char*) &appearance, 2)); } // setAppearance -*/ + /** * @brief Set the complete services. * @param [in] uuid The single service to advertise. */ - /* -void NimBLEAdvertisementData::setCompleteServices(BLEUUID uuid) { +void NimBLEAdvertisementData::setCompleteServices(NimBLEUUID uuid) { char cdata[2]; switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2)); + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS16; // 0x03 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2)); break; } case 32: { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; - cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4)); + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS32; // 0x05 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4)); break; } case 128: { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; - cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07 - addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->uuid.uuid128, 16)); + cdata[1] = BLE_HS_ADV_TYPE_COMP_UUIDS128; // 0x07 + addData(std::string(cdata, 2) + std::string((char*) uuid.getNative()->u128.value, 16)); break; } @@ -435,89 +441,89 @@ void NimBLEAdvertisementData::setCompleteServices(BLEUUID uuid) { return; } } // setCompleteServices -*/ + /** * @brief Set the advertisement flags. * @param [in] The flags to be set in the advertisement. - * + * * ****DO NOT USE THESE**** * * ESP_BLE_ADV_FLAG_LIMIT_DISC * * ESP_BLE_ADV_FLAG_GEN_DISC * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC + * * + * * ****THESE ARE SUPPORTED**** + * * BLE_HS_ADV_F_DISC_LTD + * * BLE_HS_ADV_F_DISC_GEN + * * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE */ - /* void NimBLEAdvertisementData::setFlags(uint8_t flag) { char cdata[3]; cdata[0] = 2; - cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01 - cdata[2] = flag; + cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 + cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; addData(std::string(cdata, 3)); } // setFlag -*/ /** * @brief Set manufacturer specific data. * @param [in] data Manufacturer data. */ - /* void NimBLEAdvertisementData::setManufacturerData(std::string data) { - ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData"); + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData"); char cdata[2]; cdata[0] = data.length() + 1; - cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff + cdata[1] = BLE_HS_ADV_TYPE_MFG_DATA ; // 0xff addData(std::string(cdata, 2) + data); - ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData"); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setManufacturerData"); } // setManufacturerData -*/ + /** * @brief Set the name. * @param [in] The complete name of the device. */ - /* void NimBLEAdvertisementData::setName(std::string name) { - ESP_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str()); + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; - cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09 + cdata[1] = BLE_HS_ADV_TYPE_COMP_NAME; // 0x09 addData(std::string(cdata, 2) + name); - ESP_LOGD("NimBLEAdvertisementData", "<< setName"); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setName"); } // setName -*/ + /** * @brief Set the partial services. * @param [in] uuid The single service to advertise. */ - /* void NimBLEAdvertisementData::setPartialServices(NimBLEUUID uuid) { char cdata[2]; switch (uuid.bitSize()) { case 16: { // [Len] [0x02] [LL] [HH] cdata[0] = 3; - cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid16, 2)); + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS16; // 0x02 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u16.value, 2)); break; } case 32: { // [Len] [0x04] [LL] [LL] [HH] [HH] cdata[0] = 5; - cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid32, 4)); + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS32; // 0x04 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u32.value, 4)); break; } case 128: { // [Len] [0x04] [0] [1] ... [15] cdata[0] = 17; - cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06 - addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->uuid.uuid128, 16)); + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_UUIDS128; // 0x06 + addData(std::string(cdata, 2) + std::string((char *) &uuid.getNative()->u128.value, 16)); break; } @@ -525,38 +531,37 @@ void NimBLEAdvertisementData::setPartialServices(NimBLEUUID uuid) { return; } } // setPartialServices -*/ + /** * @brief Set the service data (UUID + data) * @param [in] uuid The UUID to set with the service data. Size of UUID will be used. * @param [in] data The data to be associated with the service data advert. */ - /* -void NimBLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { +void NimBLEAdvertisementData::setServiceData(NimBLEUUID uuid, std::string data) { char cdata[2]; switch (uuid.bitSize()) { case 16: { // [Len] [0x16] [UUID16] data cdata[0] = data.length() + 3; - cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid16, 2) + data); + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16; // 0x16 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u16.value, 2) + data); break; } case 32: { // [Len] [0x20] [UUID32] data cdata[0] = data.length() + 5; - cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid32, 4) + data); + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID32; // 0x20 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u32.value, 4) + data); break; } case 128: { // [Len] [0x21] [UUID128] data cdata[0] = data.length() + 17; - cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21 - addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->uuid.uuid128, 16) + data); + cdata[1] = BLE_HS_ADV_TYPE_SVC_DATA_UUID128; // 0x21 + addData(std::string(cdata, 2) + std::string((char*) &uuid.getNative()->u128.value, 16) + data); break; } @@ -564,22 +569,21 @@ void NimBLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) { return; } } // setServiceData -*/ + /** * @brief Set the short name. * @param [in] The short name of the device. */ - /* void NimBLEAdvertisementData::setShortName(std::string name) { - ESP_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str()); + NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str()); char cdata[2]; cdata[0] = name.length() + 1; - cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08 + cdata[1] = BLE_HS_ADV_TYPE_INCOMP_NAME; // 0x08 addData(std::string(cdata, 2) + name); - ESP_LOGD("NimBLEAdvertisementData", "<< setShortName"); + NIMBLE_LOGD("NimBLEAdvertisementData", "<< setShortName"); } // setShortName -*/ + /** * @brief Retrieve the payload that is to be advertised. diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 3f32b7a3..355750f3 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -16,16 +16,22 @@ #define MAIN_BLEADVERTISING_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include "host/ble_hs.h" + #include "host/ble_gap.h" -//#include + #include "NimBLEUUID.h" #include "FreeRTOS.h" - - #include +/* COMPATIBILITY - DO NOT USE */ +#define ESP_BLE_ADV_FLAG_LIMIT_DISC (0x01 << 0) +#define ESP_BLE_ADV_FLAG_GEN_DISC (0x01 << 1) +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT (0x01 << 2) +#define ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT (0x01 << 3) +#define ESP_BLE_ADV_FLAG_DMT_HOST_SPT (0x01 << 4) +#define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) + /* ************************* */ /** * @brief Advertisement data set by the programmer to be published by the %BLE server. @@ -35,15 +41,15 @@ class NimBLEAdvertisementData { // be exposed on demand/request or as time permits. // public: -// void setAppearance(uint16_t appearance); -// void setCompleteServices(NimBLEUUID uuid); -// void setFlags(uint8_t); -// void setManufacturerData(std::string data); -// void setName(std::string name); -// void setPartialServices(NimBLEUUID uuid); -// void setServiceData(NimBLEUUID uuid, std::string data); -// void setShortName(std::string name); -// void addData(std::string data); // Add data to the payload. + void setAppearance(uint16_t appearance); + void setCompleteServices(NimBLEUUID uuid); + void setFlags(uint8_t); + void setManufacturerData(std::string data); + void setName(std::string name); + void setPartialServices(NimBLEUUID uuid); + void setServiceData(NimBLEUUID uuid, std::string data); + void setShortName(std::string name); + void addData(std::string data); // Add data to the payload. std::string getPayload(); // Retrieve the current advert payload. private: @@ -84,7 +90,8 @@ class NimBLEAdvertising { bool m_customAdvData = false; // Are we using custom advertising data? bool m_customScanResponseData = false; // Are we using custom scan response data? // FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); - bool m_scanResp = true; + bool m_scanResp = true; + bool m_advSvcsSet = false; }; #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index cd4675ed..e0f17a91 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -17,12 +17,13 @@ #include "NimBLEUtils.h" #include "NimBLELog.h" //#include "BLE2902.h" -//#include "GeneralUtils.h" #include #define NULL_HANDLE (0xffff) +static NimBLECharacteristicCallbacks defaultCallback; + static const char* LOG_TAG = "NimBLECharacteristic"; /** @@ -30,7 +31,8 @@ static const char* LOG_TAG = "NimBLECharacteristic"; * @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] properties - Properties for the characteristic. */ -NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties) : NimBLECharacteristic(NimBLEUUID(uuid), properties) { +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties, NimBLEService* pService) +: NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { } /** @@ -38,11 +40,12 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties * @param [in] uuid - UUID for the characteristic. * @param [in] properties - Properties for the characteristic. */ -NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties) { - m_bleUUID = uuid; +NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties, NimBLEService* pService) { + m_uuid = uuid; m_handle = NULL_HANDLE; m_properties = (uint16_t) 0; - m_pCallbacks = nullptr; + m_pCallbacks = &defaultCallback; + m_pService = pService; setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); setReadProperty((properties & PROPERTY_READ) != 0); @@ -77,16 +80,17 @@ void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) { * @brief Register a new characteristic with the ESP runtime. * @param [in] pService The service with which to associate this characteristic. */ + /* void NimBLECharacteristic::executeCreate(NimBLEService* pService) { NIMBLE_LOGD(LOG_TAG, ">> executeCreate()"); -/* + if (m_handle != NULL_HANDLE) { ESP_LOGE(LOG_TAG, "Characteristic already has a handle."); return; } -*/ + m_pService = pService; // Save the service to which this characteristic belongs. -/* + ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", getUUID().toString().c_str(), m_pService->toString().c_str()); @@ -114,10 +118,10 @@ void NimBLECharacteristic::executeCreate(NimBLEService* pService) { pDescriptor->executeCreate(this); pDescriptor = m_descriptorMap.getNext(); } // End while -*/ + NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate - +*/ /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. @@ -171,7 +175,7 @@ NimBLEService* NimBLECharacteristic::getService() { * @return The UUID of the characteristic. */ NimBLEUUID NimBLECharacteristic::getUUID() { - return m_bleUUID; + return m_uuid; } // getUUID @@ -179,21 +183,18 @@ NimBLEUUID NimBLECharacteristic::getUUID() { * @brief Retrieve the current value of the characteristic. * @return A pointer to storage containing the current characteristic value. */ - /* -std::string BLECharacteristic::getValue() { +std::string NimBLECharacteristic::getValue() { return m_value.getValue(); } // getValue -*/ + /** * @brief Retrieve the current raw data of the characteristic. * @return A pointer to storage containing the current characteristic data. */ - /* -uint8_t* BLECharacteristic::getData() { +uint8_t* NimBLECharacteristic::getData() { return m_value.getData(); } // getData -*/ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, @@ -201,19 +202,39 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han void *arg) { const ble_uuid_t *uuid; - int rand_num; int rc; NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg; - NIMBLE_LOGE(LOG_TAG, "Characteristic gap event for %s", pCharacteristic->getUUID().toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); + uuid = ctxt->chr->uuid; if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ - assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR); - rand_num = rand(); - rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num); - return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: { + //if (pCharacteristic->m_pCallbacks != nullptr) { + pCharacteristic->m_pCallbacks->onRead(pCharacteristic); + //} + rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength()); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_CHR: { + if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); + //if (pCharacteristic->m_pCallbacks != nullptr) { + pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); + //} + return 0; + } + default: + break; + } } - assert(0); + return BLE_ATT_ERR_UNLIKELY; } @@ -492,85 +513,150 @@ void BLECharacteristic::handleGATTServerEvent( } // handleGATTServerEvent */ + +/** + * @brief Set the subscribe status for this characteristic. + * This will maintain a map of subscribed clients and their indicate/notify status. + * @return N/A + */ +void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { + uint16_t subVal; + if(event->subscribe.cur_notify) { + subVal = 1; + }else if(event->subscribe.cur_indicate) { + subVal = 2; + }else { + subVal = 0; + } + + m_semaphoreConfEvt.give(subVal == 2 ? 0 : NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); + + NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal); + + auto it = m_subscribedMap.find(event->subscribe.conn_handle); + if(it == m_subscribedMap.cend()) { + m_subscribedMap.insert(std::pair(event->subscribe.conn_handle, subVal)); + return; + } + + if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { + m_subscribedMap.erase(event->subscribe.conn_handle); + return; + } + + (*it).second = subVal; +} + + /** * @brief Send an indication. * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication * will block waiting a positive confirmation from the client. * @return N/A */ - /* -void BLECharacteristic::indicate() { - - ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); +void NimBLECharacteristic::indicate() { + NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length()); notify(false); - ESP_LOGD(LOG_TAG, "<< indicate"); + NIMBLE_LOGD(LOG_TAG, "<< indicate"); } // indicate -*/ - + /** * @brief Send a notify. * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification * will not block; it is a fire and forget. * @return N/A. */ - /* -void BLECharacteristic::notify(bool is_notification) { - ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); +void NimBLECharacteristic::notify(bool is_notification) { + NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length()); assert(getService() != nullptr); assert(getService()->getServer() != nullptr); - GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length()); - - if (getService()->getServer()->getConnectedCount() == 0) { - ESP_LOGD(LOG_TAG, "<< notify: No connected clients."); - return; - } - - // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled - // and, if not, prevent the notification. - - BLE2902* p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902); - if(p2902 == nullptr){ - ESP_LOGE(LOG_TAG, "Characteristic without 0x2902 descriptor"); +/* + if (getService()->getServer()->getConnectedCount() == 0) { + NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients."); return; } - if(is_notification) { - if (p2902 != nullptr && !p2902->getNotifications()) { - ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring"); - return; - } - } - else{ - if (p2902 != nullptr && !p2902->getIndications()) { - ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring"); - return; - } - } - for (auto &myPair : getService()->getServer()->getPeerDevices(false)) { - uint16_t _mtu = (myPair.second.mtu); - if (m_value.getValue().length() > _mtu - 3) { - ESP_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); - } - - size_t length = m_value.getValue().length(); - if(!is_notification) - m_semaphoreConfEvt.take("indicate"); - esp_err_t errRc = ::esp_ble_gatts_send_indicate( - getService()->getServer()->getGattsIf(), - myPair.first, - getHandle(), length, (uint8_t*)m_value.getValue().data(), !is_notification); // The need_confirm = false makes this a notify. - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_ %s: rc=%d %s",is_notification?"notify":"indicate", errRc, GeneralUtils::errorToString(errRc)); - m_semaphoreConfEvt.give(); - return; +*/ + m_pCallbacks->onNotify(this); + + int rc = 0; + os_mbuf *om; + size_t length = m_value.getValue().length(); + uint8_t* data = (uint8_t*)m_value.getValue().data(); + + for (auto it = m_subscribedMap.cbegin(); it != m_subscribedMap.cend(); ++it) { + uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); + + if(_mtu == 0) { + NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); + m_subscribedMap.erase((*it).first); + it = m_subscribedMap.cbegin(); + if(it == m_subscribedMap.cend()) { + return; + } + continue; + } + + NIMBLE_LOGD(LOG_TAG, "Client MTU = %d", _mtu); + + if (length > _mtu - 3) { + NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); } - if(!is_notification) - m_semaphoreConfEvt.wait("indicate"); + + if((*it).second == 0) { + NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client"); + continue; + } + + if((*it).second == 2 && is_notification) { + NIMBLE_LOGW(LOG_TAG, + "Sending notification to client subscribed to indications, sending indication instead"); + is_notification = false; + } + + if((*it).second == 1 && !is_notification) { + NIMBLE_LOGW(LOG_TAG, + "Sending indication to client subscribed to notifications, sending notifications instead"); + is_notification = true; + } + + // don't create the m_buf until we are sure to send the data or else + // we could be allocating a buffer that doesn't get released. + // We also must create it in each loop iteration because it is consumed with each host call. + om = ble_hs_mbuf_from_flat(data, length); + + if(!is_notification) { + m_semaphoreConfEvt.take("indicate"); + rc = ble_gattc_indicate_custom((*it).first, m_handle, om); + if(rc != 0){ + m_semaphoreConfEvt.give(); + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); + return; + } + + rc = m_semaphoreConfEvt.wait(); + + if(rc == BLE_HS_ETIMEOUT) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); + } else if(rc == BLE_HS_EDONE) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc); + } else { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE, rc); + } + } else { + rc = ble_gattc_notify_custom((*it).first, m_handle, om); + if(rc == 0) { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY, 0); + } else { + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_GATT, rc); + } + } } - ESP_LOGD(LOG_TAG, "<< notify"); + + NIMBLE_LOGD(LOG_TAG, "<< notify"); } // Notify -*/ + /** * @brief Set the permission to broadcast. @@ -593,7 +679,11 @@ void NimBLECharacteristic::setBroadcastProperty(bool value) { * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. */ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) { - m_pCallbacks = pCallbacks; + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallback; + } } // setCallbacks @@ -658,19 +748,25 @@ void NimBLECharacteristic::setReadProperty(bool value) { * @param [in] data The data to set for the characteristic. * @param [in] length The length of the data in bytes. */ - /* -void BLECharacteristic::setValue(uint8_t* data, size_t length) { - char* pHex = BLEUtils::buildHexData(nullptr, data, length); - ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); +void NimBLECharacteristic::setValue(uint8_t* data, size_t length) { + char* pHex = NimBLEUtils::buildHexData(nullptr, data, length); + NIMBLE_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str()); free(pHex); - if (length > ESP_GATT_MAX_ATTR_LEN) { - ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN); + + if (length > BLE_ATT_ATTR_MAX_LEN) { + NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN); return; } + m_value.setValue(data, length); - ESP_LOGD(LOG_TAG, "<< setValue"); + + // if(m_handle != NULL_HANDLE) { + //ble_gatts_chr_updated(m_handle); + // ble_gattc_notify(getService()->getServer()->m_connId, m_handle); + // } + + NIMBLE_LOGD(LOG_TAG, "<< setValue"); } // setValue -*/ /** * @brief Set the value of the characteristic from string data. @@ -679,19 +775,19 @@ void BLECharacteristic::setValue(uint8_t* data, size_t length) { * @param [in] Set the value of the characteristic. * @return N/A. */ - /* -void BLECharacteristic::setValue(std::string value) { + +void NimBLECharacteristic::setValue(std::string value) { setValue((uint8_t*)(value.data()), value.length()); } // setValue -void BLECharacteristic::setValue(uint16_t& data16) { +void NimBLECharacteristic::setValue(uint16_t& data16) { uint8_t temp[2]; temp[0] = data16; temp[1] = data16 >> 8; setValue(temp, 2); } // setValue -void BLECharacteristic::setValue(uint32_t& data32) { +void NimBLECharacteristic::setValue(uint32_t& data32) { uint8_t temp[4]; temp[0] = data32; temp[1] = data32 >> 8; @@ -700,7 +796,7 @@ void BLECharacteristic::setValue(uint32_t& data32) { setValue(temp, 4); } // setValue -void BLECharacteristic::setValue(int& data32) { +void NimBLECharacteristic::setValue(int& data32) { uint8_t temp[4]; temp[0] = data32; temp[1] = data32 >> 8; @@ -709,18 +805,16 @@ void BLECharacteristic::setValue(int& data32) { setValue(temp, 4); } // setValue -void BLECharacteristic::setValue(float& data32) { - uint8_t temp[4]; - *((float*)temp) = data32; - setValue(temp, 4); +void NimBLECharacteristic::setValue(float& data32) { + float temp = data32; + setValue((uint8_t*)&temp, 4); } // setValue -void BLECharacteristic::setValue(double& data64) { - uint8_t temp[8]; - *((double*)temp) = data64; - setValue(temp, 8); +void NimBLECharacteristic::setValue(double& data64) { + double temp = data64; + setValue((uint8_t*)&temp, 8); } // setValue -*/ + /** * @brief Set the Write No Response property value. @@ -753,7 +847,7 @@ void NimBLECharacteristic::setWriteProperty(bool value) { * @return A string representation of the characteristic. */ std::string NimBLECharacteristic::toString() { - std::string res = "UUID: " + m_bleUUID.toString() + ", handle : 0x"; + std::string res = "UUID: " + m_uuid.toString() + ", handle : 0x"; char hex[5]; snprintf(hex, sizeof(hex), "%04x", m_handle); res += hex; @@ -776,8 +870,8 @@ NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {} * @param [in] pCharacteristic The characteristic that is the source of the event. */ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("BLECharacteristicCallbacks", ">> onRead: default"); - NIMBLE_LOGD("BLECharacteristicCallbacks", "<< onRead"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onRead: default"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onRead"); } // onRead @@ -786,8 +880,30 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic * @param [in] pCharacteristic The characteristic that is the source of the event. */ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("BLECharacteristicCallbacks", ">> onWrite: default"); - NIMBLE_LOGD("BLECharacteristicCallbacks", "<< onWrite"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onWrite: default"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onWrite"); } // onWrite + +/** + * @brief Callback function to support a Notify request. + * @param [in] pCharacteristic The characteristic that is the source of the event. + */ +void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onNotify: default"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onNotify"); +} // onNotify + + +/** + * @brief Callback function to support a Notify/Indicate Status report. + * @param [in] pCharacteristic The characteristic that is the source of the event. + * @param [in] s Status of the notification/indication + * @param [in] code Additional code of underlying errors + */ +void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { + NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onStatus: default"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onStatus"); +} // onStatus + #endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index c62d2f47..02ff8563 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -17,10 +17,8 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLEService.h" #include "NimBLEUUID.h" -//#include -//#include //#include "BLEDescriptor.h" -//#include "BLEValue.h" +#include "NimBLEValue.h" #include "FreeRTOS.h" #include "host/ble_hs.h" @@ -63,31 +61,27 @@ class BLEDescriptorMap { */ class NimBLECharacteristic { public: - NimBLECharacteristic(const char* uuid, uint32_t properties = 0); - NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties = 0); - virtual ~NimBLECharacteristic(); - // void addDescriptor(BLEDescriptor* pDescriptor); // BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); // BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); NimBLEUUID getUUID(); -// std::string getValue(); -// uint8_t* getData(); + std::string getValue(); + uint8_t* getData(); -// void indicate(); -// void notify(bool is_notification = true); + void indicate(); + void notify(bool is_notification = true); void setBroadcastProperty(bool value); void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); void setIndicateProperty(bool value); void setNotifyProperty(bool value); void setReadProperty(bool value); -// void setValue(uint8_t* data, size_t size); -// void setValue(std::string value); -// void setValue(uint16_t& data16); -// void setValue(uint32_t& data32); -// void setValue(int& data32); -// void setValue(float& data32); -// void setValue(double& data64); + void setValue(uint8_t* data, size_t size); + void setValue(std::string value); + void setValue(uint16_t& data16); + void setValue(uint32_t& data32); + void setValue(int& data32); + void setValue(float& data32); + void setValue(double& data64); void setWriteProperty(bool value); void setWriteNoResponseProperty(bool value); std::string toString(); @@ -108,30 +102,30 @@ class NimBLECharacteristic { // friend class BLEDescriptor; // friend class BLECharacteristicMap; - NimBLEUUID m_bleUUID; -// BLEDescriptorMap m_descriptorMap; - uint16_t m_handle; - uint16_t m_properties; + NimBLECharacteristic(const char* uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); + NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); + virtual ~NimBLECharacteristic(); + + NimBLEUUID m_uuid; +// BLEDescriptorMap m_descriptorMap; + uint16_t m_handle; + uint16_t m_properties; NimBLECharacteristicCallbacks* m_pCallbacks; NimBLEService* m_pService; -// BLEValue m_value; - uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; - bool m_writeEvt = false; - static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, - struct ble_gatt_access_ctxt *ctxt, - void *arg); -/* - void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); -*/ - void executeCreate(NimBLEService* pService); - uint8_t getProperties(); - NimBLEService* getService(); - void setHandle(uint16_t handle); + NimBLEValue m_value; + uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; + bool m_writeEvt = false; + std::map m_subscribedMap; + + NimBLEService* getService(); + uint8_t getProperties(); + void setSubscribe(struct ble_gap_event *event); + void setHandle(uint16_t handle); + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + // FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); -// FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); + FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); }; // NimBLECharacteristic @@ -144,9 +138,22 @@ class NimBLECharacteristic { */ class NimBLECharacteristicCallbacks { public: + typedef enum { + SUCCESS_INDICATE, + SUCCESS_NOTIFY, + ERROR_INDICATE_DISABLED, + ERROR_NOTIFY_DISABLED, + ERROR_GATT, + ERROR_NO_CLIENT, + ERROR_INDICATE_TIMEOUT, + ERROR_INDICATE_FAILURE + }Status; + virtual ~NimBLECharacteristicCallbacks(); virtual void onRead(NimBLECharacteristic* pCharacteristic); virtual void onWrite(NimBLECharacteristic* pCharacteristic); + virtual void onNotify(NimBLECharacteristic* pCharacteristic); + virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code); }; #endif /* CONFIG_BT_ENABLED */ #endif /*MAIN_NIMBLECHARACTERISTIC_H_*/ \ No newline at end of file diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index f9890b4c..5cf97cd7 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -88,7 +88,6 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; ble_gatts_reset(); ble_svc_gap_init(); ble_svc_gatt_init(); - vTaskDelay(1); } return m_pServer; @@ -102,10 +101,12 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() { return m_bleAdvertising; } + void NimBLEDevice::startAdvertising() { getAdvertising()->start(); } // startAdvertising + void NimBLEDevice::stopAdvertising() { getAdvertising()->stop(); } // stopAdvertising diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 96bd51bf..fd91640e 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -48,7 +48,8 @@ #define BLEService NimBLEService #define BLECharacteristic NimBLECharacteristic #define BLEAdvertising NimBLEAdvertising - +#define BLEServerCallbacks NimBLEServerCallbacks +#define BLECharacteristicCallbacks NimBLECharacteristicCallbacks /** * @brief BLE functions. diff --git a/src/NimBLELog.h b/src/NimBLELog.h index 5c61f1af..2470c261 100644 --- a/src/NimBLELog.h +++ b/src/NimBLELog.h @@ -5,17 +5,18 @@ * Author H2zero * */ - /* -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#else -#include "esp_log.h" -#endif -*/ +#ifndef MAIN_NIMBLELOG_H_ +#define MAIN_NIMBLELOG_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + #include "modlog/modlog.h" #define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "\033[0;33mW %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "\033[0;32mI %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__) -#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__) \ No newline at end of file +#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__) + +#endif /*CONFIG_BT_ENABLED*/ +#endif /*MAIN_NIMBLELOG_H_*/ \ No newline at end of file diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 413381bf..24db1f8c 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -32,8 +32,9 @@ static const char* LOG_TAG = "NimBLEServer"; NimBLEServer::NimBLEServer() { // m_appId = ESP_GATT_IF_NONE; // m_gatts_if = ESP_GATT_IF_NONE; - m_connectedCount = 0; +// m_connectedCount = 0; m_connId = BLE_HS_CONN_HANDLE_NONE; + m_svcChgChrHdl = 0xffff; m_pServerCallbacks = nullptr; } // BLEServer @@ -74,15 +75,15 @@ NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, //m_semaphoreCreateEvt.take("createService"); // Check that a service with the supplied UUID does not already exist. -/* if (m_serviceMap.getByUUID(uuid) != nullptr) { - ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", + if (m_serviceMap.getByUUID(uuid) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.", uuid.toString().c_str()); } -*/ - NimBLEService* pService = new NimBLEService(uuid, numHandles); -// pService->m_instId = inst_id; -// m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. - pService->executeCreate(this); // Perform the API calls to actually create the service. + + NimBLEService* pService = new NimBLEService(uuid, numHandles, this); + pService->m_instId = inst_id; + m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. +// pService->executeCreate(this); // Perform the API calls to actually create the service. // m_semaphoreCreateEvt.wait("createService"); @@ -96,50 +97,95 @@ NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, * @param [in] uuid The UUID of the new service. * @return A reference to the service object. */ - /* -BLEService* BLEServer::getServiceByUUID(const char* uuid) { +NimBLEService* NimBLEServer::getServiceByUUID(const char* uuid) { return m_serviceMap.getByUUID(uuid); } -*/ + + /** * @brief Get a %BLE Service by its UUID * @param [in] uuid The UUID of the new service. * @return A reference to the service object. */ - /* -BLEService* BLEServer::getServiceByUUID(BLEUUID uuid) { +NimBLEService* NimBLEServer::getServiceByUUID(NimBLEUUID uuid) { return m_serviceMap.getByUUID(uuid); } -*/ + + /** * @brief Retrieve the advertising object that can be used to advertise the existence of the server. * * @return An advertising object. */ - /* -BLEAdvertising* BLEServer::getAdvertising() { +NimBLEAdvertising* NimBLEServer::getAdvertising() { return BLEDevice::getAdvertising(); } -*/ + +/** + * @brief Retrieve the connection id of the last connected client. + * @todo Not very useful, should refactor or remove. + * @return Client connection id. + */ uint16_t NimBLEServer::getConnId() { return m_connId; } +/** + * @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() { + int rc = ble_gatts_start(); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + ble_gatts_show_local(); + + ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; + ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; + + //int ble_gatts_find_chr(const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid, uint16_t * out_def_handle, uint16_t * out_val_handle) + 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); +} + + +/** + * @brief Disconnect the specified client with optional reason. + * @param [in] 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)); + } + + return rc; + NIMBLE_LOGD(LOG_TAG, "<< disconnect()"); +} + + /** * @brief Return the number of connected clients. * @return The number of connected clients. */ uint32_t NimBLEServer::getConnectedCount() { - return m_connectedCount; + return m_connectedServersMap.size(); } // getConnectedCount -/* -uint16_t BLEServer::getGattsIf() { - return m_gatts_if; -} -*/ /** * @brief Handle a GATT Server Event. @@ -153,152 +199,120 @@ uint16_t BLEServer::getGattsIf() { NimBLEServer* server = (NimBLEServer*)arg; NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); -/* - switch(event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - // - case ESP_GATTS_ADD_CHAR_EVT: { - break; - } // ESP_GATTS_ADD_CHAR_EVT - - case ESP_GATTS_MTU_EVT: - updatePeerMTU(param->mtu.conn_id, param->mtu.mtu); - break; - // ESP_GATTS_CONNECT_EVT - // connect: - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda - // - case ESP_GATTS_CONNECT_EVT: { - m_connId = param->connect.conn_id; - addPeerDevice((void*)this, false, m_connId); - if (m_pServerCallbacks != nullptr) { - m_pServerCallbacks->onConnect(this); - m_pServerCallbacks->onConnect(this, param); + switch(event->type) { + + case BLE_GAP_EVENT_CONNECT: { + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + NimBLEDevice::startAdvertising(); + server->m_connId = BLE_HS_CONN_HANDLE_NONE; + } + else { + server->m_connId = event->connect.conn_handle; + server->addPeerDevice((void*)server, false, server->m_connId); + if (server->m_pServerCallbacks != nullptr) { + server->m_pServerCallbacks->onConnect(server); + //m_pServerCallbacks->onConnect(this, param); + } + } + + break; + } // BLE_GAP_EVENT_CONNECT + + + case BLE_GAP_EVENT_DISCONNECT: { + server->m_connId = BLE_HS_CONN_HANDLE_NONE; + if (server->m_pServerCallbacks != nullptr) { + server->m_pServerCallbacks->onDisconnect(server); } - m_connectedCount++; // Increment the number of connected devices count. - break; - } // ESP_GATTS_CONNECT_EVT - - - // ESP_GATTS_CREATE_EVT - // Called when a new service is registered as having been created. - // - // create: - // * esp_gatt_status_t status - // * uint16_t service_handle - // * esp_gatt_srvc_id_t service_id - // - case ESP_GATTS_CREATE_EVT: { - BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid, param->create.service_id.id.inst_id); // <--- very big bug for multi services with the same uuid - m_serviceMap.setByHandle(param->create.service_handle, pService); - m_semaphoreCreateEvt.give(); - break; - } // ESP_GATTS_CREATE_EVT - - - // ESP_GATTS_DISCONNECT_EVT - // - // disconnect - // - uint16_t conn_id - // - esp_bd_addr_t remote_bda - // - esp_gatt_conn_reason_t reason - // - // If we receive a disconnect event then invoke the callback for disconnects (if one is present). - // we also want to start advertising again. - case ESP_GATTS_DISCONNECT_EVT: { - m_connectedCount--; // Decrement the number of connected devices count. - if (m_pServerCallbacks != nullptr) { // If we have callbacks, call now. - m_pServerCallbacks->onDisconnect(this); - } - startAdvertising(); //- do this with some delay from the loop() - removePeerDevice(param->disconnect.conn_id, false); - break; - } // ESP_GATTS_DISCONNECT_EVT - - - // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. - // - // read: - // - uint16_t conn_id - // - uint32_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool is_long - // - bool need_rsp - // - case ESP_GATTS_READ_EVT: { - break; - } // ESP_GATTS_READ_EVT - - - // ESP_GATTS_REG_EVT - // reg: - // - esp_gatt_status_t status - // - uint16_t app_id - // - case ESP_GATTS_REG_EVT: { - m_gatts_if = gatts_if; - m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app. - break; - } // ESP_GATTS_REG_EVT - - - // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. - // - // write: - // - uint16_t conn_id - // - uint16_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool need_rsp - // - bool is_prep - // - uint16_t len - // - uint8_t* value - // - case ESP_GATTS_WRITE_EVT: { - break; - } - - case ESP_GATTS_OPEN_EVT: - m_semaphoreOpenEvt.give(param->open.status); - break; + /* Connection terminated; resume advertising */ + //NimBLEDevice::startAdvertising(); + server->removePeerDevice(event->disconnect.conn.conn_handle, false); + break; + } // BLE_GAP_EVENT_DISCONNECT + + case BLE_GAP_EVENT_SUBSCRIBE: { + NIMBLE_LOGI(LOG_TAG, "subscribe event; cur_notify=%d\n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, event->subscribe.attr_handle); + + NimBLECharacteristic* pChr = server->getChrByHandle(event->subscribe.attr_handle); + if(pChr != nullptr) { + pChr->setSubscribe(event); + } + /* + uint8_t numSvcs = server->m_serviceMap.getRegisteredServiceCount(); + NimBLEService* pService = server->m_serviceMap.getFirst(); + + for(int i = 0; i < numSvcs; i++) { + uint8_t numChrs = pService->m_characteristicMap.getSize(); + NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); + + for( int d = 0; d < numChrs; d++) { + if(pChr->m_handle == event->subscribe.attr_handle) { + pChr->setSubscribe(event); + return 0; + } + pChr = pService->m_characteristicMap.getNext(); + } + + pService = server->m_serviceMap.getNext(); + } + + NIMBLE_LOGE(LOG_TAG, "Subscribe handle not found"); + */ + break; + } // 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); + server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value); + break; + } // BLE_GAP_EVENT_MTU + + case BLE_GAP_EVENT_NOTIFY_TX: { + if(event->notify_tx.indication && event->notify_tx.status != 0) { + NimBLECharacteristic* pChr = server->getChrByHandle(event->notify_tx.attr_handle); + if(pChr != nullptr) { + pChr->m_semaphoreConfEvt.give(event->notify_tx.status); + } + /* + uint8_t numSvcs = server->m_serviceMap.getRegisteredServiceCount(); + NimBLEService* pService = server->m_serviceMap.getFirst(); + + for(int i = 0; i < numSvcs; i++) { + uint8_t numChrs = pService->m_characteristicMap.getSize(); + NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); + + for( int d = 0; d < numChrs; d++) { + if(pChr->m_handle == event->notify_tx.attr_handle) { + pChr->m_semaphoreConfEvt.give(event->notify_tx.status); + return 0; + } + pChr = pService->m_characteristicMap.getNext(); + } + + pService = server->m_serviceMap.getNext(); + } + + NIMBLE_LOGE(LOG_TAG, "Subscribe handle not found"); + */ + } + break; + } // BLE_GAP_EVENT_NOTIFY_TX default: break; } - // Invoke the handler for every Service we have. - m_serviceMap.handleGATTServerEvent(event, gatts_if, param); -*/ - return 0; NIMBLE_LOGD(LOG_TAG, "<< handleGATTServerEvent"); + return 0; } // handleGATTServerEvent -/** - * @brief Register the app. - * - * @return N/A - */ - /* -void BLEServer::registerApp(uint16_t m_appId) { - ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId); - m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event. - ::esp_ble_gatts_app_register(m_appId); - m_semaphoreRegisterAppEvt.wait("registerApp"); - ESP_LOGD(LOG_TAG, "<< registerApp"); -} // registerApp -*/ - /** * @brief Set the server callbacks. * @@ -328,13 +342,48 @@ void BLEServer::removeService(BLEService* service) { * Start the server advertising its existence. This is a convenience function and is equivalent to * retrieving the advertising object and invoking start upon it. */ - /* -void BLEServer::startAdvertising() { - ESP_LOGD(LOG_TAG, ">> startAdvertising"); - BLEDevice::startAdvertising(); - ESP_LOGD(LOG_TAG, "<< startAdvertising"); +void NimBLEServer::startAdvertising() { + NIMBLE_LOGD(LOG_TAG, ">> startAdvertising"); + NimBLEDevice::startAdvertising(); + NIMBLE_LOGD(LOG_TAG, "<< startAdvertising"); } // startAdvertising -*/ + + +/** + * @brief Stop advertising. + */ +void NimBLEServer::stopAdvertising() { + NIMBLE_LOGD(LOG_TAG, ">> stopAdvertising"); + NimBLEDevice::stopAdvertising(); + NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising"); +} // startAdvertising + + +NimBLECharacteristic* NimBLEServer::getChrByHandle(uint16_t handle) { + if(handle == m_svcChgChrHdl) { + return nullptr; + } + uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); + NimBLEService* pService = m_serviceMap.getFirst(); + + for(int i = 0; i < numSvcs; i++) { + uint8_t numChrs = pService->m_characteristicMap.getSize(); + NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); + + for( int d = 0; d < numChrs; d++) { + if(pChr->m_handle == handle) { + return pChr; + } + pChr = pService->m_characteristicMap.getNext(); + } + + pService = m_serviceMap.getNext(); + } + + NIMBLE_LOGE(LOG_TAG, "Characteristic by handle not found"); + return nullptr; +} + /** * Allow to connect GATT server to peer device * Probably can be used in ANCS for iPhone @@ -382,24 +431,31 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { NIMBLE_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect + /* multi connect support */ -/* TODO do some more tweaks */ void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { - // set mtu in conn_status_t const std::map::iterator it = m_connectedServersMap.find(conn_id); if (it != m_connectedServersMap.end()) { it->second.mtu = mtu; - std::swap(m_connectedServersMap[conn_id], it->second); } } -std::map NimBLEServer::getPeerDevices(bool _client) { +std::map NimBLEServer::getPeerDevices() { return m_connectedServersMap; } +/** + * @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 m_connectedServersMap.find(conn_id)->second.mtu; + auto it = m_connectedServersMap.find(conn_id); + if(it != m_connectedServersMap.cend()) { + return (*it).second.mtu; + } else { + return 0; + } } void NimBLEServer::addPeerDevice(void* peer, bool _client, uint16_t conn_id) { @@ -417,6 +473,7 @@ void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) { } /* multi connect support */ + /** * Update connection parameters can be called only after connection has been established */ @@ -431,9 +488,5 @@ void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, esp_ble_gap_update_conn_params(&conn_params); } */ -/* -void BLEServer::disconnect(uint16_t connId){ - esp_ble_gatts_close(m_gatts_if, connId); -} -*/ + #endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 5eed9ba6..37e67c54 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -22,7 +22,7 @@ #include "NimBLEAddress.h" #include "NimBLEUUID.h" #include "NimBLEAdvertising.h" -//#include "BLECharacteristic.h" +//#include "NimBLECharacteristic.h" #include "NimBLEService.h" #include "NimBLESecurity.h" #include "FreeRTOS.h" @@ -31,6 +31,7 @@ #include class NimBLEService; +class NimBLECharacteristic; class NimBLEServerCallbacks; /* TODO possibly refactor this struct */ typedef struct { @@ -43,74 +44,76 @@ typedef struct { /** * @brief A data structure that manages the %BLE servers owned by a BLE server. */ - /* -class BLEServiceMap { +class NimBLEServiceMap { public: - BLEService* getByHandle(uint16_t handle); - BLEService* getByUUID(const char* uuid); - BLEService* getByUUID(BLEUUID uuid, uint8_t inst_id = 0); - void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - void setByHandle(uint16_t handle, BLEService* service); - void setByUUID(const char* uuid, BLEService* service); - void setByUUID(BLEUUID uuid, BLEService* service); - std::string toString(); - BLEService* getFirst(); - BLEService* getNext(); - void removeService(BLEService *service); - int getRegisteredServiceCount(); +// NimBLEService* getByHandle(uint16_t handle); + NimBLEService* getByUUID(const char* uuid); + NimBLEService* getByUUID(NimBLEUUID uuid, uint8_t inst_id = 0); +// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); +// void setByHandle(uint16_t handle, NimBLEService* service); + void setByUUID(const char* uuid, NimBLEService* service); + void setByUUID(NimBLEUUID uuid, NimBLEService* service); + std::string toString(); + NimBLEService* getFirst(); + NimBLEService* getNext(); + void removeService(NimBLEService *service); + int getRegisteredServiceCount(); private: - std::map m_handleMap; - std::map m_uuidMap; - std::map::iterator m_iterator; +// std::map m_handleMap; + std::map m_uuidMap; + std::map::iterator m_iterator; }; -*/ + /** * @brief The model of a %BLE server. */ class NimBLEServer { public: - uint32_t getConnectedCount(); - NimBLEService* createService(const char* uuid); - NimBLEService* createService(NimBLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); -// BLEAdvertising* getAdvertising(); - void setCallbacks(NimBLEServerCallbacks* pCallbacks); -// void startAdvertising(); -// void removeService(BLEService* service); -// BLEService* getServiceByUUID(const char* uuid); -// BLEService* getServiceByUUID(BLEUUID uuid); -// bool connect(BLEAddress address); -// void disconnect(uint16_t connId); -// uint16_t m_appId; -// void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + uint32_t getConnectedCount(); + NimBLEService* createService(const char* uuid); + NimBLEService* createService(NimBLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0); + NimBLEAdvertising* getAdvertising(); + void setCallbacks(NimBLEServerCallbacks* pCallbacks); + void startAdvertising(); + void stopAdvertising(); + void start(); +// void removeService(BLEService* service); + NimBLEService* getServiceByUUID(const char* uuid); + NimBLEService* getServiceByUUID(NimBLEUUID uuid); + int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); + NimBLECharacteristic* getChrByHandle(uint16_t handle); +// bool connect(BLEAddress address); +// uint16_t m_appId; +// void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); /* multi connection support */ - std::map getPeerDevices(bool client); - void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); - void removePeerDevice(uint16_t conn_id, bool client); + std::map getPeerDevices(); + void addPeerDevice(void* peer, bool is_client, uint16_t conn_id); + void removePeerDevice(uint16_t conn_id, bool client); NimBLEServer* getServerByConnId(uint16_t conn_id); - void updatePeerMTU(uint16_t connId, uint16_t mtu); - uint16_t getPeerMTU(uint16_t conn_id); - uint16_t getConnId(); + void updatePeerMTU(uint16_t connId, uint16_t mtu); + uint16_t getPeerMTU(uint16_t conn_id); + uint16_t getConnId(); private: NimBLEServer(); //friend class BLEService; - //friend class BLECharacteristic; + friend class NimBLECharacteristic; friend class NimBLEDevice; + friend class NimBLEAdvertising; //esp_ble_adv_data_t m_adv_data; // BLEAdvertising m_bleAdvertising; - uint16_t m_connId; - uint32_t m_connectedCount; - uint16_t m_gatts_if; + uint16_t m_connId; + uint16_t m_svcChgChrHdl; std::map m_connectedServersMap; //FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); //FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); //FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); - //BLEServiceMap m_serviceMap; + NimBLEServiceMap m_serviceMap; NimBLEServerCallbacks* m_pServerCallbacks = nullptr; //void createApp(uint16_t appId); diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index be830696..93283943 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -40,7 +40,8 @@ static const char* LOG_TAG = "NimBLEService"; // Tag for logging. * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles) : NimBLEService(NimBLEUUID(uuid), numHandles) { +NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer) +: NimBLEService(NimBLEUUID(uuid), numHandles, pServer) { } @@ -49,10 +50,10 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles) : NimBLEServ * @param [in] uuid The UUID of the service. * @param [in] numHandles The maximum number of handles associated with the service. */ -NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles) { +NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer) { m_uuid = uuid; m_handle = NULL_HANDLE; - m_pServer = nullptr; + m_pServer = pServer; //m_serializeMutex.setName("BLEService"); m_lastCreatedCharacteristic = nullptr; m_numHandles = numHandles; @@ -65,7 +66,7 @@ NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles) { * @param [in] gatts_if The handle of the GATT server interface. * @return N/A. */ - +/* void NimBLEService::executeCreate(NimBLEServer* pServer) { NIMBLE_LOGD(LOG_TAG, ">> executeCreate() - Creating service service uuid: %s", getUUID().toString().c_str()); m_pServer = pServer; @@ -85,14 +86,14 @@ void NimBLEService::executeCreate(NimBLEServer* pServer) { // m_semaphoreCreateEvt.wait("executeCreate"); NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); } // executeCreate - +*/ /** * @brief Delete the service. * Delete the service. * @return N/A. */ - +/* void NimBLEService::executeDelete() { NIMBLE_LOGD(LOG_TAG, ">> executeDelete()"); // m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT @@ -107,7 +108,7 @@ void NimBLEService::executeDelete() { // m_semaphoreDeleteEvt.wait("executeDelete"); NIMBLE_LOGD(LOG_TAG, "<< executeDelete"); } // executeDelete - +*/ /** * @brief Dump details of this BLE GATT service. @@ -138,46 +139,46 @@ NimBLEUUID NimBLEService::getUUID() { */ bool NimBLEService::start() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). -// - NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str()); + NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str()); int rc = 0; // Nimble requires an array of services to be sent to the api // Since we are adding 1 at a time we create an array of 2 and set the type // of the second service to 0 to indicate the end of the array. ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; - ble_gatt_chr_def* pChtr_a = nullptr; + ble_gatt_chr_def* pChr_a = nullptr; svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; svc[0].uuid = &m_uuid.getNative()->u; - uint8_t numChtrs = m_characteristicMap.getSize(); - NIMBLE_LOGE(LOG_TAG,"adding %d characteristics", numChtrs); - if(numChtrs < 1){ + + uint8_t numChrs = m_characteristicMap.getSize(); + + NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); + + if(numChrs < 1){ svc[0].characteristics = NULL; }else{ - pChtr_a = new ble_gatt_chr_def[numChtrs+1]; + // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end + // of the characteristics for the service. We create 1 extra and set it to null + // for this purpose. + pChr_a = new ble_gatt_chr_def[numChrs+1]; NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); - for(uint8_t i=0; i < numChtrs; i++) { - char buf[50]; - NIMBLE_LOGI(LOG_TAG,"adding char uuid: %s", ble_uuid_to_str(&pCharacteristic->getUUID().getNative()->u, buf)); - pChtr_a[i].uuid = &pCharacteristic->getUUID().getNative()->u; - NIMBLE_LOGI(LOG_TAG, "pchtr_a[%d] uuid: %s", i, ble_uuid_to_str(pChtr_a[i].uuid, buf)); - pChtr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; - pChtr_a[i].arg = pCharacteristic; - pChtr_a[i].descriptors = NULL; - pChtr_a[i].flags = pCharacteristic->m_properties; - NIMBLE_LOGI(LOG_TAG, "pchtr_a props: %04x", pChtr_a[i].flags); - pChtr_a[i].min_key_size = 0; - pChtr_a[i].val_handle = &pCharacteristic->m_handle; + + for(uint8_t i=0; i < numChrs; i++) { + pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u; + pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; + pChr_a[i].arg = pCharacteristic; + pChr_a[i].descriptors = NULL; + pChr_a[i].flags = pCharacteristic->m_properties; + pChr_a[i].min_key_size = 0; + pChr_a[i].val_handle = &pCharacteristic->m_handle; pCharacteristic = m_characteristicMap.getNext(); } - pChtr_a[numChtrs].uuid = NULL; - svc[0].characteristics = pChtr_a; + pChr_a[numChrs].uuid = NULL; + svc[0].characteristics = pChr_a; } - + + // end of services must indicate to api with type = 0 svc[1].type = 0; rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)svc); @@ -295,8 +296,9 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const char* uuid, uint * @return The new BLE characteristic. */ NimBLECharacteristic* NimBLEService::createCharacteristic(NimBLEUUID uuid, uint32_t properties) { - NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties); + NimBLECharacteristic* pCharacteristic = new NimBLECharacteristic(uuid, properties, this); addCharacteristic(pCharacteristic); + //pCharacteristic->executeCreate(this); return pCharacteristic; } // createCharacteristic diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 106b548d..b4330297 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -61,8 +61,8 @@ class NimBLEService { NimBLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, uint32_t properties); void dump(); - void executeCreate(NimBLEServer* pServer); - void executeDelete(); +// void executeCreate(NimBLEServer* pServer); +// void executeDelete(); NimBLECharacteristic* getCharacteristic(const char* uuid); NimBLECharacteristic* getCharacteristic(NimBLEUUID uuid); NimBLEUUID getUUID(); @@ -71,11 +71,11 @@ class NimBLEService { // void stop(); std::string toString(); uint16_t getHandle(); -// uint8_t m_instId = 0; + uint8_t m_instId = 0; private: - NimBLEService(const char* uuid, uint16_t numHandles); - NimBLEService(NimBLEUUID uuid, uint16_t numHandles); + NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); + NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer); friend class NimBLEServer; // friend class BLEServiceMap; // friend class BLEDescriptor; diff --git a/src/NimBLEServiceMap.cpp b/src/NimBLEServiceMap.cpp new file mode 100644 index 00000000..9f9fa3dc --- /dev/null +++ b/src/NimBLEServiceMap.cpp @@ -0,0 +1,148 @@ +/* + * NimBLEService.cpp + * + * Created: on March 7, 2020 + * Author H2zero + * + * Originally: + * + * BLEServiceMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" + + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +NimBLEService* NimBLEServiceMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + +/** + * @brief Return the service by UUID. + * @param [in] UUID The UUID to look up the service. + * @return The characteristic. + */ +NimBLEService* NimBLEServiceMap::getByUUID(NimBLEUUID uuid, uint8_t inst_id) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + //return m_uuidMap.at(uuid.toString()); + return nullptr; +} // getByUUID + + +/** + * @brief Return the service by handle. + * @param [in] handle The handle to look up the service. + * @return The service. + */ +/* +NimBLEService* NimBLEServiceMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle +*/ + +/** + * @brief Set the service by UUID. + * @param [in] uuid The uuid of the service. + * @param [in] characteristic The service to cache. + * @return N/A. + */ +void NimBLEServiceMap::setByUUID(NimBLEUUID uuid, NimBLEService* service) { + m_uuidMap.insert(std::pair(service, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the service by handle. + * @param [in] handle The handle of the service. + * @param [in] service The service to cache. + * @return N/A. + */ + /* +void NimBLEServiceMap::setByHandle(uint16_t handle, NimBLEService* service) { + m_handleMap.insert(std::pair(handle, service)); +} // setByHandle +*/ + +/** + * @brief Return a string representation of the service map. + * @return A string representation of the service map. + */ +std::string NimBLEServiceMap::toString() { + std::string res; + //char hex[5]; + for (auto &myPair: m_uuidMap) { + // res += "handle: 0x"; + // snprintf(hex, sizeof(hex), "%04x", myPair.first); + // res += hex; + res += ", uuid: " + myPair.second + "\n"; + } + return res; +} // toString + +/* +void BLEServiceMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every Service we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} +*/ + +/** + * @brief Get the first service in the map. + * @return The first service in the map. + */ +NimBLEService* NimBLEServiceMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + +/** + * @brief Get the next service in the map. + * @return The next service in the map. + */ +NimBLEService* NimBLEServiceMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEService* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext + +/** + * @brief Removes service from maps. + * @return N/A. + */ +void NimBLEServiceMap::removeService(NimBLEService* service) { + //m_handleMap.erase(service->getHandle()); + m_uuidMap.erase(service); +} // removeService + +/** + * @brief Returns the amount of registered services + * @return amount of registered services + */ +int NimBLEServiceMap::getRegisteredServiceCount(){ + //return m_handleMap.size(); + return m_uuidMap.size(); +} + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEValue.cpp b/src/NimBLEValue.cpp new file mode 100644 index 00000000..b21f9f83 --- /dev/null +++ b/src/NimBLEValue.cpp @@ -0,0 +1,140 @@ +/* + * NimNimBLEValue.cpp + * + * Created: on March 6, 2020 + * Author H2zero + * + * Originally: + * + * BLEValue.cpp + * + * Created on: Jul 17, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEValue.h" +//#include "NimBLELog.h" + +static const char* LOG_TAG="NimBLEValue"; + +NimBLEValue::NimBLEValue() { + m_accumulation = ""; + m_value = ""; + m_readOffset = 0; +} // NimBLEValue + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] part A message part being added. + */ +void NimBLEValue::addPart(std::string part) { +// NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); + m_accumulation += part; +} // addPart + + +/** + * @brief Add a message part to the accumulation. + * The accumulation is a growing set of data that is added to until a commit or cancel. + * @param [in] pData A message part being added. + * @param [in] length The number of bytes being added. + */ +void NimBLEValue::addPart(uint8_t* pData, size_t length) { +// NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length); + m_accumulation += std::string((char*) pData, length); +} // addPart + + +/** + * @brief Cancel the current accumulation. + */ +void NimBLEValue::cancel() { +// NIMBLE_LOGD(LOG_TAG, ">> cancel"); + m_accumulation = ""; + m_readOffset = 0; +} // cancel + + +/** + * @brief Commit the current accumulation. + * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces + * of the overall message. After the last part has been received, we may perform a commit which means that + * we now have the complete message and commit the change as a unit. + */ +void NimBLEValue::commit() { +// NIMBLE_LOGD(LOG_TAG, ">> commit"); + // If there is nothing to commit, do nothing. + if (m_accumulation.length() == 0) return; + setValue(m_accumulation); + m_accumulation = ""; + m_readOffset = 0; +} // commit + + +/** + * @brief Get a pointer to the data. + * @return A pointer to the data. + */ +uint8_t* NimBLEValue::getData() { + return (uint8_t*) m_value.data(); +} + + +/** + * @brief Get the length of the data in bytes. + * @return The length of the data in bytes. + */ +size_t NimBLEValue::getLength() { + return m_value.length(); +} // getLength + + +/** + * @brief Get the read offset. + * @return The read offset into the read. + */ +uint16_t NimBLEValue::getReadOffset() { + return m_readOffset; +} // getReadOffset + + +/** + * @brief Get the current value. + */ +std::string NimBLEValue::getValue() { + return m_value; +} // getValue + + +/** + * @brief Set the read offset + * @param [in] readOffset The offset into the read. + */ +void NimBLEValue::setReadOffset(uint16_t readOffset) { + m_readOffset = readOffset; +} // setReadOffset + + +/** + * @brief Set the current value. + */ +void NimBLEValue::setValue(std::string value) { + m_value = value; +} // setValue + + +/** + * @brief Set the current value. + * @param [in] pData The data for the current value. + * @param [in] The length of the new current value. + */ +void NimBLEValue::setValue(uint8_t* pData, size_t length) { + m_value = std::string((char*) pData, length); +} // setValue + + +#endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEValue.h b/src/NimBLEValue.h new file mode 100644 index 00000000..2fa1fcb4 --- /dev/null +++ b/src/NimBLEValue.h @@ -0,0 +1,46 @@ +/* + * NimBLEValue.h + * + * Created: on March 6, 2020 + * Author H2zero + * + * Originally: + * + * BLEValue.h + * + * Created on: Jul 17, 2017 + * Author: kolban + */ + +#ifndef MAIN_BLEVALUE_H_ +#define MAIN_BLEVALUE_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include + +/** + * @brief The model of a %BLE value. + */ +class NimBLEValue { +public: + NimBLEValue(); + void addPart(std::string part); + void addPart(uint8_t* pData, size_t length); + void cancel(); + void commit(); + uint8_t* getData(); + size_t getLength(); + uint16_t getReadOffset(); + std::string getValue(); + void setReadOffset(uint16_t readOffset); + void setValue(std::string value); + void setValue(uint8_t* pData, size_t length); + +private: + std::string m_accumulation; + uint16_t m_readOffset; + std::string m_value; + +}; +#endif // CONFIG_BT_ENABLED +#endif /* MAIN_BLEVALUE_H_ */ \ No newline at end of file From a862b679a754626adee23249f35857a42bcce798 Mon Sep 17 00:00:00 2001 From: h2zero <32826625+h2zero@users.noreply.github.com> Date: Sun, 8 Mar 2020 22:10:37 -0600 Subject: [PATCH 11/62] Update README.md --- README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c41e1a6c..56575594 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ +# *** UPDATE *** + +Server code nearing readyness, checkout the ServerDev branch if you would like to assist with testing + +# ************** + + # NimBLE-Arduino A fork of the NimBLE library structured for compilation with Ardruino, designed for use with ESP32. Why? Because the Bluedroid library is too bulky, In testing I have found an initial code size reduction of ~115k and reduced ram usage by ~37k. + # Installation: Download .zip @@ -16,13 +24,16 @@ This library is intended to be compatible with the current BLE library classes, At this time only the client code has been (nearly) fully implemented and work has started on the server code. -# New Features: +# Features: Multiple clients are supported, up to 3 presently. + # Todo: -1. Implement server code. -2. Create documentation. -3. Examples. +1. Complete server implementation. +2. Code cleanup. +3. Create documentation. +4. Examples. + From fd8a874756e3188da79c6d0b6452b08c041d7f41 Mon Sep 17 00:00:00 2001 From: h2zero <32826625+h2zero@users.noreply.github.com> Date: Sun, 8 Mar 2020 22:13:09 -0600 Subject: [PATCH 12/62] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56575594..dc562efe 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Server code nearing readyness, checkout the ServerDev branch if you would like to assist with testing -# ************** +# **************** # NimBLE-Arduino From 0fe36a1ec2d96dbb167cca635586589c1ad94115 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 10 Mar 2020 14:38:04 -0600 Subject: [PATCH 13/62] Implement setting scan response data. Don't set advertising uuids if using custom data. --- src/NimBLEAdvertising.cpp | 75 ++++++--------------------------------- src/NimBLEAdvertising.h | 2 +- src/NimBLEDevice.h | 1 + 3 files changed, 12 insertions(+), 66 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 37fc7ff5..912afe28 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -178,19 +178,19 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem * @brief Set the advertisement data that is to be published in a scan response. * @param [in] advertisementData The data to be advertised. */ - /* -void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) { - ESP_LOGD(LOG_TAG, ">> setScanResponseData"); - esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw( +void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) { + NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData"); + int rc = ble_gap_adv_rsp_set_data( (uint8_t*)advertisementData.getPayload().data(), advertisementData.getPayload().length()); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc)); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc)); } m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data. - ESP_LOGD(LOG_TAG, "<< setScanResponseData"); + NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData"); } // setScanResponseData -*/ + + /** * @brief Start advertising. * Start advertising. @@ -203,7 +203,7 @@ void NimBLEAdvertising::start() { int rc = 0; uint8_t addressType; - if (!m_advSvcsSet && numServices > 0) { + if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { for (int i = 0; i < numServices; i++) { if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16, @@ -302,62 +302,7 @@ void NimBLEAdvertising::start() { NIMBLE_LOGE(LOG_TAG, "error enabling advertisement; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); abort(); } - -/* - m_advData.service_uuid_len = 16 * numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; - uint8_t* p = m_advData.p_service_uuid; - for (int i = 0; i < numServices; i++) { - ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); - BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128(); - memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16); - p += 16; - } - } else { - m_advData.service_uuid_len = 0; - ESP_LOGD(LOG_TAG, "- no services advertised"); - } -*/ -/* - - - if (!m_customAdvData) { - // Set the configuration for advertising. - m_advData.set_scan_rsp = false; - m_advData.include_name = !m_scanResp; - m_advData.include_txpower = !m_scanResp; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - if (!m_customScanResponseData && m_scanResp) { - m_advData.set_scan_rsp = true; - m_advData.include_name = m_scanResp; - m_advData.include_txpower = m_scanResp; - errRc = ::esp_ble_gap_config_adv_data(&m_advData); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data (Scan response): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - } - - // If we had services to advertise then we previously allocated some storage for them. - // Here we release that storage. - if (m_advData.service_uuid_len > 0) { - delete[] m_advData.p_service_uuid; - m_advData.p_service_uuid = nullptr; - } - - // Start advertising. - errRc = ::esp_ble_gap_start_advertising(&m_advParams); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } -*/ + NIMBLE_LOGD(LOG_TAG, "<< Advertising start"); } // start diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 355750f3..dd70e948 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -75,7 +75,7 @@ class NimBLEAdvertising { void setMinInterval(uint16_t mininterval); void setAdvertisementData(NimBLEAdvertisementData& advertisementData); // void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); -// void setScanResponseData(NimBLEAdvertisementData& advertisementData); + void setScanResponseData(NimBLEAdvertisementData& advertisementData); void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM); //void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index fd91640e..3c673bf0 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -50,6 +50,7 @@ #define BLEAdvertising NimBLEAdvertising #define BLEServerCallbacks NimBLEServerCallbacks #define BLECharacteristicCallbacks NimBLECharacteristicCallbacks +#define BLEAdvertisementData NimBLEAdvertisementData /** * @brief BLE functions. From 3064cef79ccde1ccaf6986037b97c59dedbff42f Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 10 Mar 2020 15:48:21 -0600 Subject: [PATCH 14/62] Server start now called on advertising start if not already done. Added settings to SDKconfig. --- src/NimBLEAdvertising.cpp | 5 +++++ src/NimBLEAdvertising.h | 1 + src/NimBLEServer.cpp | 7 +++++++ src/NimBLEServer.h | 3 ++- src/sdkconfig.h | 3 +++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 912afe28..c060bb67 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -202,6 +202,11 @@ void NimBLEAdvertising::start() { int numServices = m_serviceUUIDs.size(); int rc = 0; uint8_t addressType; + + NimBLEServer* pServer = NimBLEDevice::createServer(); + if(!pServer->m_gattsStarted){ + pServer->start(); + } if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { for (int i = 0; i < numServices; i++) { diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index dd70e948..d8010b91 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -33,6 +33,7 @@ #define ESP_BLE_ADV_FLAG_NON_LIMIT_DISC (0x00 ) /* ************************* */ + /** * @brief Advertisement data set by the programmer to be published by the %BLE server. */ diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 24db1f8c..4d961ea1 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -36,6 +36,7 @@ NimBLEServer::NimBLEServer() { m_connId = BLE_HS_CONN_HANDLE_NONE; m_svcChgChrHdl = 0xffff; m_pServerCallbacks = nullptr; + m_gattsStarted = false; } // BLEServer /* @@ -137,6 +138,11 @@ uint16_t NimBLEServer::getConnId() { * 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)); @@ -156,6 +162,7 @@ void NimBLEServer::start() { } NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); + m_gattsStarted = true; } diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 37e67c54..223979f5 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -108,13 +108,14 @@ class NimBLEServer { // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint16_t m_svcChgChrHdl; + bool m_gattsStarted; std::map m_connectedServersMap; //FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); //FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); //FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); NimBLEServiceMap m_serviceMap; - NimBLEServerCallbacks* m_pServerCallbacks = nullptr; + NimBLEServerCallbacks* m_pServerCallbacks; //void createApp(uint16_t appId); //uint16_t getGattsIf(); diff --git a/src/sdkconfig.h b/src/sdkconfig.h index b2135f15..b4701d8d 100644 --- a/src/sdkconfig.h +++ b/src/sdkconfig.h @@ -319,6 +319,9 @@ #define CONFIG_WL_SECTOR_SIZE_4096 1 #define CONFIG_WL_SECTOR_SIZE 4096 +#define CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_DATA_PATH_EFF 1 +#define CONFIG_ARDUINO_EVENT_RUNNING_CORE 1 + /* List of deprecated options */ #define CONFIG_MAKE_WARN_UNDEFINED_VARIABLES CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES #define CONFIG_PYTHON CONFIG_SDK_PYTHON From e1e87eec71d833d8fe6769d32a79c75e0ef1ff1e Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 10 Mar 2020 22:19:45 -0600 Subject: [PATCH 15/62] Start implementing descriptors, compiles but untested. --- src/NimBLE2902.cpp | 70 +++++++++ src/NimBLE2902.h | 41 +++++ src/NimBLEAddress.h | 4 + src/NimBLECharacteristic.cpp | 36 ++--- src/NimBLECharacteristic.h | 50 +++--- src/NimBLEClient.h | 2 +- src/NimBLEDescriptor.cpp | 289 +++++++++++++++++++++++++++++++++++ src/NimBLEDescriptor.h | 93 +++++++++++ src/NimBLEDescriptorMap.cpp | 152 ++++++++++++++++++ src/NimBLEDevice.cpp | 10 -- src/NimBLESecurity.h | 5 + src/NimBLEServer.h | 3 - 12 files changed, 696 insertions(+), 59 deletions(-) create mode 100644 src/NimBLE2902.cpp create mode 100644 src/NimBLE2902.h create mode 100644 src/NimBLEDescriptor.cpp create mode 100644 src/NimBLEDescriptor.h create mode 100644 src/NimBLEDescriptorMap.cpp diff --git a/src/NimBLE2902.cpp b/src/NimBLE2902.cpp new file mode 100644 index 00000000..3f2dbf0d --- /dev/null +++ b/src/NimBLE2902.cpp @@ -0,0 +1,70 @@ +/* + * NimBLE2902.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLE2902.cpp + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLE2902.h" + +NimBLE2902::NimBLE2902() : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902)) { + uint8_t data[2] = { 0, 0 }; + setValue(data, 2); +} // NimBLE2902 + + +/** + * @brief Get the notifications value. + * @return The notifications value. True if notifications are enabled and false if not. + */ +bool NimBLE2902::getNotifications() { + return (getValue()[0] & (1 << 0)) != 0; +} // getNotifications + + +/** + * @brief Get the indications value. + * @return The indications value. True if indications are enabled and false if not. + */ +bool NimBLE2902::getIndications() { + return (getValue()[0] & (1 << 1)) != 0; +} // getIndications + + +/** + * @brief Set the indications flag. + * @param [in] flag The indications flag. + */ +void NimBLE2902::setIndications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 1; + else pValue[0] &= ~(1 << 1); +} // setIndications + + +/** + * @brief Set the notifications flag. + * @param [in] flag The notifications flag. + */ +void NimBLE2902::setNotifications(bool flag) { + uint8_t *pValue = getValue(); + if (flag) pValue[0] |= 1 << 0; + else pValue[0] &= ~(1 << 0); +} // setNotifications + +#endif \ No newline at end of file diff --git a/src/NimBLE2902.h b/src/NimBLE2902.h new file mode 100644 index 00000000..e4cce4bc --- /dev/null +++ b/src/NimBLE2902.h @@ -0,0 +1,41 @@ +/* + * NimBLE2902.h + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLE2902.h + * + * Created on: Jun 25, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE2902_H_ +#define MAIN_NIMBLE2902_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDescriptor.h" + +/** + * @brief Descriptor for Client Characteristic Configuration. + * + * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + */ +class NimBLE2902: public NimBLEDescriptor { +public: + NimBLE2902(); + bool getNotifications(); + bool getIndications(); + void setNotifications(bool flag); + void setIndications(bool flag); + +}; // NimBLE2902 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLE2902_H_ */ \ No newline at end of file diff --git a/src/NimBLEAddress.h b/src/NimBLEAddress.h index 86179361..ac1f26ca 100644 --- a/src/NimBLEAddress.h +++ b/src/NimBLEAddress.h @@ -18,6 +18,10 @@ #if defined(CONFIG_BT_ENABLED) #include "nimble/ble.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ #include diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index e0f17a91..fc5c6e7b 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -68,13 +68,12 @@ NimBLECharacteristic::~NimBLECharacteristic() { * @param [in] pDescriptor * @return N/A. */ - /* -void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) { - ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); +void BLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); - ESP_LOGD(LOG_TAG, "<< addDescriptor()"); + NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()"); } // addDescriptor -*/ + /** * @brief Register a new characteristic with the ESP runtime. @@ -123,27 +122,26 @@ void NimBLECharacteristic::executeCreate(NimBLEService* pService) { } // executeCreate */ + /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ - /* -BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { - return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) { + return m_descriptorMap.getByUUID(NimBLEUUID(descriptorUUID)); } // getDescriptorByUUID -*/ + /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ - /* -BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { +NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(NimBLEUUID descriptorUUID) { return m_descriptorMap.getByUUID(descriptorUUID); } // getDescriptorByUUID -*/ + /** * @brief Get the handle of the characteristic. @@ -153,10 +151,12 @@ uint16_t NimBLECharacteristic::getHandle() { return m_handle; } // getHandle + void NimBLECharacteristic::setAccessPermissions(uint16_t perm) { m_permissions = perm; } + uint8_t NimBLECharacteristic::getProperties() { return m_properties; } // getProperties @@ -870,8 +870,7 @@ NimBLECharacteristicCallbacks::~NimBLECharacteristicCallbacks() {} * @param [in] pCharacteristic The characteristic that is the source of the event. */ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onRead: default"); - NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onRead"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default"); } // onRead @@ -880,8 +879,7 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic * @param [in] pCharacteristic The characteristic that is the source of the event. */ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onWrite: default"); - NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onWrite"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default"); } // onWrite @@ -890,8 +888,7 @@ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristi * @param [in] pCharacteristic The characteristic that is the source of the event. */ void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacteristic) { - NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onNotify: default"); - NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onNotify"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onNotify: default"); } // onNotify @@ -902,8 +899,7 @@ void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacterist * @param [in] code Additional code of underlying errors */ void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { - NIMBLE_LOGD("NimBLECharacteristicCallbacks", ">> onStatus: default"); - NIMBLE_LOGD("NimBLECharacteristicCallbacks", "<< onStatus"); + NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default"); } // onStatus #endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 02ff8563..dac1ac25 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -15,10 +15,11 @@ #define MAIN_NIMBLECHARACTERISTIC_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) + #include "NimBLEService.h" #include "NimBLEUUID.h" -//#include "BLEDescriptor.h" #include "NimBLEValue.h" +#include "NimBLEDescriptor.h" #include "FreeRTOS.h" #include "host/ble_hs.h" @@ -27,31 +28,30 @@ #include class NimBLEService; -//class NimBLEDescriptor; +class NimBLEDescriptor; class NimBLECharacteristicCallbacks; /** * @brief A management structure for %BLE descriptors. */ - /* -class BLEDescriptorMap { +class NimBLEDescriptorMap { public: - void setByUUID(const char* uuid, BLEDescriptor* pDescriptor); - void setByUUID(BLEUUID uuid, BLEDescriptor* pDescriptor); - void setByHandle(uint16_t handle, BLEDescriptor* pDescriptor); - BLEDescriptor* getByUUID(const char* uuid); - BLEDescriptor* getByUUID(BLEUUID uuid); - BLEDescriptor* getByHandle(uint16_t handle); + void setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor); + void setByUUID(NimBLEUUID uuid, NimBLEDescriptor* pDescriptor); +// void setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor); + NimBLEDescriptor* getByUUID(const char* uuid); + NimBLEDescriptor* getByUUID(NimBLEUUID uuid); +// NimBLEDescriptor* getByHandle(uint16_t handle); std::string toString(); - void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); - BLEDescriptor* getFirst(); - BLEDescriptor* getNext(); +// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); + NimBLEDescriptor* getFirst(); + NimBLEDescriptor* getNext(); private: - std::map m_uuidMap; - std::map m_handleMap; - std::map::iterator m_iterator; + std::map m_uuidMap; +// std::map m_handleMap; + std::map::iterator m_iterator; }; -*/ + /** * @brief The model of a %BLE Characteristic. @@ -61,12 +61,12 @@ class BLEDescriptorMap { */ class NimBLECharacteristic { public: -// void addDescriptor(BLEDescriptor* pDescriptor); -// BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); -// BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID); + void addDescriptor(NimBLEDescriptor* pDescriptor); + NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); + NimBLEDescriptor* getDescriptorByUUID(NimBLEUUID descriptorUUID); NimBLEUUID getUUID(); - std::string getValue(); - uint8_t* getData(); + std::string getValue(); + uint8_t* getData(); void indicate(); void notify(bool is_notification = true); @@ -99,15 +99,15 @@ class NimBLECharacteristic { friend class NimBLEServer; friend class NimBLEService; -// friend class BLEDescriptor; -// friend class BLECharacteristicMap; +// friend class NimBLEDescriptor; +// friend class NimBLECharacteristicMap; NimBLECharacteristic(const char* uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); virtual ~NimBLECharacteristic(); NimBLEUUID m_uuid; -// BLEDescriptorMap m_descriptorMap; + NimBLEDescriptorMap m_descriptorMap; uint16_t m_handle; uint16_t m_properties; NimBLECharacteristicCallbacks* m_pCallbacks; diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index db4ec7f3..b909ddcb 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -17,9 +17,9 @@ #if defined(CONFIG_BT_ENABLED) #include "sdkconfig.h" +#include "NimBLEAddress.h" #include "NimBLEAdvertisedDevice.h" #include "NimBLERemoteService.h" -#include "NimBLEAddress.h" #include #include diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp new file mode 100644 index 00000000..87d0d341 --- /dev/null +++ b/src/NimBLEDescriptor.cpp @@ -0,0 +1,289 @@ +/* + * NimBLEDescriptor.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEService.h" +#include "NimBLEDescriptor.h" +#include "NimBLELog.h" + +#include + +static const char* LOG_TAG = "NimBLEDescriptor"; + +#define NULL_HANDLE (0xffff) + + +/** + * @brief NimBLEDescriptor constructor. + */ +NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t len) : NimBLEDescriptor(NimBLEUUID(uuid), len) { +} + +/** + * @brief NimBLEDescriptor constructor. + */ +NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len) { + m_uuid = uuid; + m_value.attr_len = 0; // Initial length is 0. + m_value.attr_max_len = max_len; // Maximum length of the data. + m_handle = NULL_HANDLE; // Handle is initially unknown. + m_pCharacteristic = nullptr; // No initial characteristic. + m_pCallbacks = nullptr; // No initial callback. + + m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. +} // NimBLEDescriptor + + +/** + * @brief NimBLEDescriptor destructor. + */ +NimBLEDescriptor::~NimBLEDescriptor() { + free(m_value.attr_value); // Release the storage we created in the constructor. +} // ~NimBLEDescriptor + + +/** + * @brief Execute the creation of the descriptor with the BLE runtime in ESP. + * @param [in] pCharacteristic The characteristic to which to register this descriptor. + */ + /* +void NimBLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { + NIMBLE_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str()); + + if (m_handle != NULL_HANDLE) { + NIMBLE_LOGE(LOG_TAG, "Descriptor already has a handle."); + return; + } + + m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. + + esp_attr_control_t control; + control.auto_rsp = ESP_GATT_AUTO_RSP; + m_semaphoreCreateEvt.take("executeCreate"); + esp_err_t errRc = ::esp_ble_gatts_add_char_descr( + pCharacteristic->getService()->getHandle(), + getUUID().getNative(), + (esp_gatt_perm_t)m_permissions, + &m_value, + &control); + if (errRc != ESP_OK) { + NIMBLE_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + return; + } + + m_semaphoreCreateEvt.wait("executeCreate"); + NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); +} // executeCreate +*/ + +/** + * @brief Get the BLE handle for this descriptor. + * @return The handle for this descriptor. + */ +uint16_t NimBLEDescriptor::getHandle() { + return m_handle; +} // getHandle + + +/** + * @brief Get the length of the value of this descriptor. + * @return The length (in bytes) of the value of this descriptor. + */ +size_t NimBLEDescriptor::getLength() { + return m_value.attr_len; +} // getLength + + +/** + * @brief Get the UUID of the descriptor. + */ +NimBLEUUID NimBLEDescriptor::getUUID() { + return m_uuid; +} // getUUID + + +/** + * @brief Get the value of this descriptor. + * @return A pointer to the value of this descriptor. + */ +uint8_t* NimBLEDescriptor::getValue() { + return m_value.attr_value; +} // getValue + + +/** + * @brief Handle GATT server events for the descripttor. + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ + /* +void NimBLEDescriptor::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + switch (event) { + // ESP_GATTS_ADD_CHAR_DESCR_EVT + // + // add_char_descr: + // - esp_gatt_status_t status + // - uint16_t attr_handle + // - uint16_t service_handle + // - esp_bt_uuid_t char_uuid + case ESP_GATTS_ADD_CHAR_DESCR_EVT: { + if (m_pCharacteristic != nullptr && + m_uuid.equals(NimBLEUUID(param->add_char_descr.descr_uuid)) && + m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && + m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { + setHandle(param->add_char_descr.attr_handle); + m_semaphoreCreateEvt.give(); + } + break; + } // ESP_GATTS_ADD_CHAR_DESCR_EVT + + // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived. + // + // write: + // - uint16_t conn_id + // - uint16_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool need_rsp + // - bool is_prep + // - uint16_t len + // - uint8_t *value + case ESP_GATTS_WRITE_EVT: { + if (param->write.handle == m_handle) { + setValue(param->write.value, param->write.len); // Set the value of the descriptor. + + if (m_pCallbacks != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. + m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. + } + } // End of ... this is our handle. + + break; + } // ESP_GATTS_WRITE_EVT + + // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived. + // + // read: + // - uint16_t conn_id + // - uint32_t trans_id + // - esp_bd_addr_t bda + // - uint16_t handle + // - uint16_t offset + // - bool is_long + // - bool need_rsp + // + case ESP_GATTS_READ_EVT: { + if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it + + if (m_pCallbacks != nullptr) { // If we have a user supplied callback, invoke it now. + m_pCallbacks->onRead(this); // Invoke the onRead callback method in the callback handler. + } + + } // End of this is our handle + break; + } // ESP_GATTS_READ_EVT + + default: + break; + } // switch event +} // handleGATTServerEvent +*/ + +/** + * @brief Set the callback handlers for this descriptor. + * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. + */ +void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallback) { + m_pCallbacks = pCallback; +} // setCallbacks + + +/** + * @brief Set the handle of this descriptor. + * Set the handle of this descriptor to be the supplied value. + * @param [in] handle The handle to be associated with this descriptor. + * @return N/A. + */ +void NimBLEDescriptor::setHandle(uint16_t handle) { + NIMBLE_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle); + m_handle = handle; + NIMBLE_LOGD(LOG_TAG, "<< setHandle()"); +} // setHandle + + +/** + * @brief Set the value of the descriptor. + * @param [in] data The data to set for the descriptor. + * @param [in] length The length of the data in bytes. + */ +void NimBLEDescriptor::setValue(uint8_t* data, size_t length) { + if (length > BLE_ATT_ATTR_MAX_LEN) { + NIMBLE_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, BLE_ATT_ATTR_MAX_LEN); + return; + } + m_value.attr_len = length; + memcpy(m_value.attr_value, data, length); +} // setValue + + +/** + * @brief Set the value of the descriptor. + * @param [in] value The value of the descriptor in string form. + */ +void NimBLEDescriptor::setValue(std::string value) { + setValue((uint8_t*) value.data(), value.length()); +} // setValue + +void NimBLEDescriptor::setAccessPermissions(uint8_t perm) { + m_permissions = perm; +} + +/** + * @brief Return a string representation of the descriptor. + * @return A string representation of the descriptor. + */ +std::string NimBLEDescriptor::toString() { + char hex[5]; + snprintf(hex, sizeof(hex), "%04x", m_handle); + std::string res = "UUID: " + m_uuid.toString() + ", handle: 0x" + hex; + return res; +} // toString + + +NimBLEDescriptorCallbacks::~NimBLEDescriptorCallbacks() {} + +/** + * @brief Callback function to support a read request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onRead(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onRead: default"); +} // onRead + + +/** + * @brief Callback function to support a write request. + * @param [in] pDescriptor The descriptor that is the source of the event. + */ +void NimBLEDescriptorCallbacks::onWrite(NimBLEDescriptor* pDescriptor) { + NIMBLE_LOGD("NimBLEDescriptorCallbacks", "onWrite: default"); +} // onWrite + + +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h new file mode 100644 index 00000000..483133dd --- /dev/null +++ b/src/NimBLEDescriptor.h @@ -0,0 +1,93 @@ +/* + * NimBLEDescriptor.h + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptor.h + * + * Created on: Jun 22, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEDESCRIPTOR_H_ +#define MAIN_NIMBLEDESCRIPTOR_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "NimBLEUUID.h" +#include "NimBLECharacteristic.h" +#include "FreeRTOS.h" + + +typedef struct +{ + uint16_t attr_max_len; /*!< attribute max value length */ + uint16_t attr_len; /*!< attribute current value length */ + uint8_t *attr_value; /*!< the pointer to attribute value */ +} attr_value_t; + +typedef attr_value_t esp_attr_value_t; /*!< compatibility for esp32 */ + +class NimBLEService; +class NimBLECharacteristic; +class NimBLEDescriptorCallbacks; + +/** + * @brief A model of a %BLE descriptor. + */ +class NimBLEDescriptor { +public: + NimBLEDescriptor(const char* uuid, uint16_t max_len = 100); + NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len = 100); + virtual ~NimBLEDescriptor(); + + uint16_t getHandle(); // Get the handle of the descriptor. + size_t getLength(); // Get the length of the value of the descriptor. + NimBLEUUID getUUID(); // Get the UUID of the descriptor. + uint8_t* getValue(); // Get a pointer to the value of the descriptor. +/* void handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param); +*/ + void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. + void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. + void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(std::string value); // Set the value of the descriptor as a data buffer. + + std::string toString(); // Convert the descriptor to a string representation. + +private: + friend class NimBLEDescriptorMap; + friend class NimBLECharacteristic; + NimBLEUUID m_uuid; + uint16_t m_handle; + NimBLEDescriptorCallbacks* m_pCallbacks; + NimBLECharacteristic* m_pCharacteristic; + uint8_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; +// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); + attr_value_t m_value; + +// void executeCreate(BLECharacteristic* pCharacteristic); + void setHandle(uint16_t handle); +}; // BLEDescriptor + + +/** + * @brief Callbacks that can be associated with a %BLE descriptors to inform of events. + * + * When a server application creates a %BLE descriptor, we may wish to be informed when there is either + * a read or write request to the descriptors value. An application can register a + * sub-classed instance of this class and will be notified when such an event happens. + */ +class NimBLEDescriptorCallbacks { +public: + virtual ~NimBLEDescriptorCallbacks(); + virtual void onRead(NimBLEDescriptor* pDescriptor); + virtual void onWrite(NimBLEDescriptor* pDescriptor); +}; +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */ \ No newline at end of file diff --git a/src/NimBLEDescriptorMap.cpp b/src/NimBLEDescriptorMap.cpp new file mode 100644 index 00000000..9f3fdc89 --- /dev/null +++ b/src/NimBLEDescriptorMap.cpp @@ -0,0 +1,152 @@ +/* + * NimBLEDescriptorMap.cpp + * + * Created: on March 10, 2020 + * Author H2zero + * + * Originally: + * + * BLEDescriptorMap.cpp + * + * Created on: Jun 22, 2017 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include "NimBLECharacteristic.h" +#include "NimBLEDescriptor.h" + + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(const char* uuid) { + return getByUUID(NimBLEUUID(uuid)); +} + + +/** + * @brief Return the descriptor by UUID. + * @param [in] UUID The UUID to look up the descriptor. + * @return The descriptor. If not present, then nullptr is returned. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getByUUID(NimBLEUUID uuid) { + for (auto &myPair : m_uuidMap) { + if (myPair.first->getUUID().equals(uuid)) { + return myPair.first; + } + } + return nullptr; +} // getByUUID + + +/** + * @brief Return the descriptor by handle. + * @param [in] handle The handle to look up the descriptor. + * @return The descriptor. + */ + /* +NimBLEDescriptor* NimBLEDescriptorMap::getByHandle(uint16_t handle) { + return m_handleMap.at(handle); +} // getByHandle +*/ + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void NimBLEDescriptorMap::setByUUID(const char* uuid, NimBLEDescriptor* pDescriptor){ + m_uuidMap.insert(std::pair(pDescriptor, uuid)); +} // setByUUID + + + +/** + * @brief Set the descriptor by UUID. + * @param [in] uuid The uuid of the descriptor. + * @param [in] characteristic The descriptor to cache. + * @return N/A. + */ +void NimBLEDescriptorMap::setByUUID(NimBLEUUID uuid, NimBLEDescriptor* pDescriptor) { + m_uuidMap.insert(std::pair(pDescriptor, uuid.toString())); +} // setByUUID + + +/** + * @brief Set the descriptor by handle. + * @param [in] handle The handle of the descriptor. + * @param [in] descriptor The descriptor to cache. + * @return N/A. + */ + /* +void NimBLEDescriptorMap::setByHandle(uint16_t handle, NimBLEDescriptor* pDescriptor) { + m_handleMap.insert(std::pair(handle, pDescriptor)); +} // setByHandle +*/ + +/** + * @brief Return a string representation of the descriptor map. + * @return A string representation of the descriptor map. + */ +std::string NimBLEDescriptorMap::toString() { + std::string res; + char hex[5]; + int count = 0; + for (auto &myPair : m_uuidMap) { + if (count > 0) {res += "\n";} + snprintf(hex, sizeof(hex), "%04x", myPair.first->getHandle()); + count++; + res += "handle: 0x"; + res += hex; + res += ", uuid: " + myPair.first->getUUID().toString(); + } + return res; +} // toString + + +/** + * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping + * @param [in] event + * @param [in] gatts_if + * @param [in] param + */ + /* +void NimBLEDescriptorMap::handleGATTServerEvent( + esp_gatts_cb_event_t event, + esp_gatt_if_t gatts_if, + esp_ble_gatts_cb_param_t* param) { + // Invoke the handler for every descriptor we have. + for (auto &myPair : m_uuidMap) { + myPair.first->handleGATTServerEvent(event, gatts_if, param); + } +} // handleGATTServerEvent +*/ + +/** + * @brief Get the first descriptor in the map. + * @return The first descriptor in the map. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getFirst() { + m_iterator = m_uuidMap.begin(); + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getFirst + + +/** + * @brief Get the next descriptor in the map. + * @return The next descriptor in the map. + */ +NimBLEDescriptor* NimBLEDescriptorMap::getNext() { + if (m_iterator == m_uuidMap.end()) return nullptr; + NimBLEDescriptor* pRet = m_iterator->first; + m_iterator++; + return pRet; +} // getNext +#endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 5cf97cd7..6287cf78 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -32,16 +32,6 @@ #include "esp32-hal-bt.h" #endif -/* -#if defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLEDevice"; -#endif -*/ - #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEDevice"; diff --git a/src/NimBLESecurity.h b/src/NimBLESecurity.h index 5c7695bb..0c920d0e 100644 --- a/src/NimBLESecurity.h +++ b/src/NimBLESecurity.h @@ -18,6 +18,11 @@ #if defined(CONFIG_BT_ENABLED) #include "host/ble_gap.h" +/**** FIX COMPILATION ****/ +#undef min +#undef max +/**************************/ + #include //#include "esp_gap_ble_api.h" diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 223979f5..04b57d02 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -16,13 +16,10 @@ #define MAIN_NIMBLESERVER_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -//#include -// #include "BLEDevice.h" #include "NimBLEAddress.h" #include "NimBLEUUID.h" #include "NimBLEAdvertising.h" -//#include "NimBLECharacteristic.h" #include "NimBLEService.h" #include "NimBLESecurity.h" #include "FreeRTOS.h" From 9ec71f0bd3993fe37b42fe9f045795adc1b96ff3 Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 11 Mar 2020 15:02:16 -0600 Subject: [PATCH 16/62] Advertising start now prioritises data between the advertising packet and scan response. Now checks for 31 byte limit and truncates. --- src/NimBLEAdvertising.cpp | 76 ++++++++++++++++++++++++++++++++++++--- src/NimBLEAdvertising.h | 1 + 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index c060bb67..4c963e44 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -33,14 +33,15 @@ static const char* LOG_TAG = "NimBLEAdvertising"; */ NimBLEAdvertising::NimBLEAdvertising() { memset(&m_advData, 0, sizeof m_advData); + memset(&m_scanData, 0, sizeof m_scanData); memset(&m_advParams, 0, sizeof m_advParams); const char *name = ble_svc_gap_device_name(); m_advData.name = (uint8_t *)name; m_advData.name_len = strlen(name); m_advData.name_is_complete = 1; - m_advData.tx_pwr_lvl_is_present = 1; - m_advData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + m_scanData.tx_pwr_lvl_is_present = 1; + m_scanData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); m_advData.appearance = 0x00; m_advData.appearance_is_present = 0; @@ -202,6 +203,7 @@ void NimBLEAdvertising::start() { int numServices = m_serviceUUIDs.size(); int rc = 0; uint8_t addressType; + uint8_t payloadLen = 3; //start with 3 bytes for the flags data NimBLEServer* pServer = NimBLEDevice::createServer(); if(!pServer->m_gattsStarted){ @@ -211,6 +213,13 @@ void NimBLEAdvertising::start() { if (!m_customAdvData && !m_advSvcsSet && numServices > 0) { for (int i = 0; i < numServices; i++) { if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_16) { + int add = (m_advData.num_uuids16 > 0) ? 2 : 4; + if((payloadLen + add) > 31){ + m_advData.uuids16_is_complete = 0; + continue; + } + payloadLen += add; + if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16, (m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t)))) { @@ -230,6 +239,13 @@ void NimBLEAdvertising::start() { m_advData.num_uuids16++; } if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_32) { + int add = (m_advData.num_uuids32 > 0) ? 4 : 6; + if((payloadLen + add) > 31){ + m_advData.uuids32_is_complete = 0; + continue; + } + payloadLen += add; + if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32, (m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t)))) { @@ -249,6 +265,13 @@ void NimBLEAdvertising::start() { m_advData.num_uuids32++; } if(m_serviceUUIDs[i].getNative()->u.type == BLE_UUID_TYPE_128){ + int add = (m_advData.num_uuids128 > 0) ? 16 : 18; + if((payloadLen + add) > 31){ + m_advData.uuids128_is_complete = 0; + continue; + } + payloadLen += add; + if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128, (m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t)))) { NIMBLE_LOGE(LOG_TAG, "Error, no mem"); @@ -267,7 +290,52 @@ void NimBLEAdvertising::start() { m_advData.num_uuids128++; } } - + + // check if there is room for the name, if not put it in scan data + if((payloadLen + m_advData.name_len) > 29) { + if(m_scanResp){ + m_scanData.name = m_advData.name; + m_scanData.name_len = m_advData.name_len; + m_scanData.name_is_complete = m_advData.name_is_complete; + m_advData.name = nullptr; + m_advData.name_len = 0; + } else { + // if not using scan response just cut the name down + // leaving 2 bytes for the data specifier. + m_advData.name_len = (29 - payloadLen); + } + m_advData.name_is_complete = 0; + } + + if(m_advData.name_len > 0) { + payloadLen += (m_advData.name_len + 2); + } + + if(m_scanResp) { + // name length + type byte + length byte + tx power type + length + data + if((m_scanData.name_len + 5) > 31) { + // prioritize name data over tx power + m_scanData.tx_pwr_lvl_is_present = 0; + m_scanData.tx_pwr_lvl = 0; + // limit name to 29 to leave room for the data specifiers + if(m_scanData.name_len > 29) { + m_scanData.name_len = 29; + m_scanData.name_is_complete = false; + } + } + + rc = ble_gap_adv_rsp_set_fields(&m_scanData); + if (rc != 0) { + NIMBLE_LOGE(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + abort(); + } + // if not using scan response and there is room, + // throw the tx power data into the advertisment + } else if (payloadLen < 29) { + m_advData.tx_pwr_lvl_is_present = 1; + m_advData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + } + rc = ble_gap_adv_set_fields(&m_advData); if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); @@ -413,7 +481,7 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) { char cdata[3]; cdata[0] = 2; cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 - cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; + cdata[2] = (flag | BLE_HS_ADV_F_BREDR_UNSUP); addData(std::string(cdata, 3)); } // setFlag diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index d8010b91..14c05fd9 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -86,6 +86,7 @@ class NimBLEAdvertising { private: ble_hs_adv_fields m_advData; + ble_hs_adv_fields m_scanData; ble_gap_adv_params m_advParams; std::vector m_serviceUUIDs; bool m_customAdvData = false; // Are we using custom advertising data? From d3390b7a15abe8aecaf878f9ad92b8a621c2f83a Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 11 Mar 2020 22:13:34 -0600 Subject: [PATCH 17/62] Create a map of characteristics that have notifications or indications for better handling events. 2902 descriptors now functional with onwrite callbacks working. Refactored characterisic.notify() and setSubscribe() to use the 2902 descriptor. --- src/NimBLE2902.h | 6 ++ src/NimBLECharacteristic.cpp | 44 +++++++---- src/NimBLECharacteristic.h | 2 +- src/NimBLEDescriptor.cpp | 15 ++-- src/NimBLEServer.cpp | 146 +++++++++++++---------------------- src/NimBLEServer.h | 13 +--- 6 files changed, 100 insertions(+), 126 deletions(-) diff --git a/src/NimBLE2902.h b/src/NimBLE2902.h index e4cce4bc..40e90bc4 100644 --- a/src/NimBLE2902.h +++ b/src/NimBLE2902.h @@ -19,6 +19,9 @@ #include "NimBLEDescriptor.h" +#include + + /** * @brief Descriptor for Client Characteristic Configuration. * @@ -34,6 +37,9 @@ class NimBLE2902: public NimBLEDescriptor { bool getIndications(); void setNotifications(bool flag); void setIndications(bool flag); +private: + friend class NimBLECharacteristic; + std::map m_subscribedMap; }; // NimBLE2902 diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index fc5c6e7b..493e5a2f 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -16,7 +16,6 @@ #include "NimBLEDevice.h" #include "NimBLEUtils.h" #include "NimBLELog.h" -//#include "BLE2902.h" #include @@ -520,27 +519,37 @@ void BLECharacteristic::handleGATTServerEvent( * @return N/A */ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { - uint16_t subVal; + uint16_t subVal = 0; if(event->subscribe.cur_notify) { - subVal = 1; - }else if(event->subscribe.cur_indicate) { - subVal = 2; - }else { - subVal = 0; + subVal |= BLE_GATT_CHR_F_NOTIFY; + } + if(event->subscribe.cur_indicate) { + subVal |= BLE_GATT_CHR_F_INDICATE; } m_semaphoreConfEvt.give(subVal == 2 ? 0 : NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal); - auto it = m_subscribedMap.find(event->subscribe.conn_handle); - if(it == m_subscribedMap.cend()) { - m_subscribedMap.insert(std::pair(event->subscribe.conn_handle, subVal)); + NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); + if(p2902 == nullptr){ + ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s", getUUID().toString().c_str()); + return; + } + + p2902->setNotifications((subVal & BLE_GATT_CHR_F_NOTIFY) != 0); + p2902->setIndications((subVal & BLE_GATT_CHR_F_INDICATE) != 0); + p2902->m_pCallbacks->onWrite(p2902); + + + auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle); + if(it == p2902->m_subscribedMap.cend()) { + p2902->m_subscribedMap.insert(std::pair(event->subscribe.conn_handle, subVal)); return; } if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { - m_subscribedMap.erase(event->subscribe.conn_handle); + p2902->m_subscribedMap.erase(event->subscribe.conn_handle); return; } @@ -584,15 +593,16 @@ void NimBLECharacteristic::notify(bool is_notification) { os_mbuf *om; size_t length = m_value.getValue().length(); uint8_t* data = (uint8_t*)m_value.getValue().data(); + NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); - for (auto it = m_subscribedMap.cbegin(); it != m_subscribedMap.cend(); ++it) { + for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); if(_mtu == 0) { NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); - m_subscribedMap.erase((*it).first); - it = m_subscribedMap.cbegin(); - if(it == m_subscribedMap.cend()) { + p2902->m_subscribedMap.erase((*it).first); + it = p2902->m_subscribedMap.cbegin(); + if(it == p2902->m_subscribedMap.cend()) { return; } continue; @@ -609,13 +619,13 @@ void NimBLECharacteristic::notify(bool is_notification) { continue; } - if((*it).second == 2 && is_notification) { + if(is_notification && (!((*it).second & BLE_GATT_CHR_F_NOTIFY))) { NIMBLE_LOGW(LOG_TAG, "Sending notification to client subscribed to indications, sending indication instead"); is_notification = false; } - if((*it).second == 1 && !is_notification) { + if(!is_notification && (!((*it).second & BLE_GATT_CHR_F_INDICATE))) { NIMBLE_LOGW(LOG_TAG, "Sending indication to client subscribed to notifications, sending notifications instead"); is_notification = true; diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index dac1ac25..b683b623 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -20,6 +20,7 @@ #include "NimBLEUUID.h" #include "NimBLEValue.h" #include "NimBLEDescriptor.h" +#include "NimBLE2902.h" #include "FreeRTOS.h" #include "host/ble_hs.h" @@ -115,7 +116,6 @@ class NimBLECharacteristic { NimBLEValue m_value; uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; bool m_writeEvt = false; - std::map m_subscribedMap; NimBLEService* getService(); uint8_t getProperties(); diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index 87d0d341..1bc9a720 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -20,10 +20,11 @@ #include -static const char* LOG_TAG = "NimBLEDescriptor"; - #define NULL_HANDLE (0xffff) +static const char* LOG_TAG = "NimBLEDescriptor"; +static NimBLEDescriptorCallbacks defaultCallbacks; + /** * @brief NimBLEDescriptor constructor. @@ -40,7 +41,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len) { m_value.attr_max_len = max_len; // Maximum length of the data. m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCharacteristic = nullptr; // No initial characteristic. - m_pCallbacks = nullptr; // No initial callback. + m_pCallbacks = &defaultCallbacks; // No initial callback. m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. } // NimBLEDescriptor @@ -209,8 +210,12 @@ void NimBLEDescriptor::handleGATTServerEvent( * @brief Set the callback handlers for this descriptor. * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. */ -void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallback) { - m_pCallbacks = pCallback; +void NimBLEDescriptor::setCallbacks(NimBLEDescriptorCallbacks* pCallbacks) { + if (pCallbacks != nullptr){ + m_pCallbacks = pCallbacks; + } else { + m_pCallbacks = &defaultCallbacks; + } } // setCallbacks diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 4d961ea1..802f6a55 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -30,8 +30,6 @@ static const char* LOG_TAG = "NimBLEServer"; * the BLEDevice class. */ NimBLEServer::NimBLEServer() { -// m_appId = ESP_GATT_IF_NONE; -// m_gatts_if = ESP_GATT_IF_NONE; // m_connectedCount = 0; m_connId = BLE_HS_CONN_HANDLE_NONE; m_svcChgChrHdl = 0xffff; @@ -39,12 +37,6 @@ NimBLEServer::NimBLEServer() { m_gattsStarted = false; } // BLEServer -/* -void BLEServer::createApp(uint16_t appId) { - m_appId = appId; - registerApp(appId); -} // createApp -*/ /** * @brief Create a %BLE Service. @@ -54,7 +46,6 @@ void BLEServer::createApp(uint16_t appId) { * @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)); } @@ -70,10 +61,8 @@ NimBLEService* NimBLEServer::createService(const char* uuid) { * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service. * @return A reference to the new service object. */ - NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, uint8_t inst_id) { NIMBLE_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str()); - //m_semaphoreCreateEvt.take("createService"); // Check that a service with the supplied UUID does not already exist. if (m_serviceMap.getByUUID(uuid) != nullptr) { @@ -84,9 +73,6 @@ NimBLEService* NimBLEServer::createService(NimBLEUUID uuid, uint32_t numHandles, NimBLEService* pService = new NimBLEService(uuid, numHandles, this); pService->m_instId = inst_id; m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server. -// pService->executeCreate(this); // Perform the API calls to actually create the service. - -// m_semaphoreCreateEvt.wait("createService"); NIMBLE_LOGD(LOG_TAG, "<< createService"); return pService; @@ -134,8 +120,8 @@ uint16_t NimBLEServer::getConnId() { /** - * @brief Start the GATT server, required to be called after setup of all - * services and characteristics / descriptors for the NimBLE host to register them + * @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) { @@ -145,7 +131,8 @@ void NimBLEServer::start() { int rc = ble_gatts_start(); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "ble_gatts_start; rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); abort(); } @@ -154,14 +141,51 @@ void NimBLEServer::start() { ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; - //int ble_gatts_find_chr(const ble_uuid_t * svc_uuid, const ble_uuid_t * chr_uuid, uint16_t * out_def_handle, uint16_t * out_val_handle) 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)); + 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); + + uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); + NimBLEService* pService = m_serviceMap.getFirst(); + + for(int i = 0; i < numSvcs; i++) { + uint8_t numChrs = pService->m_characteristicMap.getSize(); + uint16_t chrHdl = 0xffff; + + NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); + + if(pChr != nullptr) { + for( int d = 0; d < numChrs; d++) { + rc = ble_gatts_find_chr(&pService->getUUID().getNative()->u, + &pChr->getUUID().getNative()->u, NULL, &chrHdl); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, + NimBLEUtils::returnCodeToString(rc)); + abort(); + } + + pChr->setHandle(chrHdl); + + if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) || + (pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { + + if(nullptr == pChr->getDescriptorByUUID("2902")) { + pChr->addDescriptor(new NimBLE2902()); + } + m_notifyChrMap.insert(std::pair + (chrHdl, pChr)); + } + pChr = pService->m_characteristicMap.getNext(); + } + } + pService = m_serviceMap.getNext(); + } + m_gattsStarted = true; } @@ -177,7 +201,8 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) { 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_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, + NimBLEUtils::returnCodeToString(rc)); } return rc; @@ -244,31 +269,11 @@ uint32_t NimBLEServer::getConnectedCount() { "val_handle=%d\n", event->subscribe.cur_notify, event->subscribe.attr_handle); - NimBLECharacteristic* pChr = server->getChrByHandle(event->subscribe.attr_handle); - if(pChr != nullptr) { - pChr->setSubscribe(event); + auto it = server->m_notifyChrMap.find(event->subscribe.attr_handle); + if(it != server->m_notifyChrMap.cend()) { + (*it).second->setSubscribe(event); } - /* - uint8_t numSvcs = server->m_serviceMap.getRegisteredServiceCount(); - NimBLEService* pService = server->m_serviceMap.getFirst(); - - for(int i = 0; i < numSvcs; i++) { - uint8_t numChrs = pService->m_characteristicMap.getSize(); - NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); - - for( int d = 0; d < numChrs; d++) { - if(pChr->m_handle == event->subscribe.attr_handle) { - pChr->setSubscribe(event); - return 0; - } - pChr = pService->m_characteristicMap.getNext(); - } - - pService = server->m_serviceMap.getNext(); - } - - NIMBLE_LOGE(LOG_TAG, "Subscribe handle not found"); - */ + break; } // BLE_GAP_EVENT_SUBSCRIBE @@ -282,32 +287,12 @@ uint32_t NimBLEServer::getConnectedCount() { case BLE_GAP_EVENT_NOTIFY_TX: { if(event->notify_tx.indication && event->notify_tx.status != 0) { - NimBLECharacteristic* pChr = server->getChrByHandle(event->notify_tx.attr_handle); - if(pChr != nullptr) { - pChr->m_semaphoreConfEvt.give(event->notify_tx.status); - } - /* - uint8_t numSvcs = server->m_serviceMap.getRegisteredServiceCount(); - NimBLEService* pService = server->m_serviceMap.getFirst(); - - for(int i = 0; i < numSvcs; i++) { - uint8_t numChrs = pService->m_characteristicMap.getSize(); - NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); - - for( int d = 0; d < numChrs; d++) { - if(pChr->m_handle == event->notify_tx.attr_handle) { - pChr->m_semaphoreConfEvt.give(event->notify_tx.status); - return 0; - } - pChr = pService->m_characteristicMap.getNext(); - } - - pService = server->m_serviceMap.getNext(); + auto it = server->m_notifyChrMap.find(event->notify_tx.attr_handle); + if(it != server->m_notifyChrMap.cend()) { + (*it).second->m_semaphoreConfEvt.give(event->notify_tx.status); } - - NIMBLE_LOGE(LOG_TAG, "Subscribe handle not found"); - */ } + break; } // BLE_GAP_EVENT_NOTIFY_TX @@ -365,31 +350,6 @@ void NimBLEServer::stopAdvertising() { NIMBLE_LOGD(LOG_TAG, "<< stopAdvertising"); } // startAdvertising - -NimBLECharacteristic* NimBLEServer::getChrByHandle(uint16_t handle) { - if(handle == m_svcChgChrHdl) { - return nullptr; - } - uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); - NimBLEService* pService = m_serviceMap.getFirst(); - - for(int i = 0; i < numSvcs; i++) { - uint8_t numChrs = pService->m_characteristicMap.getSize(); - NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); - - for( int d = 0; d < numChrs; d++) { - if(pChr->m_handle == handle) { - return pChr; - } - pChr = pService->m_characteristicMap.getNext(); - } - - pService = m_serviceMap.getNext(); - } - - NIMBLE_LOGE(LOG_TAG, "Characteristic by handle not found"); - return nullptr; -} /** * Allow to connect GATT server to peer device diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 04b57d02..95f8db80 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -80,9 +80,7 @@ class NimBLEServer { NimBLEService* getServiceByUUID(const char* uuid); NimBLEService* getServiceByUUID(NimBLEUUID uuid); int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); - NimBLECharacteristic* getChrByHandle(uint16_t handle); // bool connect(BLEAddress address); -// uint16_t m_appId; // void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); /* multi connection support */ @@ -101,24 +99,19 @@ class NimBLEServer { friend class NimBLECharacteristic; friend class NimBLEDevice; friend class NimBLEAdvertising; - //esp_ble_adv_data_t m_adv_data; + // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint16_t m_svcChgChrHdl; bool m_gattsStarted; + std::map m_connectedServersMap; + std::map m_notifyChrMap; - //FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); - //FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - //FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); NimBLEServiceMap m_serviceMap; NimBLEServerCallbacks* m_pServerCallbacks; - //void createApp(uint16_t appId); - //uint16_t getGattsIf(); - //void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); static int handleGapEvent(struct ble_gap_event *event, void *arg); - //void registerApp(uint16_t); }; // NimBLEServer From c5493190fcefbae252564d4774741337818ec8e6 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 12 Mar 2020 22:16:03 -0600 Subject: [PATCH 18/62] Descriptors now fully implemented. --- src/NimBLECharacteristic.cpp | 8 ++----- src/NimBLECharacteristic.h | 2 ++ src/NimBLECharacteristicMap.cpp | 2 +- src/NimBLEDescriptor.cpp | 40 +++++++++++++++++++++++++++++++- src/NimBLEDescriptor.h | 5 ++++ src/NimBLEDescriptorMap.cpp | 9 ++++++++ src/NimBLEService.cpp | 41 +++++++++++++++++++++++++++++++-- 7 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 493e5a2f..0424df63 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -211,9 +211,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han if(ble_uuid_cmp(uuid, &pCharacteristic->getUUID().getNative()->u) == 0){ switch(ctxt->op) { case BLE_GATT_ACCESS_OP_READ_CHR: { - //if (pCharacteristic->m_pCallbacks != nullptr) { - pCharacteristic->m_pCallbacks->onRead(pCharacteristic); - //} + pCharacteristic->m_pCallbacks->onRead(pCharacteristic); rc = os_mbuf_append(ctxt->om, pCharacteristic->getData(), pCharacteristic->m_value.getLength()); return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } @@ -224,9 +222,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han } pCharacteristic->setValue(ctxt->om->om_data, ctxt->om->om_len); - //if (pCharacteristic->m_pCallbacks != nullptr) { - pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); - //} + pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); return 0; } default: diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index b683b623..e447d452 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -47,6 +47,8 @@ class NimBLEDescriptorMap { // void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); NimBLEDescriptor* getFirst(); NimBLEDescriptor* getNext(); + uint8_t getSize(); + private: std::map m_uuidMap; // std::map m_handleMap; diff --git a/src/NimBLECharacteristicMap.cpp b/src/NimBLECharacteristicMap.cpp index 285f0015..1c0f10b7 100644 --- a/src/NimBLECharacteristicMap.cpp +++ b/src/NimBLECharacteristicMap.cpp @@ -56,7 +56,7 @@ NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(NimBLEUUID uuid) { */ uint8_t NimBLECharacteristicMap::getSize() { return (uint8_t)m_uuidMap.size(); -} // getFirst +} // getSize /** * @brief Get the first characteristic in the map. diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index 1bc9a720..66b67920 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -43,7 +43,7 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len) { m_pCharacteristic = nullptr; // No initial characteristic. m_pCallbacks = &defaultCallbacks; // No initial callback. - m_value.attr_value = (uint8_t*) malloc(max_len); // Allocate storage for the value. + m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. } // NimBLEDescriptor @@ -124,6 +124,44 @@ uint8_t* NimBLEDescriptor::getValue() { } // getValue +int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) +{ + const ble_uuid_t *uuid; + int rc; + NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; + + NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), + ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); + + uuid = ctxt->chr->uuid; + if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ + switch(ctxt->op) { + case BLE_GATT_ACCESS_OP_READ_CHR: { + pDescriptor->m_pCallbacks->onRead(pDescriptor); + rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength()); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; + } + + case BLE_GATT_ACCESS_OP_WRITE_CHR: { + if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { + return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; + } + + pDescriptor->setValue(ctxt->om->om_data, ctxt->om->om_len); + pDescriptor->m_pCallbacks->onWrite(pDescriptor); + return 0; + } + default: + break; + } + } + + return BLE_ATT_ERR_UNLIKELY; +} + + /** * @brief Handle GATT server events for the descripttor. * @param [in] event diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 483133dd..83bf470f 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -63,6 +63,8 @@ class NimBLEDescriptor { private: friend class NimBLEDescriptorMap; friend class NimBLECharacteristic; + friend class NimBLEService; + NimBLEUUID m_uuid; uint16_t m_handle; NimBLEDescriptorCallbacks* m_pCallbacks; @@ -72,6 +74,9 @@ class NimBLEDescriptor { attr_value_t m_value; // void executeCreate(BLECharacteristic* pCharacteristic); + static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + void setHandle(uint16_t handle); }; // BLEDescriptor diff --git a/src/NimBLEDescriptorMap.cpp b/src/NimBLEDescriptorMap.cpp index 9f3fdc89..3e73a9c1 100644 --- a/src/NimBLEDescriptorMap.cpp +++ b/src/NimBLEDescriptorMap.cpp @@ -88,6 +88,15 @@ void NimBLEDescriptorMap::setByHandle(uint16_t handle, NimBLEDescriptor* pDescri } // setByHandle */ + +/** + * @brief Get the number of descriptors in the map. + */ +uint8_t NimBLEDescriptorMap::getSize() { + return (uint8_t)m_uuidMap.size(); +} // getSize + + /** * @brief Return a string representation of the descriptor map. * @return A string representation of the descriptor map. diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 93283943..19780955 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -146,6 +146,7 @@ bool NimBLEService::start() { // of the second service to 0 to indicate the end of the array. ble_gatt_svc_def* svc = new ble_gatt_svc_def[2]; ble_gatt_chr_def* pChr_a = nullptr; + ble_gatt_dsc_def* pDsc_a = nullptr; svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; svc[0].uuid = &m_uuid.getNative()->u; @@ -154,7 +155,7 @@ bool NimBLEService::start() { NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str()); - if(numChrs < 1){ + if(!numChrs){ svc[0].characteristics = NULL; }else{ // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end @@ -164,10 +165,46 @@ bool NimBLEService::start() { NimBLECharacteristic* pCharacteristic = m_characteristicMap.getFirst(); for(uint8_t i=0; i < numChrs; i++) { + uint8_t numDscs = pCharacteristic->m_descriptorMap.getSize(); + if(numDscs) { + // skip 2902 as it's automatically created by NimBLE + // if Indicate or Notify flags are set + if((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) || + (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) { + numDscs--; + } + } + + if(!numDscs){ + pChr_a[i].descriptors = NULL; + } else { + // Must have last descriptor uuid = 0 so we have to create 1 extra + NIMBLE_LOGE(LOG_TAG, "Adding %d descriptors", numDscs); + pDsc_a = new ble_gatt_dsc_def[numDscs+1]; + NimBLEDescriptor* pDescriptor = pCharacteristic->m_descriptorMap.getFirst(); + for(uint8_t d=0; d < numDscs;) { + // skip 2902 + if(pDescriptor->m_uuid.equals(NimBLEUUID((uint16_t)0x2902))) { + NIMBLE_LOGE(LOG_TAG, "Skipped 0x2902"); + pDescriptor = pCharacteristic->m_descriptorMap.getNext(); + continue; + } + pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; + pDsc_a[d].att_flags = pDescriptor->m_permissions; + pDsc_a[d].min_key_size = 0; + pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; + pDsc_a[d].arg = pDescriptor; + pDescriptor = pCharacteristic->m_descriptorMap.getNext(); + d++; + } + + pDsc_a[numDscs].uuid = NULL; + pChr_a[i].descriptors = pDsc_a; + } + pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u; pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent; pChr_a[i].arg = pCharacteristic; - pChr_a[i].descriptors = NULL; pChr_a[i].flags = pCharacteristic->m_properties; pChr_a[i].min_key_size = 0; pChr_a[i].val_handle = &pCharacteristic->m_handle; From a9dc0719d3628935716a35d7bf4029c47d30dc23 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 13 Mar 2020 15:43:04 -0600 Subject: [PATCH 19/62] Add 2904 Descriptor. Added defines for descriptor backward compatibility. --- src/NimBLE2904.cpp | 81 ++++++++++++++++++++++++++++++++++++ src/NimBLE2904.h | 81 ++++++++++++++++++++++++++++++++++++ src/NimBLECharacteristic.cpp | 1 + src/NimBLECharacteristic.h | 1 - src/NimBLEDevice.h | 4 ++ src/NimBLEServer.cpp | 1 + 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 src/NimBLE2904.cpp create mode 100644 src/NimBLE2904.h diff --git a/src/NimBLE2904.cpp b/src/NimBLE2904.cpp new file mode 100644 index 00000000..c57d4b30 --- /dev/null +++ b/src/NimBLE2904.cpp @@ -0,0 +1,81 @@ +/* + * NimBLE2904.cpp + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.cpp + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +/* + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLE2904.h" + + +NimBLE2904::NimBLE2904() : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904)) { + m_data.m_format = 0; + m_data.m_exponent = 0; + m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers + m_data.m_unit = 0; + m_data.m_description = 0; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // BLE2902 + + +/** + * @brief Set the description. + */ +void NimBLE2904::setDescription(uint16_t description) { + m_data.m_description = description; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} + + +/** + * @brief Set the exponent. + */ +void NimBLE2904::setExponent(int8_t exponent) { + m_data.m_exponent = exponent; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setExponent + + +/** + * @brief Set the format. + */ +void NimBLE2904::setFormat(uint8_t format) { + m_data.m_format = format; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setFormat + + +/** + * @brief Set the namespace. + */ +void NimBLE2904::setNamespace(uint8_t namespace_value) { + m_data.m_namespace = namespace_value; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setNamespace + + +/** + * @brief Set the units for this value. It should be one of the encoded values defined here: + * https://www.bluetooth.com/specifications/assigned-numbers/units + * @param [in] unit The type of units of this characteristic as defined by assigned numbers. + */ +void NimBLE2904::setUnit(uint16_t unit) { + m_data.m_unit = unit; + setValue((uint8_t*) &m_data, sizeof(m_data)); +} // setUnit + +#endif \ No newline at end of file diff --git a/src/NimBLE2904.h b/src/NimBLE2904.h new file mode 100644 index 00000000..d8e23e54 --- /dev/null +++ b/src/NimBLE2904.h @@ -0,0 +1,81 @@ +/* + * NimBLE2904.h + * + * Created: on March 13, 2020 + * Author H2zero + * + * Originally: + * + * BLE2904.h + * + * Created on: Dec 23, 2017 + * Author: kolban + */ + +#ifndef MAIN_NIMBLE2904_H_ +#define MAIN_NIMBLE2904_H_ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEDescriptor.h" + +struct BLE2904_Data { + uint8_t m_format; + int8_t m_exponent; + uint16_t m_unit; // See https://www.bluetooth.com/specifications/assigned-numbers/units + uint8_t m_namespace; + uint16_t m_description; + +} __attribute__((packed)); + +/** + * @brief Descriptor for Characteristic Presentation Format. + * + * This is a convenience descriptor for the Characteristic Presentation Format which has a UUID of 0x2904. + * + * See also: + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + */ +class NimBLE2904: public NimBLEDescriptor { +public: + NimBLE2904(); + static const uint8_t FORMAT_BOOLEAN = 1; + static const uint8_t FORMAT_UINT2 = 2; + static const uint8_t FORMAT_UINT4 = 3; + static const uint8_t FORMAT_UINT8 = 4; + static const uint8_t FORMAT_UINT12 = 5; + static const uint8_t FORMAT_UINT16 = 6; + static const uint8_t FORMAT_UINT24 = 7; + static const uint8_t FORMAT_UINT32 = 8; + static const uint8_t FORMAT_UINT48 = 9; + static const uint8_t FORMAT_UINT64 = 10; + static const uint8_t FORMAT_UINT128 = 11; + static const uint8_t FORMAT_SINT8 = 12; + static const uint8_t FORMAT_SINT12 = 13; + static const uint8_t FORMAT_SINT16 = 14; + static const uint8_t FORMAT_SINT24 = 15; + static const uint8_t FORMAT_SINT32 = 16; + static const uint8_t FORMAT_SINT48 = 17; + static const uint8_t FORMAT_SINT64 = 18; + static const uint8_t FORMAT_SINT128 = 19; + static const uint8_t FORMAT_FLOAT32 = 20; + static const uint8_t FORMAT_FLOAT64 = 21; + static const uint8_t FORMAT_SFLOAT16 = 22; + static const uint8_t FORMAT_SFLOAT32 = 23; + static const uint8_t FORMAT_IEEE20601 = 24; + static const uint8_t FORMAT_UTF8 = 25; + static const uint8_t FORMAT_UTF16 = 26; + static const uint8_t FORMAT_OPAQUE = 27; + + void setDescription(uint16_t); + void setExponent(int8_t exponent); + void setFormat(uint8_t format); + void setNamespace(uint8_t namespace_value); + void setUnit(uint16_t unit); + +private: + BLE2904_Data m_data; +}; // BLE2904 + +#endif /* CONFIG_BT_ENABLED */ +#endif /* MAIN_NIMBLE2904_H_ */ \ No newline at end of file diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 0424df63..4e8c4d96 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -13,6 +13,7 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLECharacteristic.h" +#include "NimBLE2902.h" #include "NimBLEDevice.h" #include "NimBLEUtils.h" #include "NimBLELog.h" diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index e447d452..2fe89822 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -20,7 +20,6 @@ #include "NimBLEUUID.h" #include "NimBLEValue.h" #include "NimBLEDescriptor.h" -#include "NimBLE2902.h" #include "FreeRTOS.h" #include "host/ble_hs.h" diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 3c673bf0..8ece70f5 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -51,6 +51,10 @@ #define BLEServerCallbacks NimBLEServerCallbacks #define BLECharacteristicCallbacks NimBLECharacteristicCallbacks #define BLEAdvertisementData NimBLEAdvertisementData +#define BLEDescriptor NimBLEDescriptor +#define BLE2902 NimBLE2902 +#define BLEDescriptorCallbacks NimBLEDescriptorCallbacks + /** * @brief BLE functions. diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 802f6a55..c7ad8e23 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -16,6 +16,7 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLEServer.h" +#include "NimBLE2902.h" #include "NimBLEUtils.h" #include "NimBLEDevice.h" #include "NimBLELog.h" From 764ea6e2ce93b05393433147536feb3cfe1dde19 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 14 Mar 2020 12:19:04 -0600 Subject: [PATCH 20/62] Implement update connection parameters from server. --- src/NimBLEServer.cpp | 66 +++++++++++++++++++++++++++----------------- src/NimBLEServer.h | 7 +++-- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index c7ad8e23..04870151 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -31,7 +31,6 @@ static const char* LOG_TAG = "NimBLEServer"; * the BLEDevice class. */ NimBLEServer::NimBLEServer() { -// m_connectedCount = 0; m_connId = BLE_HS_CONN_HANDLE_NONE; m_svcChgChrHdl = 0xffff; m_pServerCallbacks = nullptr; @@ -151,6 +150,7 @@ void NimBLEServer::start() { NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl); + // Build a map of characteristics with Notify / Indicate capabilities for event handling uint8_t numSvcs = m_serviceMap.getRegisteredServiceCount(); NimBLEService* pService = m_serviceMap.getFirst(); @@ -162,7 +162,8 @@ void NimBLEServer::start() { if(pChr != nullptr) { for( int d = 0; d < numChrs; d++) { - rc = ble_gatts_find_chr(&pService->getUUID().getNative()->u, + // Not needed as NimBLE sets the handle for us + /*rc = ble_gatts_find_chr(&pService->getUUID().getNative()->u, &pChr->getUUID().getNative()->u, NULL, &chrHdl); if(rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, @@ -171,7 +172,9 @@ void NimBLEServer::start() { } pChr->setHandle(chrHdl); - + */ + // if Notify / Indicate is enabled but we didn't create the descriptor + // we do it now. if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) || (pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { @@ -245,8 +248,11 @@ uint32_t NimBLEServer::getConnectedCount() { server->m_connId = event->connect.conn_handle; server->addPeerDevice((void*)server, false, server->m_connId); if (server->m_pServerCallbacks != nullptr) { + ble_gap_conn_desc desc; + int rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); server->m_pServerCallbacks->onConnect(server); - //m_pServerCallbacks->onConnect(this, param); + server->m_pServerCallbacks->onConnect(server, &desc); } } @@ -380,23 +386,23 @@ bool BLEServer::connect(BLEAddress address) { void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { - NIMBLE_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); - NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); - NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); +// NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect -/* -void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, esp_ble_gatts_cb_param_t* param) { - NIMBLE_LOGD("BLEServerCallbacks", ">> onConnect(): Default"); - NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); - NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); + +void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); +// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); +// NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect -*/ + void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { - NIMBLE_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default"); - NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); - NIMBLE_LOGD("BLEServerCallbacks", "<< onDisconnect()"); + NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); +// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); +// NIMBLE_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect @@ -445,16 +451,24 @@ void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) { /** * Update connection parameters can be called only after connection has been established */ - /* -void BLEServer::updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout) { - esp_ble_conn_update_params_t conn_params; - memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t)); - conn_params.latency = latency; - conn_params.max_int = maxInterval; // max_int = 0x20*1.25ms = 40ms - conn_params.min_int = minInterval; // min_int = 0x10*1.25ms = 20ms - conn_params.timeout = timeout; // timeout = 400*10ms = 4000ms - esp_ble_gap_update_conn_params(&conn_params); +void NimBLEServer::updateConnParams(ble_gap_conn_desc* desc, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime, uint16_t maxConnTime) +{ + 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 = minConnTime; // Minimum length of connection event in 0.625ms units + params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = ble_gap_update_params(desc->conn_handle, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } } -*/ #endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 95f8db80..222886a1 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -81,7 +81,10 @@ class NimBLEServer { NimBLEService* getServiceByUUID(NimBLEUUID uuid); int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); // bool connect(BLEAddress address); -// void updateConnParams(esp_bd_addr_t remote_bda, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout); + void updateConnParams(ble_gap_conn_desc* desc, + uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime=0, uint16_t maxConnTime=0); /* multi connection support */ std::map getPeerDevices(); @@ -129,7 +132,7 @@ class NimBLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the client connection. */ virtual void onConnect(NimBLEServer* pServer); - //virtual void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param); + virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc); /** * @brief Handle an existing client disconnection. * From 9ba5e2dad21ae229d7bfb40b40dc69d6bd4729a3 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 14 Mar 2020 12:48:02 -0600 Subject: [PATCH 21/62] Fixed Notify / Indicate values Fixed Characteristic sub map handles. --- src/NimBLE2902.h | 3 +++ src/NimBLECharacteristic.cpp | 12 ++++++------ src/NimBLEServer.cpp | 10 ++-------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/NimBLE2902.h b/src/NimBLE2902.h index 40e90bc4..7e4cd377 100644 --- a/src/NimBLE2902.h +++ b/src/NimBLE2902.h @@ -21,6 +21,9 @@ #include +#define NIMBLE_DESC_FLAG_NOTIFY 0x0001 +#define NIMBLE_DESC_FLAG_INDICATE 0x0002 + /** * @brief Descriptor for Client Characteristic Configuration. diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 4e8c4d96..2602891c 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -518,10 +518,10 @@ void BLECharacteristic::handleGATTServerEvent( void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { uint16_t subVal = 0; if(event->subscribe.cur_notify) { - subVal |= BLE_GATT_CHR_F_NOTIFY; + subVal |= NIMBLE_DESC_FLAG_NOTIFY; } if(event->subscribe.cur_indicate) { - subVal |= BLE_GATT_CHR_F_INDICATE; + subVal |= NIMBLE_DESC_FLAG_INDICATE; } m_semaphoreConfEvt.give(subVal == 2 ? 0 : NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); @@ -534,8 +534,8 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { return; } - p2902->setNotifications((subVal & BLE_GATT_CHR_F_NOTIFY) != 0); - p2902->setIndications((subVal & BLE_GATT_CHR_F_INDICATE) != 0); + p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY); + p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE); p2902->m_pCallbacks->onWrite(p2902); @@ -616,13 +616,13 @@ void NimBLECharacteristic::notify(bool is_notification) { continue; } - if(is_notification && (!((*it).second & BLE_GATT_CHR_F_NOTIFY))) { + if(is_notification && (!((*it).second & NIMBLE_DESC_FLAG_NOTIFY))) { NIMBLE_LOGW(LOG_TAG, "Sending notification to client subscribed to indications, sending indication instead"); is_notification = false; } - if(!is_notification && (!((*it).second & BLE_GATT_CHR_F_INDICATE))) { + if(!is_notification && (!((*it).second & NIMBLE_DESC_FLAG_INDICATE))) { NIMBLE_LOGW(LOG_TAG, "Sending indication to client subscribed to notifications, sending notifications instead"); is_notification = true; diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 04870151..3d6d090e 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -156,7 +156,7 @@ void NimBLEServer::start() { for(int i = 0; i < numSvcs; i++) { uint8_t numChrs = pService->m_characteristicMap.getSize(); - uint16_t chrHdl = 0xffff; + //uint16_t chrHdl = 0xffff; NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); @@ -182,7 +182,7 @@ void NimBLEServer::start() { pChr->addDescriptor(new NimBLE2902()); } m_notifyChrMap.insert(std::pair - (chrHdl, pChr)); + (pChr->getHandle() /*chrHdl*/, pChr)); } pChr = pService->m_characteristicMap.getNext(); } @@ -387,22 +387,16 @@ bool BLEServer::connect(BLEAddress address) { void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer) { NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); -// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); -// NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect void NimBLEServerCallbacks::onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { NIMBLE_LOGD("NimBLEServerCallbacks", "onConnect(): Default"); -// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); -// NIMBLE_LOGD("BLEServerCallbacks", "<< onConnect()"); } // onConnect void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); -// NIMBLE_LOGD("BLEServerCallbacks", "Device: %s", NimBLEDevice::toString().c_str()); -// NIMBLE_LOGD("BLEServerCallbacks", "<< onDisconnect()"); } // onDisconnect From 1aeb2fed80f64a430d26f1537b9f5811fe33a294 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 14 Mar 2020 22:45:40 -0600 Subject: [PATCH 22/62] Implemented server security. Code cleanup. Client security tweaks. --- src/FreeRTOS.cpp | 66 +++-- src/FreeRTOS.h | 2 + src/NimBLEAdvertising.cpp | 73 +---- src/NimBLEAdvertising.h | 4 +- src/NimBLECharacteristic.cpp | 439 ++++------------------------- src/NimBLECharacteristic.h | 22 +- src/NimBLECharacteristicMap.cpp | 15 - src/NimBLEClient.cpp | 26 +- src/NimBLEClient.h | 2 +- src/NimBLEDescriptor.cpp | 118 -------- src/NimBLEDescriptor.h | 7 - src/NimBLEDescriptorMap.cpp | 18 -- src/NimBLEDevice.cpp | 2 +- src/NimBLEDevice.h | 1 + src/NimBLELog.h | 1 + src/NimBLERemoteCharacteristic.cpp | 11 - src/NimBLERemoteDescriptor.cpp | 9 - src/NimBLERemoteService.cpp | 10 - src/NimBLEScan.cpp | 10 - src/NimBLESecurity.h | 2 +- src/NimBLEServer.cpp | 98 ++++++- src/NimBLEServer.h | 7 +- src/NimBLEService.cpp | 187 ------------ src/NimBLEService.h | 13 - src/NimBLEServiceMap.cpp | 11 - src/NimBLEUUID.cpp | 47 --- src/NimBLEUUID.h | 1 - src/NimBLEUtils.cpp | 12 - src/NimBLEValue.cpp | 10 +- 29 files changed, 244 insertions(+), 980 deletions(-) diff --git a/src/FreeRTOS.cpp b/src/FreeRTOS.cpp index 47dc4932..6435590a 100644 --- a/src/FreeRTOS.cpp +++ b/src/FreeRTOS.cpp @@ -4,20 +4,17 @@ * Created on: Feb 24, 2017 * Author: kolban */ +#include "sdkconfig.h" +#include "FreeRTOS.h" +#include "NimBLELog.h" + #include // Include the base FreeRTOS definitions #include // Include the task definitions #include // Include the semaphore definitions #include -#include -#include "FreeRTOS.h" -#include "sdkconfig.h" -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" + static const char* LOG_TAG = "FreeRTOS"; -#endif + /** * Sleep for the specified number of milliseconds. @@ -65,7 +62,7 @@ uint32_t FreeRTOS::getTimeSinceStart() { * @return The value associated with the semaphore. */ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { - ESP_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); @@ -79,11 +76,44 @@ uint32_t FreeRTOS::Semaphore::wait(std::string owner) { xSemaphoreGive(m_semaphore); } - ESP_LOGD(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str()); return m_value; } // wait +/** + * @brief Wait for a semaphore to be released in a given period of time by trying to take it and + * then releasing it again. The value associated with the semaphore can be taken by value() call after return + * @param [in] owner A debug tag. + * @param [in] timeoutMs timeout to wait in ms. + * @return True if we took the semaphore within timeframe. + */ +bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) { + NIMBLE_LOGD(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str()); + + if (m_usePthreads && timeoutMs != portMAX_DELAY) { + assert(false); // We apparently don't have a timed wait for pthreads. + } + + auto ret = pdTRUE; + + if (m_usePthreads) { + pthread_mutex_lock(&m_pthread_mutex); + } else { + ret = xSemaphoreTake(m_semaphore, timeoutMs); + } + + if (m_usePthreads) { + pthread_mutex_unlock(&m_pthread_mutex); + } else { + xSemaphoreGive(m_semaphore); + } + + NIMBLE_LOGD(LOG_TAG, "<< wait: Semaphore %s released: %d", toString().c_str(), ret); + return ret; +} // wait + + FreeRTOS::Semaphore::Semaphore(std::string name) { m_usePthreads = false; // Are we using pThreads or FreeRTOS? if (m_usePthreads) { @@ -114,7 +144,7 @@ FreeRTOS::Semaphore::~Semaphore() { * The Semaphore is given. */ void FreeRTOS::Semaphore::give() { - ESP_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str()); m_owner = std::string(""); if (m_usePthreads) { @@ -160,7 +190,7 @@ void FreeRTOS::Semaphore::giveFromISR() { * @return True if we took the semaphore. */ bool FreeRTOS::Semaphore::take(std::string owner) { - ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); bool rc = false; if (m_usePthreads) { pthread_mutex_lock(&m_pthread_mutex); @@ -169,9 +199,9 @@ bool FreeRTOS::Semaphore::take(std::string owner) { } m_owner = owner; if (rc) { - ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } else { - ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); } return rc; } // Semaphore::take @@ -185,7 +215,7 @@ bool FreeRTOS::Semaphore::take(std::string owner) { * @return True if we took the semaphore. */ bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { - ESP_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); + NIMBLE_LOGD(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str()); bool rc = false; if (m_usePthreads) { assert(false); // We apparently don't have a timed wait for pthreads. @@ -194,9 +224,9 @@ bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) { } m_owner = owner; if (rc) { - ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str()); } else { - ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); + NIMBLE_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str()); } return rc; } // Semaphore::take diff --git a/src/FreeRTOS.h b/src/FreeRTOS.h index 6114981e..efec5be7 100644 --- a/src/FreeRTOS.h +++ b/src/FreeRTOS.h @@ -40,7 +40,9 @@ class FreeRTOS { bool take(std::string owner = ""); bool take(uint32_t timeoutMs, std::string owner = ""); std::string toString(); + bool timedWait(std::string owner = "", uint32_t timeoutMs = portMAX_DELAY); uint32_t wait(std::string owner = ""); + uint32_t value(){ return m_value; }; private: SemaphoreHandle_t m_semaphore; diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 4c963e44..b428eacf 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -52,26 +52,7 @@ NimBLEAdvertising::NimBLEAdvertising() { m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; m_advParams.itvl_min = 0x20; m_advParams.itvl_max = 0x40; - -/* m_advData.set_scan_rsp = false; - m_advData.manufacturer_len = 0; - m_advData.p_manufacturer_data = nullptr; - m_advData.service_data_len = 0; - m_advData.p_service_data = nullptr; - m_advData.service_uuid_len = 0; - m_advData.p_service_uuid = nullptr; - - m_advParams.adv_int_min = 0x20; - m_advParams.adv_int_max = 0x40; - m_advParams.adv_type = ADV_TYPE_IND; - m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - m_advParams.channel_map = ADV_CHNL_ALL; - m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; - m_advParams.peer_addr_type = BLE_ADDR_TYPE_PUBLIC; - - m_customAdvData = false; // No custom advertising data - m_customScanResponseData = false; // No custom scan response data - */ + } // NimBLEAdvertising @@ -131,31 +112,29 @@ void NimBLEAdvertising::setScanResponse(bool set) { * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list. * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list. */ - /* -void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { - ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); +void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) { + NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly); if (!scanRequestWhitelistOnly && !connectWhitelistOnly) { - m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY; - ESP_LOGD(LOG_TAG, "<< setScanFilter"); + m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && !connectWhitelistOnly) { - m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY; - ESP_LOGD(LOG_TAG, "<< setScanFilter"); + m_advParams.filter_policy = BLE_HCI_ADV_FILT_SCAN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (!scanRequestWhitelistOnly && connectWhitelistOnly) { - m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST; - ESP_LOGD(LOG_TAG, "<< setScanFilter"); + m_advParams.filter_policy = BLE_HCI_ADV_FILT_CONN; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } if (scanRequestWhitelistOnly && connectWhitelistOnly) { - m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST; - ESP_LOGD(LOG_TAG, "<< setScanFilter"); + m_advParams.filter_policy = BLE_HCI_ADV_FILT_BOTH; + NIMBLE_LOGD(LOG_TAG, "<< setScanFilter"); return; } } // setScanFilter -*/ /** * @brief Set the advertisement data that is to be published in a regular advertisement. @@ -610,35 +589,5 @@ void NimBLEAdvertisementData::setShortName(std::string name) { std::string NimBLEAdvertisementData::getPayload() { return m_payload; } // getPayload -/* -void NimBLEAdvertising::handleGAPEvent( - esp_gap_ble_cb_event_t event, - esp_ble_gap_cb_param_t* param) { - - ESP_LOGD(LOG_TAG, "handleGAPEvent [event no: %d]", (int)event); - - switch(event) { - case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: { - // m_semaphoreSetAdv.give(); - break; - } - case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: { - // m_semaphoreSetAdv.give(); - break; - } - case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: { - // m_semaphoreSetAdv.give(); - break; - } - case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: { - ESP_LOGI(LOG_TAG, "STOP advertising"); - start(); - break; - } - default: - break; - } -} -*/ #endif /* CONFIG_BT_ENABLED */ \ No newline at end of file diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 14c05fd9..617950f8 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -75,11 +75,10 @@ class NimBLEAdvertising { void setMaxInterval(uint16_t maxinterval); void setMinInterval(uint16_t mininterval); void setAdvertisementData(NimBLEAdvertisementData& advertisementData); -// void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); + void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly); void setScanResponseData(NimBLEAdvertisementData& advertisementData); void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM); - //void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t* param); // void setMinPreferred(uint16_t); // void setMaxPreferred(uint16_t); void setScanResponse(bool); @@ -91,7 +90,6 @@ class NimBLEAdvertising { std::vector m_serviceUUIDs; bool m_customAdvData = false; // Are we using custom advertising data? bool m_customScanResponseData = false; // Are we using custom scan response data? -// FreeRTOS::Semaphore m_semaphoreSetAdv = FreeRTOS::Semaphore("startAdvert"); bool m_scanResp = true; bool m_advSvcsSet = false; diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 2602891c..394724ca 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -31,7 +31,7 @@ static const char* LOG_TAG = "NimBLECharacteristic"; * @param [in] uuid - UUID (const char*) for the characteristic. * @param [in] properties - Properties for the characteristic. */ -NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties, NimBLEService* pService) +NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint16_t properties, NimBLEService* pService) : NimBLECharacteristic(NimBLEUUID(uuid), properties, pService) { } @@ -40,19 +40,20 @@ NimBLECharacteristic::NimBLECharacteristic(const char* uuid, uint32_t properties * @param [in] uuid - UUID for the characteristic. * @param [in] properties - Properties for the characteristic. */ -NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties, NimBLEService* pService) { +NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties, NimBLEService* pService) { m_uuid = uuid; m_handle = NULL_HANDLE; - m_properties = (uint16_t) 0; + m_properties = properties; m_pCallbacks = &defaultCallback; m_pService = pService; - +// Backward Compatibility - to be removed setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); setReadProperty((properties & PROPERTY_READ) != 0); setWriteProperty((properties & PROPERTY_WRITE) != 0); setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); setIndicateProperty((properties & PROPERTY_INDICATE) != 0); setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); +/////////////////////////////////////////// } // NimBLECharacteristic /** @@ -75,54 +76,6 @@ void BLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { } // addDescriptor -/** - * @brief Register a new characteristic with the ESP runtime. - * @param [in] pService The service with which to associate this characteristic. - */ - /* -void NimBLECharacteristic::executeCreate(NimBLEService* pService) { - NIMBLE_LOGD(LOG_TAG, ">> executeCreate()"); - - if (m_handle != NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "Characteristic already has a handle."); - return; - } - - m_pService = pService; // Save the service to which this characteristic belongs. - - ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s", - getUUID().toString().c_str(), - m_pService->toString().c_str()); - - esp_attr_control_t control; - control.auto_rsp = ESP_GATT_RSP_BY_APP; - - m_semaphoreCreateEvt.take("executeCreate"); - esp_err_t errRc = ::esp_ble_gatts_add_char( - m_pService->getHandle(), - getUUID().getNative(), - static_cast(m_permissions), - getProperties(), - nullptr, - &control); // Whether to auto respond or not. - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreCreateEvt.wait("executeCreate"); - - BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); - while (pDescriptor != nullptr) { - pDescriptor->executeCreate(this); - pDescriptor = m_descriptorMap.getNext(); - } // End while - - NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); -} // executeCreate -*/ - - /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. @@ -233,282 +186,7 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han return BLE_ATT_ERR_UNLIKELY; } - -/** - * Handle a GATT server event. - */ -/* -void BLECharacteristic::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { - ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str()); - - switch(event) { - // Events handled: - // - // ESP_GATTS_ADD_CHAR_EVT - // ESP_GATTS_CONF_EVT - // ESP_GATTS_CONNECT_EVT - // ESP_GATTS_DISCONNECT_EVT - // ESP_GATTS_EXEC_WRITE_EVT - // ESP_GATTS_READ_EVT - // ESP_GATTS_WRITE_EVT - - // - // ESP_GATTS_EXEC_WRITE_EVT - // When we receive this event it is an indication that a previous write long needs to be committed. - // - // exec_write: - // - uint16_t conn_id - // - uint32_t trans_id - // - esp_bd_addr_t bda - // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL - // - case ESP_GATTS_EXEC_WRITE_EVT: { - if(m_writeEvt){ - m_writeEvt = false; - if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { - m_value.commit(); - if (m_pCallbacks != nullptr) { - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. - } - } else { - m_value.cancel(); - } - // ??? - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, - param->write.conn_id, - param->write.trans_id, ESP_GATT_OK, nullptr); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } - break; - } // ESP_GATTS_EXEC_WRITE_EVT - - - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - case ESP_GATTS_ADD_CHAR_EVT: { - if (getHandle() == param->add_char.attr_handle) { - // we have created characteristic, now we can create descriptors - // BLEDescriptor* pDescriptor = m_descriptorMap.getFirst(); - // while (pDescriptor != nullptr) { - // pDescriptor->executeCreate(this); - // pDescriptor = m_descriptorMap.getNext(); - // } // End while - m_semaphoreCreateEvt.give(); - } - break; - } // ESP_GATTS_ADD_CHAR_EVT - - - // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived. - // - // write: - // - uint16_t conn_id - // - uint16_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool need_rsp - // - bool is_prep - // - uint16_t len - // - uint8_t *value - // - case ESP_GATTS_WRITE_EVT: { -// We check if this write request is for us by comparing the handles in the event. If it is for us -// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need -// to send a response. If we do, then we formulate a response and send it. - if (param->write.handle == m_handle) { - if (param->write.is_prep) { - m_value.addPart(param->write.value, param->write.len); - m_writeEvt = true; - } else { - setValue(param->write.value, param->write.len); - if (m_pCallbacks != nullptr && param->write.is_prep != true) { - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. - } - } - - ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s", - getHandle(), getUUID().toString().c_str()); - - char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len); - ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData); - free(pHexData); - - if (param->write.need_rsp) { - esp_gatt_rsp_t rsp; - - rsp.attr_value.len = param->write.len; - rsp.attr_value.handle = m_handle; - rsp.attr_value.offset = param->write.offset; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(rsp.attr_value.value, param->write.value, param->write.len); - - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, - param->write.conn_id, - param->write.trans_id, ESP_GATT_OK, &rsp); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } // Response needed - - } // Match on handles. - break; - } // ESP_GATTS_WRITE_EVT - - - // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived. - // - // read: - // - uint16_t conn_id - // - uint32_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool is_long - // - bool need_rsp - // - case ESP_GATTS_READ_EVT: { - if (param->read.handle == m_handle) { - - - -// Here's an interesting thing. The read request has the option of saying whether we need a response -// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like -// a very strange read. -// -// We have to handle the case where the data we wish to send back to the client is greater than the maximum -// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes. -// The apparent algorithm is as follows: -// -// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes. -// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than -// 22 bytes, then we "just" send it and thats the end of the story. -// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request. -// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request. -// Because of follow on request processing, we need to maintain an offset of how much data we have already sent -// so that when a follow on request arrives, we know where to start in the data to send the next sequence. -// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response. -// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length. -// -// The following code has deliberately not been factored to make it fewer statements because this would cloud the -// the logic flow comprehension. -// - - // get mtu for peer device that we are sending read request to - uint16_t maxOffset = getService()->getServer()->getPeerMTU(param->read.conn_id) - 1; - ESP_LOGD(LOG_TAG, "mtu value: %d", maxOffset); - if (param->read.need_rsp) { - ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)"); - esp_gatt_rsp_t rsp; - - if (param->read.is_long) { - std::string value = m_value.getValue(); - - if (value.length() - m_value.getReadOffset() < maxOffset) { - // This is the last in the chain - rsp.attr_value.len = value.length() - m_value.getReadOffset(); - rsp.attr_value.offset = m_value.getReadOffset(); - memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); - m_value.setReadOffset(0); - } else { - // There will be more to come. - rsp.attr_value.len = maxOffset; - rsp.attr_value.offset = m_value.getReadOffset(); - memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len); - m_value.setReadOffset(rsp.attr_value.offset + maxOffset); - } - } else { // read.is_long == false - - if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback - m_pCallbacks->onRead(this); // Invoke the read callback. - } - std::string value = m_value.getValue(); - - if (value.length() + 1 > maxOffset) { - // Too big for a single shot entry. - m_value.setReadOffset(maxOffset); - rsp.attr_value.len = maxOffset; - rsp.attr_value.offset = 0; - memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); - } else { - // Will fit in a single packet with no callbacks required. - rsp.attr_value.len = value.length(); - rsp.attr_value.offset = 0; - memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len); - } - - // if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback - // m_pCallbacks->onRead(this); // Invoke the read callback. - // } - } - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - - char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len); - ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset); - free(pHexData); - - esp_err_t errRc = ::esp_ble_gatts_send_response( - gatts_if, param->read.conn_id, - param->read.trans_id, - ESP_GATT_OK, - &rsp); - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - } - } // Response needed - } // Handle matches this characteristic. - break; - } // ESP_GATTS_READ_EVT - - - // ESP_GATTS_CONF_EVT - // - // conf: - // - esp_gatt_status_t status – The status code. - // - uint16_t conn_id – The connection used. - // - case ESP_GATTS_CONF_EVT: { - // ESP_LOGD(LOG_TAG, "m_handle = %d, conf->handle = %d", m_handle, param->conf.handle); - if(param->conf.conn_id == getService()->getServer()->getConnId()) // && param->conf.handle == m_handle) // bug in esp-idf and not implemented in arduino yet - m_semaphoreConfEvt.give(param->conf.status); - break; - } - - case ESP_GATTS_CONNECT_EVT: { - break; - } - - case ESP_GATTS_DISCONNECT_EVT: { - m_semaphoreConfEvt.give(); - break; - } - - default: { - break; - } // default - - } // switch event - - // Give each of the descriptors associated with this characteristic the opportunity to handle the - // event. - - m_descriptorMap.handleGATTServerEvent(event, gatts_if, param); - ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent"); -} // handleGATTServerEvent -*/ - + /** * @brief Set the subscribe status for this characteristic. @@ -578,12 +256,12 @@ void NimBLECharacteristic::notify(bool is_notification) { assert(getService() != nullptr); assert(getService()->getServer() != nullptr); -/* + if (getService()->getServer()->getConnectedCount() == 0) { NIMBLE_LOGD(LOG_TAG, "<< notify: No connected clients."); return; } -*/ + m_pCallbacks->onNotify(this); int rc = 0; @@ -665,22 +343,6 @@ void NimBLECharacteristic::notify(bool is_notification) { } // Notify -/** - * @brief Set the permission to broadcast. - * A characteristics has properties associated with it which define what it is capable of doing. - * One of these is the broadcast flag. - * @param [in] value The flag value of the property. - * @return N/A - */ -void NimBLECharacteristic::setBroadcastProperty(bool value) { - if (value) { - m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST); - } else { - m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST); - } -} // setBroadcastProperty - - /** * @brief Set the callback handlers for this characteristic. * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic. @@ -693,22 +355,21 @@ void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallback } } // setCallbacks - +// Backward compatibility - to be removed //////////////////////////////// /** - * @brief Set the BLE handle associated with this characteristic. - * A user program will request that a characteristic be created against a service. When the characteristic has been - * registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the - * server/service but it is told to the service, not the characteristic associated with the service. This internally - * exposed function can be invoked by the service against this model of the characteristic to allow the characteristic - * to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events - * that will be propagated down to it which contain a handle value and now know that the event is destined for it. - * @param [in] handle The handle associated with this characteristic. + * @brief Set the permission to broadcast. + * A characteristics has properties associated with it which define what it is capable of doing. + * One of these is the broadcast flag. + * @param [in] value The flag value of the property. + * @return N/A */ -void NimBLECharacteristic::setHandle(uint16_t handle) { - NIMBLE_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str()); - m_handle = handle; - NIMBLE_LOGD(LOG_TAG, "<< setHandle"); -} // setHandle +void NimBLECharacteristic::setBroadcastProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_BROADCAST); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_BROADCAST); + } +} // setBroadcastProperty /** @@ -743,13 +404,39 @@ void NimBLECharacteristic::setNotifyProperty(bool value) { */ void NimBLECharacteristic::setReadProperty(bool value) { if (value) { - m_properties = (m_properties | BLE_GATT_CHR_F_READ ); + m_properties = (m_properties | BLE_GATT_CHR_F_READ); } else { - m_properties = (m_properties & ~BLE_GATT_CHR_F_READ ); + m_properties = (m_properties & ~BLE_GATT_CHR_F_READ); } } // setReadProperty +/** + * @brief Set the Write No Response property value. + * @param [in] value Set to true if we are to allow writes with no response. + */ +void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP); + } +} // setWriteNoResponseProperty + + +/** + * @brief Set the Write property value. + * @param [in] value Set to true if we are to allow writes. + */ +void NimBLECharacteristic::setWriteProperty(bool value) { + if (value) { + m_properties = (m_properties | BLE_GATT_CHR_F_WRITE ); + } else { + m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE ); + } +} // setWriteProperty +////////////////////////////////////////////////////////////////////////////////// + /** * @brief Set the value of the characteristic. * @param [in] data The data to set for the characteristic. @@ -775,6 +462,7 @@ void NimBLECharacteristic::setValue(uint8_t* data, size_t length) { NIMBLE_LOGD(LOG_TAG, "<< setValue"); } // setValue + /** * @brief Set the value of the characteristic from string data. * We set the value of the characteristic from the bytes contained in the @@ -782,7 +470,6 @@ void NimBLECharacteristic::setValue(uint8_t* data, size_t length) { * @param [in] Set the value of the characteristic. * @return N/A. */ - void NimBLECharacteristic::setValue(std::string value) { setValue((uint8_t*)(value.data()), value.length()); } // setValue @@ -823,32 +510,6 @@ void NimBLECharacteristic::setValue(double& data64) { } // setValue -/** - * @brief Set the Write No Response property value. - * @param [in] value Set to true if we are to allow writes with no response. - */ -void NimBLECharacteristic::setWriteNoResponseProperty(bool value) { - if (value) { - m_properties = (m_properties | BLE_GATT_CHR_F_WRITE_NO_RSP); - } else { - m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE_NO_RSP); - } -} // setWriteNoResponseProperty - - -/** - * @brief Set the Write property value. - * @param [in] value Set to true if we are to allow writes. - */ -void NimBLECharacteristic::setWriteProperty(bool value) { - if (value) { - m_properties = (m_properties | BLE_GATT_CHR_F_WRITE ); - } else { - m_properties = (m_properties & ~BLE_GATT_CHR_F_WRITE ); - } -} // setWriteProperty - - /** * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 2fe89822..dc05c2c9 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -43,7 +43,6 @@ class NimBLEDescriptorMap { NimBLEDescriptor* getByUUID(NimBLEUUID uuid); // NimBLEDescriptor* getByHandle(uint16_t handle); std::string toString(); -// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); NimBLEDescriptor* getFirst(); NimBLEDescriptor* getNext(); uint8_t getSize(); @@ -72,11 +71,15 @@ class NimBLECharacteristic { void indicate(); void notify(bool is_notification = true); - void setBroadcastProperty(bool value); void setCallbacks(NimBLECharacteristicCallbacks* pCallbacks); +// Backward Compatibility - to be removed + void setBroadcastProperty(bool value); void setIndicateProperty(bool value); void setNotifyProperty(bool value); void setReadProperty(bool value); + void setWriteProperty(bool value); + void setWriteNoResponseProperty(bool value); +////////////////////////////////////////////////////// void setValue(uint8_t* data, size_t size); void setValue(std::string value); void setValue(uint16_t& data16); @@ -84,19 +87,22 @@ class NimBLECharacteristic { void setValue(int& data32); void setValue(float& data32); void setValue(double& data64); - void setWriteProperty(bool value); - void setWriteNoResponseProperty(bool value); + std::string toString(); uint16_t getHandle(); void setAccessPermissions(uint16_t perm); +// Backward Compatibility - to be removed static const uint32_t PROPERTY_READ = 1<<0; static const uint32_t PROPERTY_WRITE = 1<<1; static const uint32_t PROPERTY_NOTIFY = 1<<2; static const uint32_t PROPERTY_BROADCAST = 1<<3; static const uint32_t PROPERTY_INDICATE = 1<<4; static const uint32_t PROPERTY_WRITE_NR = 1<<5; - +////////////////////////////////////////////////////// + #define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC + #define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC + private: friend class NimBLEServer; @@ -104,8 +110,8 @@ class NimBLECharacteristic { // friend class NimBLEDescriptor; // friend class NimBLECharacteristicMap; - NimBLECharacteristic(const char* uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); - NimBLECharacteristic(NimBLEUUID uuid, uint32_t properties = 0, NimBLEService* pService = nullptr); + NimBLECharacteristic(const char* uuid, uint16_t properties = 0, NimBLEService* pService = nullptr); + NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = 0, NimBLEService* pService = nullptr); virtual ~NimBLECharacteristic(); NimBLEUUID m_uuid; @@ -121,11 +127,9 @@ class NimBLECharacteristic { NimBLEService* getService(); uint8_t getProperties(); void setSubscribe(struct ble_gap_event *event); - void setHandle(uint16_t handle); static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); -// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt"); }; // NimBLECharacteristic diff --git a/src/NimBLECharacteristicMap.cpp b/src/NimBLECharacteristicMap.cpp index 1c0f10b7..3e30f270 100644 --- a/src/NimBLECharacteristicMap.cpp +++ b/src/NimBLECharacteristicMap.cpp @@ -83,21 +83,6 @@ NimBLECharacteristic* NimBLECharacteristicMap::getNext() { } // getNext -/** - * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping - * @param [in] event - * @param [in] gatts_if - * @param [in] param - */ -/* -void BLECharacteristicMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { - // Invoke the handler for every Service we have. - for (auto& myPair : m_uuidMap) { - myPair.first->handleGATTServerEvent(event, gatts_if, param); - } -} // handleGATTServerEvent -*/ - /** * @brief Set the characteristic by handle. * @param [in] handle The handle of the characteristic. diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 36cc9fda..2466679d 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -17,16 +17,6 @@ #include "NimBLEClient.h" #include "NimBLEUtils.h" #include "NimBLEDevice.h" -/* -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLEClient"; -#endif -*/ - #include "NimBLELog.h" #include @@ -645,7 +635,7 @@ uint16_t NimBLEClient::getMTU() { struct ble_gap_conn_desc desc; rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); assert(rc == 0); - client->m_pClientCallbacks->onAuthenticationComplete(desc); + client->m_pClientCallbacks->onAuthenticationComplete(&desc); } client->m_semeaphoreSecEvt.give(event->enc_change.status); @@ -655,7 +645,6 @@ uint16_t NimBLEClient::getMTU() { case BLE_GAP_EVENT_PASSKEY_ACTION: { struct ble_sm_io pkey = {0}; - //int key = 0; if(client->m_conn_id != event->passkey.conn_handle) return 0; @@ -669,7 +658,11 @@ uint16_t NimBLEClient::getMTU() { } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { NIMBLE_LOGD(LOG_TAG, "Passkey on device's display: %d", event->passkey.params.numcmp); pkey.action = event->passkey.params.action; - if(client->m_pClientCallbacks != nullptr) { + // 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 if(client->m_pClientCallbacks != nullptr) { pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); }else{ pkey.numcmp_accept = false; @@ -691,7 +684,11 @@ uint16_t NimBLEClient::getMTU() { NIMBLE_LOGD(LOG_TAG, "Enter the passkey"); pkey.action = event->passkey.params.action; - if(client->m_pClientCallbacks != nullptr) { + // Compatibility only - Do not use, should be removed the in future + if(NimBLEDevice::m_securityCallbacks != nullptr) { + pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); + ///////////////////////////////////////////// + }else if(client->m_pClientCallbacks != nullptr) { pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); }else{ @@ -710,7 +707,6 @@ uint16_t NimBLEClient::getMTU() { } default: { - //NimBLEUtils::dumpGapEvent(event, arg); return 0; } } // Switch diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index b909ddcb..995becd7 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -94,7 +94,7 @@ class NimBLEClientCallbacks { virtual uint32_t onPassKeyRequest(){return 0;} virtual void onPassKeyNotify(uint32_t pass_key){} virtual bool onSecurityRequest(){return false;} - virtual void onAuthenticationComplete(ble_gap_conn_desc){}; + virtual void onAuthenticationComplete(ble_gap_conn_desc*){}; virtual bool onConfirmPIN(uint32_t pin){return false;} }; diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index 66b67920..ed1e67ad 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -54,41 +54,6 @@ NimBLEDescriptor::~NimBLEDescriptor() { free(m_value.attr_value); // Release the storage we created in the constructor. } // ~NimBLEDescriptor - -/** - * @brief Execute the creation of the descriptor with the BLE runtime in ESP. - * @param [in] pCharacteristic The characteristic to which to register this descriptor. - */ - /* -void NimBLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) { - NIMBLE_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str()); - - if (m_handle != NULL_HANDLE) { - NIMBLE_LOGE(LOG_TAG, "Descriptor already has a handle."); - return; - } - - m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service. - - esp_attr_control_t control; - control.auto_rsp = ESP_GATT_AUTO_RSP; - m_semaphoreCreateEvt.take("executeCreate"); - esp_err_t errRc = ::esp_ble_gatts_add_char_descr( - pCharacteristic->getService()->getHandle(), - getUUID().getNative(), - (esp_gatt_perm_t)m_permissions, - &m_value, - &control); - if (errRc != ESP_OK) { - NIMBLE_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - - m_semaphoreCreateEvt.wait("executeCreate"); - NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); -} // executeCreate -*/ - /** * @brief Get the BLE handle for this descriptor. * @return The handle for this descriptor. @@ -161,89 +126,6 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, return BLE_ATT_ERR_UNLIKELY; } - -/** - * @brief Handle GATT server events for the descripttor. - * @param [in] event - * @param [in] gatts_if - * @param [in] param - */ - /* -void NimBLEDescriptor::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { - switch (event) { - // ESP_GATTS_ADD_CHAR_DESCR_EVT - // - // add_char_descr: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - case ESP_GATTS_ADD_CHAR_DESCR_EVT: { - if (m_pCharacteristic != nullptr && - m_uuid.equals(NimBLEUUID(param->add_char_descr.descr_uuid)) && - m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle && - m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) { - setHandle(param->add_char_descr.attr_handle); - m_semaphoreCreateEvt.give(); - } - break; - } // ESP_GATTS_ADD_CHAR_DESCR_EVT - - // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived. - // - // write: - // - uint16_t conn_id - // - uint16_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool need_rsp - // - bool is_prep - // - uint16_t len - // - uint8_t *value - case ESP_GATTS_WRITE_EVT: { - if (param->write.handle == m_handle) { - setValue(param->write.value, param->write.len); // Set the value of the descriptor. - - if (m_pCallbacks != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now. - m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler. - } - } // End of ... this is our handle. - - break; - } // ESP_GATTS_WRITE_EVT - - // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived. - // - // read: - // - uint16_t conn_id - // - uint32_t trans_id - // - esp_bd_addr_t bda - // - uint16_t handle - // - uint16_t offset - // - bool is_long - // - bool need_rsp - // - case ESP_GATTS_READ_EVT: { - if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it - - if (m_pCallbacks != nullptr) { // If we have a user supplied callback, invoke it now. - m_pCallbacks->onRead(this); // Invoke the onRead callback method in the callback handler. - } - - } // End of this is our handle - break; - } // ESP_GATTS_READ_EVT - - default: - break; - } // switch event -} // handleGATTServerEvent -*/ - /** * @brief Set the callback handlers for this descriptor. * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor. diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 83bf470f..e0f1af3b 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -48,11 +48,6 @@ class NimBLEDescriptor { size_t getLength(); // Get the length of the value of the descriptor. NimBLEUUID getUUID(); // Get the UUID of the descriptor. uint8_t* getValue(); // Get a pointer to the value of the descriptor. -/* void handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param); -*/ void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. @@ -70,10 +65,8 @@ class NimBLEDescriptor { NimBLEDescriptorCallbacks* m_pCallbacks; NimBLECharacteristic* m_pCharacteristic; uint8_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; -// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); attr_value_t m_value; -// void executeCreate(BLECharacteristic* pCharacteristic); static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); diff --git a/src/NimBLEDescriptorMap.cpp b/src/NimBLEDescriptorMap.cpp index 3e73a9c1..8d0a13d2 100644 --- a/src/NimBLEDescriptorMap.cpp +++ b/src/NimBLEDescriptorMap.cpp @@ -117,24 +117,6 @@ std::string NimBLEDescriptorMap::toString() { } // toString -/** - * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping - * @param [in] event - * @param [in] gatts_if - * @param [in] param - */ - /* -void NimBLEDescriptorMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { - // Invoke the handler for every descriptor we have. - for (auto &myPair : m_uuidMap) { - myPair.first->handleGATTServerEvent(event, gatts_if, param); - } -} // handleGATTServerEvent -*/ - /** * @brief Get the first descriptor in the map. * @return The first descriptor in the map. diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 6287cf78..5d8b656a 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -400,7 +400,7 @@ bool NimBLEDevice::getInitialized() { * @param sc, if true we will perform secure connection pairing, false we will use legacy pairing. */ /*STATIC*/ void NimBLEDevice::setSecuityAuth(bool bonding, bool mitm, bool sc) { - NIMBLE_LOGE(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,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; diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 8ece70f5..16546a36 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -98,6 +98,7 @@ class NimBLEDevice { static std::list* getClientList(); private: + friend class NimBLEServer; friend class NimBLEClient; friend class NimBLEScan; // friend class NimBLERemoteService; diff --git a/src/NimBLELog.h b/src/NimBLELog.h index 2470c261..2e8f8526 100644 --- a/src/NimBLELog.h +++ b/src/NimBLELog.h @@ -10,6 +10,7 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "syscfg/syscfg.h" #include "modlog/modlog.h" #define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__) diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 4562fc1f..1ab16e48 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -19,17 +19,6 @@ #include #include "NimBLEUtils.h" - -/* -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLERemoteCharacteristic"; // The logging tag for this class. -#endif -*/ - #include "NimBLELog.h" static const char* LOG_TAG = "NimBLERemoteCharacteristic"; diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp index b44e0cb2..014c6ca6 100644 --- a/src/NimBLERemoteDescriptor.cpp +++ b/src/NimBLERemoteDescriptor.cpp @@ -14,15 +14,6 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -/* -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLERemoteDescriptor"; -#endif -*/ #include "NimBLERemoteDescriptor.h" #include "NimBLELog.h" diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index 3f307fb6..501f3f52 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -17,16 +17,6 @@ #include "NimBLERemoteService.h" #include "NimBLEUtils.h" #include "NimBLEDevice.h" -/* -#include -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLERemoteService"; -#endif -*/ #include "NimBLELog.h" static const char* LOG_TAG = "NimBLERemoteService"; diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index ee56d788..55f6c30d 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -17,16 +17,6 @@ #include "NimBLEScan.h" #include "NimBLEUtils.h" #include "NimBLEDevice.h" - -//#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -//#include "esp32-hal-log.h" -//#define LOG_TAG "" -//#else -//#include "esp_log.h" -//static const char* LOG_TAG = "NimBLEScan"; -//#endif -//#include "modlog/modlog.h" - #include "NimBLELog.h" #include diff --git a/src/NimBLESecurity.h b/src/NimBLESecurity.h index 0c920d0e..562aad97 100644 --- a/src/NimBLESecurity.h +++ b/src/NimBLESecurity.h @@ -107,7 +107,7 @@ class NimBLESecurityCallbacks { /** * Provide us information when authentication process is completed */ - virtual void onAuthenticationComplete(ble_gap_conn_desc) = 0; + virtual void onAuthenticationComplete(ble_gap_conn_desc*) = 0; virtual bool onConfirmPIN(uint32_t pin) = 0; }; // BLESecurityCallbacks diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 3d6d090e..def89fee 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -235,6 +235,7 @@ uint32_t NimBLEServer::getConnectedCount() { NimBLEServer* server = (NimBLEServer*)arg; NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); + int rc = 0; switch(event->type) { @@ -249,14 +250,14 @@ uint32_t NimBLEServer::getConnectedCount() { server->addPeerDevice((void*)server, false, server->m_connId); if (server->m_pServerCallbacks != nullptr) { ble_gap_conn_desc desc; - int rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); assert(rc == 0); server->m_pServerCallbacks->onConnect(server); server->m_pServerCallbacks->onConnect(server, &desc); } } - break; + return 0; } // BLE_GAP_EVENT_CONNECT @@ -268,7 +269,7 @@ uint32_t NimBLEServer::getConnectedCount() { /* Connection terminated; resume advertising */ //NimBLEDevice::startAdvertising(); server->removePeerDevice(event->disconnect.conn.conn_handle, false); - break; + return 0; } // BLE_GAP_EVENT_DISCONNECT case BLE_GAP_EVENT_SUBSCRIBE: { @@ -281,7 +282,7 @@ uint32_t NimBLEServer::getConnectedCount() { (*it).second->setSubscribe(event); } - break; + return 0; } // BLE_GAP_EVENT_SUBSCRIBE case BLE_GAP_EVENT_MTU: { @@ -289,7 +290,7 @@ uint32_t NimBLEServer::getConnectedCount() { event->mtu.conn_handle, event->mtu.value); server->updatePeerMTU(event->mtu.conn_handle, event->mtu.value); - break; + return 0; } // BLE_GAP_EVENT_MTU case BLE_GAP_EVENT_NOTIFY_TX: { @@ -300,8 +301,93 @@ uint32_t NimBLEServer::getConnectedCount() { } } - break; + return 0; } // BLE_GAP_EVENT_NOTIFY_TX + + case BLE_GAP_EVENT_CONN_UPDATE: { + NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); + return 0; + } // BLE_GAP_EVENT_CONN_UPDATE + + case BLE_GAP_EVENT_ENC_CHANGE: { + //if(client->m_conn_id != event->enc_change.conn_handle) + // return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + + struct ble_gap_conn_desc desc; + int rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + if(rc != 0) { + return BLE_ATT_ERR_INVALID_HANDLE; + } + + server->m_pServerCallbacks->onAuthenticationComplete(&desc); + if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + } + + return 0; + } // BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_PASSKEY_ACTION: { + struct ble_sm_io pkey = {0}; + + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + pkey.action = event->passkey.params.action; + pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer + 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: %d", 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 if(server->m_pServerCallbacks != nullptr) { + pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + }else{ + pkey.numcmp_accept = false; + } + + 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 if(server->m_pServerCallbacks != nullptr) { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); + }else{ + pkey.passkey = 0; + NIMBLE_LOGE(LOG_TAG, "No Callback! Sending 0 as the passkey"); + } +; + 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; diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 222886a1..2ef65c13 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -46,7 +46,6 @@ class NimBLEServiceMap { // NimBLEService* getByHandle(uint16_t handle); NimBLEService* getByUUID(const char* uuid); NimBLEService* getByUUID(NimBLEUUID uuid, uint8_t inst_id = 0); -// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); // void setByHandle(uint16_t handle, NimBLEService* service); void setByUUID(const char* uuid, NimBLEService* service); void setByUUID(NimBLEUUID uuid, NimBLEService* service); @@ -141,6 +140,12 @@ class NimBLEServerCallbacks { * @param [in] pServer A reference to the %BLE server that received the existing client disconnection. */ virtual void onDisconnect(NimBLEServer* pServer); + + virtual uint32_t onPassKeyRequest(){return 0;} + virtual void onPassKeyNotify(uint32_t pass_key){} + virtual bool onSecurityRequest(){return true;} + virtual void onAuthenticationComplete(ble_gap_conn_desc*){}; + virtual bool onConfirmPIN(uint32_t pin){return true;} }; // BLEServerCallbacks diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 19780955..3bd540ec 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -16,20 +16,13 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -//#include -//#include #include "NimBLEService.h" #include "NimBLEUtils.h" #include "NimBLELog.h" -//#include -//#include #include -//#include "BLEServer.h" -//#include "GeneralUtils.h" - static const char* LOG_TAG = "NimBLEService"; // Tag for logging. #define NULL_HANDLE (0xffff) @@ -60,56 +53,6 @@ NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* } // NimBLEService -/** - * @brief Create the service. - * Create the service. - * @param [in] gatts_if The handle of the GATT server interface. - * @return N/A. - */ -/* -void NimBLEService::executeCreate(NimBLEServer* pServer) { - NIMBLE_LOGD(LOG_TAG, ">> executeCreate() - Creating service service uuid: %s", getUUID().toString().c_str()); - m_pServer = pServer; -// m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT - -// esp_gatt_srvc_id_t srvc_id; -// srvc_id.is_primary = true; -// srvc_id.id.inst_id = m_instId; -// srvc_id.id.uuid = *m_uuid.getNative(); -// esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, m_numHandles); // The maximum number of handles associated with the service. - -// if (errRc != ESP_OK) { -// ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); -// return; -// } - -// m_semaphoreCreateEvt.wait("executeCreate"); - NIMBLE_LOGD(LOG_TAG, "<< executeCreate"); -} // executeCreate -*/ - -/** - * @brief Delete the service. - * Delete the service. - * @return N/A. - */ -/* -void NimBLEService::executeDelete() { - NIMBLE_LOGD(LOG_TAG, ">> executeDelete()"); -// m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT - -// esp_err_t errRc = ::esp_ble_gatts_delete_service(getHandle()); - -// if (errRc != ESP_OK) { -// ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); -// return; -// } - -// m_semaphoreDeleteEvt.wait("executeDelete"); - NIMBLE_LOGD(LOG_TAG, "<< executeDelete"); -} // executeDelete -*/ - /** * @brief Dump details of this BLE GATT service. * @return N/A. @@ -236,33 +179,6 @@ bool NimBLEService::start() { } // start -/** - * @brief Stop the service. - */ - /* -void BLEService::stop() { -// We ask the BLE runtime to start the service and then create each of the characteristics. -// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event -// obtained as a result of calling esp_ble_gatts_create_service(). - ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str()); - if (m_handle == NULL_HANDLE) { - ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!"); - return; - } - - m_semaphoreStopEvt.take("stop"); - esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle); - - if (errRc != ESP_OK) { - ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); - return; - } - m_semaphoreStopEvt.wait("stop"); - - ESP_LOGD(LOG_TAG, "<< stop()"); -} // start -*/ - /** * @brief Set the handle associated with this service. * @param [in] handle The handle associated with the service. @@ -340,109 +256,6 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(NimBLEUUID uuid, uint3 } // createCharacteristic -/** - * @brief Handle a GATTS server event. - */ - /* -void BLEService::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param) { - switch (event) { - // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service. - // add_char: - // - esp_gatt_status_t status - // - uint16_t attr_handle - // - uint16_t service_handle - // - esp_bt_uuid_t char_uuid - - // If we have reached the correct service, then locate the characteristic and remember the handle - // for that characteristic. - case ESP_GATTS_ADD_CHAR_EVT: { - if (m_handle == param->add_char.service_handle) { - BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic(); - if (pCharacteristic == nullptr) { - ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!", - BLEUUID(param->add_char.char_uuid).toString().c_str()); - dump(); - break; - } - pCharacteristic->setHandle(param->add_char.attr_handle); - m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic); - break; - } // Reached the correct service. - break; - } // ESP_GATTS_ADD_CHAR_EVT - - - // ESP_GATTS_START_EVT - // - // start: - // esp_gatt_status_t status - // uint16_t service_handle - case ESP_GATTS_START_EVT: { - if (param->start.service_handle == getHandle()) { - m_semaphoreStartEvt.give(); - } - break; - } // ESP_GATTS_START_EVT - - // ESP_GATTS_STOP_EVT - // - // stop: - // esp_gatt_status_t status - // uint16_t service_handle - // - case ESP_GATTS_STOP_EVT: { - if (param->stop.service_handle == getHandle()) { - m_semaphoreStopEvt.give(); - } - break; - } // ESP_GATTS_STOP_EVT - - - // ESP_GATTS_CREATE_EVT - // Called when a new service is registered as having been created. - // - // create: - // * esp_gatt_status_t status - // * uint16_t service_handle - // * esp_gatt_srvc_id_t service_id - // * - esp_gatt_id id - // * - esp_bt_uuid uuid - // * - uint8_t inst_id - // * - bool is_primary - // - case ESP_GATTS_CREATE_EVT: { - if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_instId == param->create.service_id.id.inst_id) { - setHandle(param->create.service_handle); - m_semaphoreCreateEvt.give(); - } - break; - } // ESP_GATTS_CREATE_EVT - - - // ESP_GATTS_DELETE_EVT - // Called when a service is deleted. - // - // delete: - // * esp_gatt_status_t status - // * uint16_t service_handle - // - case ESP_GATTS_DELETE_EVT: { - if (param->del.service_handle == getHandle()) { - m_semaphoreDeleteEvt.give(); - } - break; - } // ESP_GATTS_DELETE_EVT - - default: - break; - } // Switch - - // Invoke the GATTS handler in each of the associated characteristics. - m_characteristicMap.handleGATTServerEvent(event, gatts_if, param); -} // handleGATTServerEvent -*/ - - NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) { return getCharacteristic(NimBLEUUID(uuid)); } diff --git a/src/NimBLEService.h b/src/NimBLEService.h index b4330297..b9156c97 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -42,7 +42,6 @@ class NimBLECharacteristicMap { NimBLECharacteristic* getNext(); uint8_t getSize(); std::string toString(); -// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); private: std::map m_uuidMap; @@ -61,8 +60,6 @@ class NimBLEService { NimBLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, uint32_t properties); void dump(); -// void executeCreate(NimBLEServer* pServer); -// void executeDelete(); NimBLECharacteristic* getCharacteristic(const char* uuid); NimBLECharacteristic* getCharacteristic(NimBLEUUID uuid); NimBLEUUID getUUID(); @@ -77,9 +74,6 @@ class NimBLEService { NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer); NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer); friend class NimBLEServer; -// friend class BLEServiceMap; -// friend class BLEDescriptor; -// friend class BLECharacteristic; friend class NimBLEDevice; NimBLECharacteristicMap m_characteristicMap; @@ -88,17 +82,10 @@ class NimBLEService { NimBLEServer* m_pServer = nullptr; NimBLEUUID m_uuid; -// FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); -// FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt"); -// FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt"); -// FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt"); - uint16_t m_numHandles; NimBLECharacteristic* getLastCreatedCharacteristic(); -// void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t* param); void setHandle(uint16_t handle); - //void setService(esp_gatt_srvc_id_t srvc_id); }; // BLEService diff --git a/src/NimBLEServiceMap.cpp b/src/NimBLEServiceMap.cpp index 9f9fa3dc..779c49a1 100644 --- a/src/NimBLEServiceMap.cpp +++ b/src/NimBLEServiceMap.cpp @@ -92,17 +92,6 @@ std::string NimBLEServiceMap::toString() { return res; } // toString -/* -void BLEServiceMap::handleGATTServerEvent( - esp_gatts_cb_event_t event, - esp_gatt_if_t gatts_if, - esp_ble_gatts_cb_param_t* param) { - // Invoke the handler for every Service we have. - for (auto &myPair : m_uuidMap) { - myPair.first->handleGATTServerEvent(event, gatts_if, param); - } -} -*/ /** * @brief Get the first service in the map. diff --git a/src/NimBLEUUID.cpp b/src/NimBLEUUID.cpp index 1d3ca43b..0002d191 100644 --- a/src/NimBLEUUID.cpp +++ b/src/NimBLEUUID.cpp @@ -16,58 +16,11 @@ #include "NimBLEUtils.h" #include "NimBLEUUID.h" -/* -#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLEUUID"; -#endif -*/ #include "NimBLELog.h" -//#include -//#include -//#include -//#include -//#include - static const char* LOG_TAG = "NimBLEUUID"; -/** - * @brief Copy memory from source to target but in reverse order. - * - * When we move memory from one location it is normally: - * - * ``` - * [0][1][2]...[n] -> [0][1][2]...[n] - * ``` - * - * with this function, it is: - * - * ``` - * [0][1][2]...[n] -> [n][n-1][n-2]...[0] - * ``` - * - * @param [in] target The target of the copy - * @param [in] source The source of the copy - * @param [in] size The number of bytes to copy - */ - /* -static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { - assert(size > 0); - target += (size - 1); // Point target to the last byte of the target data - while (size > 0) { - *target = *source; - target--; - source++; - size--; - } -} // memrcpy -*/ - /** * @brief Create a UUID from a string. * diff --git a/src/NimBLEUUID.h b/src/NimBLEUUID.h index a2fa1120..df36a52a 100644 --- a/src/NimBLEUUID.h +++ b/src/NimBLEUUID.h @@ -31,7 +31,6 @@ class NimBLEUUID { NimBLEUUID(uint32_t uuid); NimBLEUUID(ble_uuid128_t* uuid); NimBLEUUID(uint8_t* pData, size_t size, bool msbFirst); -// BLEUUID(esp_gatt_id_t gattId); NimBLEUUID(); uint8_t bitSize(); // Get the number of bits in this uuid. bool equals(NimBLEUUID uuid); diff --git a/src/NimBLEUtils.cpp b/src/NimBLEUtils.cpp index 30de9e67..038c00b8 100644 --- a/src/NimBLEUtils.cpp +++ b/src/NimBLEUtils.cpp @@ -10,18 +10,6 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLEUtils.h" -/* -#if defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLEUtils"; -#endif - -#include "modlog/modlog.h" -*/ - #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEUtils"; diff --git a/src/NimBLEValue.cpp b/src/NimBLEValue.cpp index b21f9f83..808812bc 100644 --- a/src/NimBLEValue.cpp +++ b/src/NimBLEValue.cpp @@ -15,7 +15,7 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLEValue.h" -//#include "NimBLELog.h" +#include "NimBLELog.h" static const char* LOG_TAG="NimBLEValue"; @@ -32,7 +32,7 @@ NimBLEValue::NimBLEValue() { * @param [in] part A message part being added. */ void NimBLEValue::addPart(std::string part) { -// NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); + NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", part.length()); m_accumulation += part; } // addPart @@ -44,7 +44,7 @@ void NimBLEValue::addPart(std::string part) { * @param [in] length The number of bytes being added. */ void NimBLEValue::addPart(uint8_t* pData, size_t length) { -// NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length); + NIMBLE_LOGD(LOG_TAG, ">> addPart: length=%d", length); m_accumulation += std::string((char*) pData, length); } // addPart @@ -53,7 +53,7 @@ void NimBLEValue::addPart(uint8_t* pData, size_t length) { * @brief Cancel the current accumulation. */ void NimBLEValue::cancel() { -// NIMBLE_LOGD(LOG_TAG, ">> cancel"); + NIMBLE_LOGD(LOG_TAG, ">> cancel"); m_accumulation = ""; m_readOffset = 0; } // cancel @@ -66,7 +66,7 @@ void NimBLEValue::cancel() { * we now have the complete message and commit the change as a unit. */ void NimBLEValue::commit() { -// NIMBLE_LOGD(LOG_TAG, ">> commit"); + NIMBLE_LOGD(LOG_TAG, ">> commit"); // If there is nothing to commit, do nothing. if (m_accumulation.length() == 0) return; setValue(m_accumulation); From f43533d731d8c698de0c554bc36244886e17b9e1 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 15 Mar 2020 11:50:08 -0600 Subject: [PATCH 23/62] Code cleanup. Fix notifications sent to multiple clients from server, where any clients after the first one sent got incorrect data. Fix abort on advertising start if already advertising. Fix compile error in NimBLELog.h --- src/NimBLEAdvertisedDevice.cpp | 10 ------ src/NimBLEAdvertising.cpp | 7 ++-- src/NimBLECharacteristic.cpp | 11 +++--- src/NimBLECharacteristicMap.cpp | 2 +- src/NimBLEClient.cpp | 16 ++------- src/NimBLEDevice.cpp | 20 ++++------- src/NimBLEDevice.h | 3 +- src/NimBLERemoteCharacteristic.cpp | 58 ++++++------------------------ src/NimBLERemoteCharacteristic.h | 4 +-- src/NimBLEScan.cpp | 1 + src/NimBLESecurity.cpp | 3 +- src/NimBLESecurity.h | 6 ++-- src/NimBLEServer.cpp | 22 ++---------- src/NimBLEService.cpp | 16 +-------- src/NimBLEService.h | 5 --- 15 files changed, 47 insertions(+), 137 deletions(-) diff --git a/src/NimBLEAdvertisedDevice.cpp b/src/NimBLEAdvertisedDevice.cpp index a29404f5..a0c2e308 100644 --- a/src/NimBLEAdvertisedDevice.cpp +++ b/src/NimBLEAdvertisedDevice.cpp @@ -16,16 +16,6 @@ #include "NimBLEAdvertisedDevice.h" #include "NimBLEUtils.h" -/* -#if defined(CONFIG_ARDUHAL_ESP_LOG) -#include "esp32-hal-log.h" -#define LOG_TAG "" -#else -#include "esp_log.h" -static const char* LOG_TAG = "NimBLEAdvertisedDevice"; -#endif -*/ - #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEAdvertisedDevice"; diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index b428eacf..d7605e96 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -19,10 +19,8 @@ #include "NimBLEAdvertising.h" #include "NimBLEDevice.h" #include "NimBLEServer.h" -//#include #include "NimBLEUtils.h" #include "NimBLELog.h" -//#include "GeneralUtils.h" static const char* LOG_TAG = "NimBLEAdvertising"; @@ -183,6 +181,11 @@ void NimBLEAdvertising::start() { int rc = 0; uint8_t addressType; uint8_t payloadLen = 3; //start with 3 bytes for the flags data + + // If already advertising just return + if(ble_gap_adv_active()) { + return; + } NimBLEServer* pServer = NimBLEDevice::createServer(); if(!pServer->m_gattsStarted){ diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 394724ca..c0ec90ed 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -60,7 +60,6 @@ NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties, * @brief Destructor. */ NimBLECharacteristic::~NimBLECharacteristic() { - //free(m_value.attr_value); // Release the storage for the value. } // ~NimBLECharacteristic @@ -265,13 +264,17 @@ void NimBLECharacteristic::notify(bool is_notification) { m_pCallbacks->onNotify(this); int rc = 0; - os_mbuf *om; - size_t length = m_value.getValue().length(); - uint8_t* data = (uint8_t*)m_value.getValue().data(); + //os_mbuf *om; + //size_t length = m_value.getValue().length(); + //uint8_t* data = (uint8_t*)m_value.getValue().data(); NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { uint16_t _mtu = getService()->getServer()->getPeerMTU((*it).first); + // Must rebuild the data on each loop iteration as NimBLE will release it. + size_t length = m_value.getValue().length(); + uint8_t* data = (uint8_t*)m_value.getValue().data(); + os_mbuf *om; if(_mtu == 0) { NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); diff --git a/src/NimBLECharacteristicMap.cpp b/src/NimBLECharacteristicMap.cpp index 3e30f270..9ee741bc 100644 --- a/src/NimBLECharacteristicMap.cpp +++ b/src/NimBLECharacteristicMap.cpp @@ -47,7 +47,7 @@ NimBLECharacteristic* NimBLECharacteristicMap::getByUUID(NimBLEUUID uuid) { return myPair.first; } } - //return m_uuidMap.at(uuid.toString()); + return nullptr; } // getByUUID diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 2466679d..a2d3190c 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -59,11 +59,6 @@ NimBLEClient::NimBLEClient() * to ensure proper disconnect and removal from device list. */ NimBLEClient::~NimBLEClient() { - //m_isConnected = false; - //m_semaphoreOpenEvt.give(1); - //m_semaphoreSearchCmplEvt.give(1); - //m_semeaphoreSecEvt.give(1); - // We may have allocated service references associated with this client. // Before we are finished with the client, we must release resources. clearServices(); @@ -509,7 +504,8 @@ uint16_t NimBLEClient::getMTU() { if(client->m_conn_id != event->disconnect.conn.conn_handle) return 0; - NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d ", event->disconnect.reason); + NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason, + NimBLEUtils::returnCodeToString(event->disconnect.reason)); //print_conn_desc(&event->disconnect.conn); //MODLOG_DFLT(INFO, "\n"); @@ -565,7 +561,6 @@ uint16_t NimBLEClient::getMTU() { // print_conn_desc(&desc); // MODLOG_DFLT(INFO, "\n"); - // BLEDevice::updatePeerDevice(this, true, m_gattc_if); client->m_isConnected = true; if (client->m_pClientCallbacks != nullptr) { @@ -618,14 +613,8 @@ uint16_t NimBLEClient::getMTU() { case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); - //NimBLEDevice::gapEventHandler(event, arg); - return 0; - } - -/* case BLE_GAP_EVENT_CONN_UPDATE: { return 0; } -*/ case BLE_GAP_EVENT_ENC_CHANGE: { if(client->m_conn_id != event->enc_change.conn_handle) @@ -639,7 +628,6 @@ uint16_t NimBLEClient::getMTU() { } client->m_semeaphoreSecEvt.give(event->enc_change.status); - //NimBLEDevice::gapEventHandler(event, arg); return 0; } diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 5d8b656a..c51e4911 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -52,11 +52,6 @@ std::list NimBLEDevice::m_cList; std::list NimBLEDevice::m_ignoreList; NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr; -//esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; -//BLESecurityCallbacks* BLEDevice::m_securityCallbacks = nullptr; -//uint16_t BLEDevice::m_localMTU = 23; // not sure if this variable is useful -//BLEAdvertising* BLEDevice::m_bleAdvertising = nullptr; -//uint16_t BLEDevice::m_appId = 0; //std::map BLEDevice::m_connectedClientsMap; //gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; @@ -281,14 +276,6 @@ void NimBLEDevice::stopAdvertising() { rc = ble_hs_util_ensure_addr(0); assert(rc == 0); - /*rc = ble_gap_event_listener_register(&m_listener, NimBLEDevice::gapEventHandler, NULL); - if(rc == BLE_HS_EALREADY){ - NIMBLE_LOGI(LOG_TAG, "Already listening to GAP events."); - } - else{ - assert(rc == 0); - } - */ NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); m_synced = true; @@ -553,6 +540,13 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) { */ 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 diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 16546a36..9de0cba4 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -53,13 +53,14 @@ #define BLEAdvertisementData NimBLEAdvertisementData #define BLEDescriptor NimBLEDescriptor #define BLE2902 NimBLE2902 +#define BLE2904 NimBLE2904 #define BLEDescriptorCallbacks NimBLEDescriptorCallbacks /** * @brief BLE functions. */ - typedef void (*gap_event_handler)(ble_gap_event *event, void *arg); + typedef int (*gap_event_handler)(ble_gap_event *event, void *arg); //typedef void (*gattc_event_handler)(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t* param); //typedef void (*gatts_event_handler)(esp_gatts_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gatts_cb_param_t* param); diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 1ab16e48..d9f1f237 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -63,7 +63,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic"; */ NimBLERemoteCharacteristic::~NimBLERemoteCharacteristic() { removeDescriptors(); // Release resources for any descriptor information we may have allocated. - //if(m_rawData != nullptr) free(m_rawData); + if(m_rawData != nullptr) free(m_rawData); } // ~NimBLERemoteCharacteristic /* @@ -383,13 +383,17 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle, NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); + if(characteristic->m_rawData != nullptr) { + free(characteristic->m_rawData); + } + if (error->status == 0) { characteristic->m_value = std::string((char*) attr->om->om_data, attr->om->om_len); characteristic->m_semaphoreReadCharEvt.give(0); - //if(m_rawData != nullptr) free(m_rawData); - //m_rawData = (uint8_t*) calloc(evtParam->read.value_len, sizeof(uint8_t)); - //memcpy(m_rawData, evtParam->read.value, evtParam->read.value_len); + characteristic->m_rawData = (uint8_t*) calloc(attr->om->om_len, sizeof(uint8_t)); + memcpy(characteristic->m_rawData, attr->om->om_data, attr->om->om_len); } else { + characteristic->m_rawData = nullptr; characteristic->m_value = ""; characteristic->m_semaphoreReadCharEvt.give(error->status); } @@ -410,64 +414,26 @@ bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallbac NIMBLE_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str()); m_notifyCallback = notifyCallback; // Save the notification callback. - -// int rc = 0; - //m_registeredForNotify = false; uint8_t val[] = {0x01, 0x00}; NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902)); if(desc == nullptr) return false; - - /*if(!m_registeredForNotify){ - - if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration. - if(!notifications) val[0] = 0x02; - } - else { // If we weren't passed a callback function, then this is an unregistration. - val[0] = 0x00; - } - - if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ESP_GATTC_WRITE_CHAR_DESC: rc=%d %s", rc, GeneralUtils::errorToString(rc)); - return false; - } - m_registeredForNotify = true; - } -*/ - // If subscribing register a callback to receive GAP events for notifications / indications if(notifyCallback != nullptr){ - // rc = ble_gap_event_listener_register(&m_gapEventListener, NimBLERemoteCharacteristic::gapEventHandler, this); - // only return false if there is a value error otherwise continue with writing to the descriptor - // if it's already registered theres no issue - // if(rc == BLE_HS_EINVAL){ - // return false; - // } - if(!notifications){ val[0] = 0x02; } } else if (notifyCallback == nullptr){ - //rc = ble_gap_event_listener_unregister(&m_gapEventListener); -// if(rc == BLE_HS_EINVAL){ -// return false; -// } val[0] = 0x00; } -/* - if(!desc->writeValue(val, 2, response)){ - return false; - } -*/ - NIMBLE_LOGD(LOG_TAG, "<< registerForNotify()"); - //m_registeredForNotify = true; + NIMBLE_LOGD(LOG_TAG, "<< registerForNotify()"); - return desc->writeValue(val, 2, response); //true; + return desc->writeValue(val, 2, response); } // registerForNotify //END_H2ZERO_MOD @@ -634,11 +600,9 @@ int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle, * @brief Read raw data from remote characteristic as hex bytes * @return return pointer data read */ - /* -uint8_t* BLERemoteCharacteristic::readRawData() { +uint8_t* NimBLERemoteCharacteristic::readRawData() { return m_rawData; } -*/ void NimBLERemoteCharacteristic::releaseSemaphores() { diff --git a/src/NimBLERemoteCharacteristic.h b/src/NimBLERemoteCharacteristic.h index fa19d14b..c107be79 100644 --- a/src/NimBLERemoteCharacteristic.h +++ b/src/NimBLERemoteCharacteristic.h @@ -59,7 +59,7 @@ class NimBLERemoteCharacteristic { bool writeValue(std::string newValue, bool response = false); bool writeValue(uint8_t newValue, bool response = false); std::string toString(); -// uint8_t* readRawData(); + uint8_t* readRawData(); NimBLERemoteService* getRemoteService(); private: @@ -90,7 +90,7 @@ class NimBLERemoteCharacteristic { FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt"); FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt"); std::string m_value; - //uint8_t *m_rawData = nullptr; + uint8_t* m_rawData = nullptr; notify_callback m_notifyCallback; // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 55f6c30d..404530df 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -40,6 +40,7 @@ static const char* LOG_TAG = "NimBLEScan"; * directed advertisement shall not be ignored if the InitA is a * resolvable private address. */ + //#define BLE_HCI_SCAN_FILT_NO_WL (0) //#define BLE_HCI_SCAN_FILT_USE_WL (1) //#define BLE_HCI_SCAN_FILT_NO_WL_INITA (2) diff --git a/src/NimBLESecurity.cpp b/src/NimBLESecurity.cpp index 7f2f04ed..69f0e84a 100644 --- a/src/NimBLESecurity.cpp +++ b/src/NimBLESecurity.cpp @@ -19,8 +19,9 @@ #include "NimBLEDevice.h" /** - * @brief These class methods are for backward compatibility with the bluedroid based library. + * @brief This class is for backward compatibility with the bluedroid based library. * Use the new security functions in NimBLEDevice instead. + * New callback functions in NimBLEServer and NimBLEClient. */ NimBLESecurity::NimBLESecurity() { diff --git a/src/NimBLESecurity.h b/src/NimBLESecurity.h index 562aad97..8a950b34 100644 --- a/src/NimBLESecurity.h +++ b/src/NimBLESecurity.h @@ -12,6 +12,9 @@ * Author: chegewara */ +// This class exists for backward compatibility - Should not be used in new code // +// See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient // + #ifndef COMPONENTS_NIMBLESECURITY_H_ #define COMPONENTS_NIMBLESECURITY_H_ #include "sdkconfig.h" @@ -23,11 +26,8 @@ #undef max /**************************/ - #include -//#include "esp_gap_ble_api.h" -/* relate to BTM_LE_AUTH_xxx in stack/btm_api.h */ #define ESP_LE_AUTH_NO_BOND 0x00 /*!< 0*/ /* relate to BTM_LE_AUTH_NO_BOND in stack/btm_api.h */ #define ESP_LE_AUTH_BOND 0x01 /*!< 1 << 0 */ /* relate to BTM_LE_AUTH_BOND in stack/btm_api.h */ #define ESP_LE_AUTH_REQ_MITM (1 << 2) /*!< 1 << 2 */ /* relate to BTM_LE_AUTH_REQ_MITM in stack/btm_api.h */ diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index def89fee..340d3a36 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -155,24 +155,11 @@ void NimBLEServer::start() { NimBLEService* pService = m_serviceMap.getFirst(); for(int i = 0; i < numSvcs; i++) { - uint8_t numChrs = pService->m_characteristicMap.getSize(); - //uint16_t chrHdl = 0xffff; - + uint8_t numChrs = pService->m_characteristicMap.getSize(); NimBLECharacteristic* pChr = pService->m_characteristicMap.getFirst(); if(pChr != nullptr) { for( int d = 0; d < numChrs; d++) { - // Not needed as NimBLE sets the handle for us - /*rc = ble_gatts_find_chr(&pService->getUUID().getNative()->u, - &pChr->getUUID().getNative()->u, NULL, &chrHdl); - if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "ble_gatts_find_chr: rc=%d, %s", rc, - NimBLEUtils::returnCodeToString(rc)); - abort(); - } - - pChr->setHandle(chrHdl); - */ // if Notify / Indicate is enabled but we didn't create the descriptor // we do it now. if((pChr->m_properties & BLE_GATT_CHR_F_INDICATE) || @@ -182,7 +169,7 @@ void NimBLEServer::start() { pChr->addDescriptor(new NimBLE2902()); } m_notifyChrMap.insert(std::pair - (pChr->getHandle() /*chrHdl*/, pChr)); + (pChr->getHandle(), pChr)); } pChr = pService->m_characteristicMap.getNext(); } @@ -309,10 +296,7 @@ uint32_t NimBLEServer::getConnectedCount() { return 0; } // BLE_GAP_EVENT_CONN_UPDATE - case BLE_GAP_EVENT_ENC_CHANGE: { - //if(client->m_conn_id != event->enc_change.conn_handle) - // return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE - + case BLE_GAP_EVENT_ENC_CHANGE: { struct ble_gap_conn_desc desc; int rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); if(rc != 0) { diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 3bd540ec..e8da9a29 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -47,8 +47,6 @@ NimBLEService::NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* m_uuid = uuid; m_handle = NULL_HANDLE; m_pServer = pServer; - //m_serializeMutex.setName("BLEService"); - m_lastCreatedCharacteristic = nullptr; m_numHandles = numHandles; } // NimBLEService @@ -61,7 +59,7 @@ void NimBLEService::dump() { NIMBLE_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x", m_uuid.toString().c_str(), m_handle); -// NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str()); } // dump @@ -283,18 +281,6 @@ std::string NimBLEService::toString() { } // toString -/** - * @brief Get the last created characteristic. - * It is lamentable that this function has to exist. It returns the last created characteristic. - * We need this because the descriptor API is built around the notion that a new descriptor, when created, - * is associated with the last characteristics created and we need that information. - * @return The last created characteristic. - */ -NimBLECharacteristic* NimBLEService::getLastCreatedCharacteristic() { - return m_lastCreatedCharacteristic; -} // getLastCreatedCharacteristic - - /** * @brief Get the BLE server associated with this service. * @return The BLEServer associated with this service. diff --git a/src/NimBLEService.h b/src/NimBLEService.h index b9156c97..09bd1d1b 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -17,8 +17,6 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -//#include - #include "NimBLEServer.h" #include "NimBLEUUID.h" #include "FreeRTOS.h" @@ -78,13 +76,10 @@ class NimBLEService { NimBLECharacteristicMap m_characteristicMap; uint16_t m_handle; - NimBLECharacteristic* m_lastCreatedCharacteristic = nullptr; NimBLEServer* m_pServer = nullptr; NimBLEUUID m_uuid; uint16_t m_numHandles; - - NimBLECharacteristic* getLastCreatedCharacteristic(); void setHandle(uint16_t handle); }; // BLEService From 0281973414fc3878651c7b0a9a1ffaf3afe3ada4 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 15 Mar 2020 13:50:24 -0600 Subject: [PATCH 24/62] Change sdkconfig to include Arduino.h so local differences won't conflict. Change NimBLELog.h to accomodate arduino serial monitor and log level settings. --- src/NimBLELog.h | 33 +++++- src/sdkconfig.h | 307 +----------------------------------------------- 2 files changed, 34 insertions(+), 306 deletions(-) diff --git a/src/NimBLELog.h b/src/NimBLELog.h index 2e8f8526..55aff696 100644 --- a/src/NimBLELog.h +++ b/src/NimBLELog.h @@ -12,12 +12,43 @@ #include "syscfg/syscfg.h" #include "modlog/modlog.h" - + +//If Arduino is being used, strip out the colors and ignore log printing below ui setting. +#ifdef ARDUINO_ARCH_ESP32 + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG +#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGD( tag, format, ... ) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "I %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGI( tag, format, ... ) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN +#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "W %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGW( tag, format, ... ) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) +#else +#define NIMBLE_LOGE( tag, format, ... ) +#endif + +#define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "CRIT %s: "#format"\n",tag,##__VA_ARGS__) + +#else #define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "\033[0;31mE %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "\033[0;33mW %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "\033[0;32mI %s: "#format"\033[0m\n",tag,##__VA_ARGS__) #define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__) #define NIMBLE_LOGC( tag, format, ... ) MODLOG_DFLT(CRITICAL, "\033[1;31mCRIT %s: "#format"\033[0m\n",tag,##__VA_ARGS__) +#endif /*ARDUINO_ARCH_ESP32*/ #endif /*CONFIG_BT_ENABLED*/ #endif /*MAIN_NIMBLELOG_H_*/ \ No newline at end of file diff --git a/src/sdkconfig.h b/src/sdkconfig.h index b4701d8d..26e583b3 100644 --- a/src/sdkconfig.h +++ b/src/sdkconfig.h @@ -1,18 +1,4 @@ -/* - * Automatically generated file. DO NOT EDIT. - * Espressif IoT Development Framework (ESP-IDF) Configuration Header - */ -#pragma once -#define CONFIG_IDF_TARGET "esp32" -#define CONFIG_SDK_TOOLPREFIX "xtensa-esp32-elf-" -#define CONFIG_SDK_PYTHON "python" -#define CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES 1 -#define CONFIG_APP_COMPILE_TIME_DATE 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL 3 -#define CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V 1 -#define CONFIG_BOOTLOADER_WDT_ENABLE 1 -#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 +#include "Arduino.h" #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1 #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 #define CONFIG_BT_NIMBLE_MAX_BONDS 3 @@ -36,293 +22,4 @@ #define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255 #define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70 #define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30 -#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 -#define CONFIG_ESPTOOLPY_PORT "/dev/ttyUSB0" -#define CONFIG_ESPTOOLPY_BAUD_115200B 1 -#define CONFIG_ESPTOOLPY_BAUD_OTHER_VAL 115200 -#define CONFIG_ESPTOOLPY_BAUD 115200 -#define CONFIG_ESPTOOLPY_COMPRESSED 1 -#define CONFIG_FLASHMODE_DIO 1 -#define CONFIG_ESPTOOLPY_FLASHMODE "dio" -#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 -#define CONFIG_ESPTOOLPY_FLASHFREQ "40m" -#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1 -#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB" -#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1 -#define CONFIG_ESPTOOLPY_BEFORE_RESET 1 -#define CONFIG_ESPTOOLPY_BEFORE "default_reset" -#define CONFIG_ESPTOOLPY_AFTER_RESET 1 -#define CONFIG_ESPTOOLPY_AFTER "hard_reset" -#define CONFIG_MONITOR_BAUD_115200B 1 -#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200 -#define CONFIG_MONITOR_BAUD 115200 -#define CONFIG_BLE_SM_IO_CAP_NO_IO 1 -#define CONFIG_EXAMPLE_IO_TYPE 3 -#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 -#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" -#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" -#define CONFIG_PARTITION_TABLE_OFFSET 0x8000 -#define CONFIG_PARTITION_TABLE_MD5 1 -#define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1 -#define CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED 1 -#define CONFIG_STACK_CHECK_NONE 1 -#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 -#define CONFIG_ESP32_APPTRACE_LOCK_ENABLE 1 -#define CONFIG_BT_ENABLED 1 -#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 -#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN 3 -#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF 3 -#define CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF 0 -#define CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF 0 -#define CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_0 1 -#define CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE 0 -#define CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI 1 -#define CONFIG_BTDM_CONTROLLER_MODEM_SLEEP 1 -#define CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG 1 -#define CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL 1 -#define CONFIG_BLE_SCAN_DUPLICATE 1 -#define CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR 1 -#define CONFIG_SCAN_DUPLICATE_TYPE 0 -#define CONFIG_DUPLICATE_SCAN_CACHE_SIZE 200 -#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_SUPPORTED 1 -#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_NUM 100 -#define CONFIG_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 -#define CONFIG_BT_RESERVE_DRAM 0xdb5c -#define CONFIG_ADC2_DISABLE_DAC 1 -#define CONFIG_SPI_MASTER_ISR_IN_IRAM 1 -#define CONFIG_SPI_SLAVE_ISR_IN_IRAM 1 -#define CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4 1 -#define CONFIG_EFUSE_MAX_BLK_LEN 192 -#define CONFIG_IDF_TARGET_ESP32 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_160 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 160 -#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 -#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 -#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 -#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 -#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 2304 -#define CONFIG_MAIN_TASK_STACK_SIZE 3584 -#define CONFIG_IPC_TASK_STACK_SIZE 1024 -#define CONFIG_TIMER_TASK_STACK_SIZE 3584 -#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1 -#define CONFIG_NEWLIB_STDIN_LINE_ENDING_CR 1 -#define CONFIG_CONSOLE_UART_DEFAULT 1 -#define CONFIG_CONSOLE_UART_NUM 0 -#define CONFIG_CONSOLE_UART_BAUDRATE 115200 -#define CONFIG_ULP_COPROC_RESERVE_MEM 0 -#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 -#define CONFIG_ESP32_DEBUG_OCDAWARE 1 -#define CONFIG_ESP32_DEBUG_STUBS_ENABLE 1 -#define CONFIG_INT_WDT 1 -#define CONFIG_INT_WDT_TIMEOUT_MS 300 -#define CONFIG_INT_WDT_CHECK_CPU1 1 -#define CONFIG_TASK_WDT 1 -#define CONFIG_TASK_WDT_TIMEOUT_S 5 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 1 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 1 -#define CONFIG_BROWNOUT_DET 1 -#define CONFIG_BROWNOUT_DET_LVL_SEL_0 1 -#define CONFIG_BROWNOUT_DET_LVL 0 -#define CONFIG_REDUCE_PHY_TX_POWER 1 -#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 -#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 -#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 -#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000 -#define CONFIG_ESP32_XTAL_FREQ_40 1 -#define CONFIG_ESP32_XTAL_FREQ 40 -#define CONFIG_ESP_ERR_TO_NAME_LOOKUP 1 -#define CONFIG_ADC_CAL_EFUSE_TP_ENABLE 1 -#define CONFIG_ADC_CAL_EFUSE_VREF_ENABLE 1 -#define CONFIG_ADC_CAL_LUT_ENABLE 1 -#define CONFIG_POST_EVENTS_FROM_ISR 1 -#define CONFIG_POST_EVENTS_FROM_IRAM_ISR 1 -#define CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS 1 -#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 512 -#define CONFIG_HTTPD_MAX_URI_LEN 512 -#define CONFIG_HTTPD_ERR_RESP_NO_DELAY 1 -#define CONFIG_SW_COEXIST_ENABLE 1 -#define CONFIG_SW_COEXIST_PREFERENCE_BALANCE 1 -#define CONFIG_SW_COEXIST_PREFERENCE_VALUE 2 -#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 -#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32 -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 -#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 -#define CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED 1 -#define CONFIG_ESP32_WIFI_TX_BA_WIN 6 -#define CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED 1 -#define CONFIG_ESP32_WIFI_RX_BA_WIN 6 -#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 -#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1 -#define CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN 752 -#define CONFIG_ESP32_WIFI_MGMT_SBUF_NUM 32 -#define CONFIG_ESP32_WIFI_IRAM_OPT 1 -#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 -#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20 -#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 -#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 -#define CONFIG_DMA_RX_BUF_NUM 10 -#define CONFIG_DMA_TX_BUF_NUM 10 -#define CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE 1 -#define CONFIG_EMAC_CHECK_LINK_PERIOD_MS 2000 -#define CONFIG_EMAC_TASK_PRIORITY 20 -#define CONFIG_EMAC_TASK_STACK_SIZE 3072 -#define CONFIG_FATFS_CODEPAGE_437 1 -#define CONFIG_FATFS_CODEPAGE 437 -#define CONFIG_FATFS_LFN_NONE 1 -#define CONFIG_FATFS_FS_LOCK 0 -#define CONFIG_FATFS_TIMEOUT_MS 10000 -#define CONFIG_FATFS_PER_FILE_CACHE 1 -#define CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND 150 -#define CONFIG_MB_MASTER_DELAY_MS_CONVERT 200 -#define CONFIG_MB_QUEUE_LENGTH 20 -#define CONFIG_MB_SERIAL_TASK_STACK_SIZE 2048 -#define CONFIG_MB_SERIAL_BUF_SIZE 256 -#define CONFIG_MB_SERIAL_TASK_PRIO 10 -#define CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT 20 -#define CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE 20 -#define CONFIG_MB_CONTROLLER_STACK_SIZE 4096 -#define CONFIG_MB_EVENT_QUEUE_TIMEOUT 20 -#define CONFIG_MB_TIMER_PORT_ENABLED 1 -#define CONFIG_MB_TIMER_GROUP 0 -#define CONFIG_MB_TIMER_INDEX 0 -#define CONFIG_FREERTOS_NO_AFFINITY 0x7FFFFFFF -#define CONFIG_FREERTOS_CORETIMER_0 1 -#define CONFIG_FREERTOS_HZ 100 -#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 -#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 -#define CONFIG_FREERTOS_INTERRUPT_BACKTRACE 1 -#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1 -#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 -#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1536 -#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 -#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 -#define CONFIG_TIMER_TASK_PRIORITY 1 -#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 -#define CONFIG_TIMER_QUEUE_LENGTH 10 -#define CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE 0 -#define CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER 1 -#define CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER 1 -#define CONFIG_HEAP_POISONING_DISABLED 1 -#define CONFIG_HEAP_TRACING_OFF 1 -#define CONFIG_LIBSODIUM_USE_MBEDTLS_SHA 1 -#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 -#define CONFIG_LOG_DEFAULT_LEVEL 3 -#define CONFIG_LOG_COLORS 1 -#define CONFIG_LWIP_MAX_SOCKETS 10 -#define CONFIG_LWIP_SO_REUSE 1 -#define CONFIG_LWIP_SO_REUSE_RXTOALL 1 -#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 -#define CONFIG_ESP_GRATUITOUS_ARP 1 -#define CONFIG_GARP_TMR_INTERVAL 60 -#define CONFIG_TCPIP_RECVMBOX_SIZE 32 -#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 -#define CONFIG_LWIP_DHCPS_LEASE_UNIT 60 -#define CONFIG_LWIP_DHCPS_MAX_STATION_NUM 8 -#define CONFIG_LWIP_NETIF_LOOPBACK 1 -#define CONFIG_LWIP_LOOPBACK_MAX_PBUFS 8 -#define CONFIG_LWIP_MAX_ACTIVE_TCP 16 -#define CONFIG_LWIP_MAX_LISTENING_TCP 16 -#define CONFIG_TCP_MAXRTX 12 -#define CONFIG_TCP_SYNMAXRTX 6 -#define CONFIG_TCP_MSS 1436 -#define CONFIG_TCP_MSL 60000 -#define CONFIG_TCP_SND_BUF_DEFAULT 5744 -#define CONFIG_TCP_WND_DEFAULT 5744 -#define CONFIG_TCP_RECVMBOX_SIZE 6 -#define CONFIG_TCP_QUEUE_OOSEQ 1 -#define CONFIG_TCP_OVERSIZE_MSS 1 -#define CONFIG_LWIP_MAX_UDP_PCBS 16 -#define CONFIG_UDP_RECVMBOX_SIZE 6 -#define CONFIG_TCPIP_TASK_STACK_SIZE 3072 -#define CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY 1 -#define CONFIG_TCPIP_TASK_AFFINITY 0x7FFFFFFF -#define CONFIG_LWIP_MAX_RAW_PCBS 16 -#define CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC 1 -#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 -#define CONFIG_MBEDTLS_HARDWARE_AES 1 -#define CONFIG_MBEDTLS_HAVE_TIME 1 -#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1 -#define CONFIG_MBEDTLS_TLS_SERVER 1 -#define CONFIG_MBEDTLS_TLS_CLIENT 1 -#define CONFIG_MBEDTLS_TLS_ENABLED 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA 1 -#define CONFIG_MBEDTLS_SSL_RENEGOTIATION 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 1 -#define CONFIG_MBEDTLS_SSL_ALPN 1 -#define CONFIG_MBEDTLS_SSL_SESSION_TICKETS 1 -#define CONFIG_MBEDTLS_AES_C 1 -#define CONFIG_MBEDTLS_RC4_DISABLED 1 -#define CONFIG_MBEDTLS_CCM_C 1 -#define CONFIG_MBEDTLS_GCM_C 1 -#define CONFIG_MBEDTLS_PEM_PARSE_C 1 -#define CONFIG_MBEDTLS_PEM_WRITE_C 1 -#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1 -#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1 -#define CONFIG_MBEDTLS_ECP_C 1 -#define CONFIG_MBEDTLS_ECDH_C 1 -#define CONFIG_MBEDTLS_ECDSA_C 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_NIST_OPTIM 1 -#define CONFIG_MDNS_MAX_SERVICES 10 -#define CONFIG_MQTT_PROTOCOL_311 1 -#define CONFIG_MQTT_TRANSPORT_SSL 1 -#define CONFIG_MQTT_TRANSPORT_WEBSOCKET 1 -#define CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE 1 -#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1 -#define CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT 5 -#define CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT 3072 -#define CONFIG_PTHREAD_STACK_MIN 768 -#define CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY 1 -#define CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT -1 -#define CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT "pthread" -#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 -#define CONFIG_SPIFFS_MAX_PARTITIONS 3 -#define CONFIG_SPIFFS_CACHE 1 -#define CONFIG_SPIFFS_CACHE_WR 1 -#define CONFIG_SPIFFS_PAGE_CHECK 1 -#define CONFIG_SPIFFS_GC_MAX_RUNS 10 -#define CONFIG_SPIFFS_PAGE_SIZE 256 -#define CONFIG_SPIFFS_OBJ_NAME_LEN 32 -#define CONFIG_SPIFFS_USE_MAGIC 1 -#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1 -#define CONFIG_SPIFFS_META_LENGTH 4 -#define CONFIG_SPIFFS_USE_MTIME 1 -#define CONFIG_IP_LOST_TIMER_INTERVAL 120 -#define CONFIG_TCPIP_LWIP 1 -#define CONFIG_UNITY_ENABLE_FLOAT 1 -#define CONFIG_UNITY_ENABLE_DOUBLE 1 -#define CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER 1 -#define CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT 1 -#define CONFIG_SUPPORT_TERMIOS 1 -#define CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS 1 -#define CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN 128 -#define CONFIG_WL_SECTOR_SIZE_4096 1 -#define CONFIG_WL_SECTOR_SIZE 4096 - -#define CONFIG_BTDM_CONTROLLER_BR_EDR_SCO_DATA_PATH_EFF 1 -#define CONFIG_ARDUINO_EVENT_RUNNING_CORE 1 - -/* List of deprecated options */ -#define CONFIG_MAKE_WARN_UNDEFINED_VARIABLES CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES -#define CONFIG_PYTHON CONFIG_SDK_PYTHON -#define CONFIG_TOOLPREFIX CONFIG_SDK_TOOLPREFIX +#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 \ No newline at end of file From 18f05d4c0bdd48911c5a1642a5b9bce7752ebb81 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 15 Mar 2020 20:21:56 -0600 Subject: [PATCH 25/62] Added beacon code for eddystone and ibeacon. Implemented compatibility functions in advertising class for original examples to compile. Added / edited original examples. --- examples/BLE_iBeacon/BLE_iBeacon.ino | 103 +++++ examples/BLE_notify/BLE_notify.ino | 110 +++++ examples/BLE_scan/BLE_scan.ino | 40 ++ examples/BLE_server/BLE_server.ino | 45 ++ .../BLE_server_multiconnect.ino | 111 +++++ examples/BLE_uart/BLE_uart.ino | 125 ++++++ examples/BLE_write/BLE_write.ino | 65 +++ src/HIDKeyboardTypes.h | 402 ++++++++++++++++++ src/HIDTypes.h | 96 +++++ src/NimBLEAdvertising.cpp | 15 +- src/NimBLEAdvertising.h | 4 +- src/NimBLEBeacon.cpp | 92 ++++ src/NimBLEBeacon.h | 50 +++ src/NimBLEDevice.h | 3 + src/NimBLEEddystoneTLM.cpp | 140 ++++++ src/NimBLEEddystoneTLM.h | 60 +++ src/NimBLEEddystoneURL.cpp | 157 +++++++ src/NimBLEEddystoneURL.h | 52 +++ 18 files changed, 1661 insertions(+), 9 deletions(-) create mode 100644 examples/BLE_iBeacon/BLE_iBeacon.ino create mode 100644 examples/BLE_notify/BLE_notify.ino create mode 100644 examples/BLE_scan/BLE_scan.ino create mode 100644 examples/BLE_server/BLE_server.ino create mode 100644 examples/BLE_server_multiconnect/BLE_server_multiconnect.ino create mode 100644 examples/BLE_uart/BLE_uart.ino create mode 100644 examples/BLE_write/BLE_write.ino create mode 100644 src/HIDKeyboardTypes.h create mode 100644 src/HIDTypes.h create mode 100644 src/NimBLEBeacon.cpp create mode 100644 src/NimBLEBeacon.h create mode 100644 src/NimBLEEddystoneTLM.cpp create mode 100644 src/NimBLEEddystoneTLM.h create mode 100644 src/NimBLEEddystoneURL.cpp create mode 100644 src/NimBLEEddystoneURL.h diff --git a/examples/BLE_iBeacon/BLE_iBeacon.ino b/examples/BLE_iBeacon/BLE_iBeacon.ino new file mode 100644 index 00000000..b93c4b27 --- /dev/null +++ b/examples/BLE_iBeacon/BLE_iBeacon.ino @@ -0,0 +1,103 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by pcbreflux +*/ + + +/* + Create a BLE server that will send periodic iBeacon frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ +#include "sys/time.h" + +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" +#include "NimBLEBeacon.h" +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +#ifdef __cplusplus +extern "C" { +#endif + +uint8_t temprature_sens_read(); +//uint8_t g_phyFuns; + +#ifdef __cplusplus +} +#endif + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +void setBeacon() { + + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setManufacturerId(0x4C00); // fake Apple 0x004C LSB (ENDIAN_CHANGE_U16!) + oBeacon.setProximityUUID(BLEUUID(BEACON_UUID)); + oBeacon.setMajor((bootcount & 0xFFFF0000) >> 16); + oBeacon.setMinor(bootcount&0xFFFF); + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04 + + std::string strServiceData = ""; + + strServiceData += (char)26; // Len + strServiceData += (char)0xFF; // Type + strServiceData += oBeacon.getData(); + oAdvertisementData.addData(strServiceData); + + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); + +} + +void setup() { + + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n",bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n",now.tv_sec,now.tv_sec-last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init(""); + + // Create the BLE Server + // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(100); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() { +} diff --git a/examples/BLE_notify/BLE_notify.ino b/examples/BLE_notify/BLE_notify.ino new file mode 100644 index 00000000..396a60af --- /dev/null +++ b/examples/BLE_notify/BLE_notify.ino @@ -0,0 +1,110 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ +#include +#include +#include +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + pCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} \ No newline at end of file diff --git a/examples/BLE_scan/BLE_scan.ino b/examples/BLE_scan/BLE_scan.ino new file mode 100644 index 00000000..a0725164 --- /dev/null +++ b/examples/BLE_scan/BLE_scan.ino @@ -0,0 +1,40 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include +#include + +int scanTime = 5; //In seconds +BLEScan* pBLEScan; + +class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice* advertisedDevice) { + Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() { + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} \ No newline at end of file diff --git a/examples/BLE_server/BLE_server.ino b/examples/BLE_server/BLE_server.ino new file mode 100644 index 00000000..3106c0e2 --- /dev/null +++ b/examples/BLE_server/BLE_server.ino @@ -0,0 +1,45 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp + Ported to Arduino ESP32 by Evandro Copercini + updates by chegewara +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + BLEDevice::init("Long name works now"); + BLEServer *pServer = BLEDevice::createServer(); + BLEService *pService = pServer->createService(SERVICE_UUID); + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setValue("Hello World says Neil"); + pService->start(); + // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino new file mode 100644 index 00000000..622678bf --- /dev/null +++ b/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -0,0 +1,111 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + updated by chegewara + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8 + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + A connect hander associated with the server starts a background task that performs notification + every couple of seconds. +*/ +#include +#include +#include +#include + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint32_t value = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + BLEDevice::startAdvertising(); + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("ESP32"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor + pCharacteristic->addDescriptor(new BLE2902()); + + // Start the service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(false); + pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter + BLEDevice::startAdvertising(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + // notify changed value + if (deviceConnected) { + pCharacteristic->setValue((uint8_t*)&value, 4); + pCharacteristic->notify(); + value++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms + } + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/examples/BLE_uart/BLE_uart.ino b/examples/BLE_uart/BLE_uart.ino new file mode 100644 index 00000000..08d8d2f9 --- /dev/null +++ b/examples/BLE_uart/BLE_uart.ino @@ -0,0 +1,125 @@ +/* + Video: https://www.youtube.com/watch?v=oCMOYS71NIU + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp + Ported to Arduino ESP32 by Evandro Copercini + + Create a BLE server that, once we receive a connection, will send periodic notifications. + The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE" + Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY" + + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create a BLE Service + 3. Create a BLE Characteristic on the Service + 4. Create a BLE Descriptor on the characteristic + 5. Start the service. + 6. Start advertising. + + In this example rxValue is the data received (only accessible inside that function). + And txValue is the data to be sent, in this example just a byte incremented every second. +*/ +#include +#include +#include +#include + +BLEServer *pServer = NULL; +BLECharacteristic * pTxCharacteristic; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t txValue = 0; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID +#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" +#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string rxValue = pCharacteristic->getValue(); + + if (rxValue.length() > 0) { + Serial.println("*********"); + Serial.print("Received Value: "); + for (int i = 0; i < rxValue.length(); i++) + Serial.print(rxValue[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + + +void setup() { + Serial.begin(115200); + + // Create the BLE Device + BLEDevice::init("UART Service"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pTxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_TX, + BLECharacteristic::PROPERTY_NOTIFY + ); + + pTxCharacteristic->addDescriptor(new BLE2902()); + + BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID_RX, + BLECharacteristic::PROPERTY_WRITE + ); + + pRxCharacteristic->setCallbacks(new MyCallbacks()); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->start(); + Serial.println("Waiting a client connection to notify..."); +} + +void loop() { + + if (deviceConnected) { + pTxCharacteristic->setValue(&txValue, 1); + pTxCharacteristic->notify(); + txValue++; + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } + + // disconnecting + if (!deviceConnected && oldDeviceConnected) { + delay(500); // give the bluetooth stack the chance to get things ready + pServer->startAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} diff --git a/examples/BLE_write/BLE_write.ino b/examples/BLE_write/BLE_write.ino new file mode 100644 index 00000000..7aa2c99a --- /dev/null +++ b/examples/BLE_write/BLE_write.ino @@ -0,0 +1,65 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyCallbacks: public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + std::string value = pCharacteristic->getValue(); + + if (value.length() > 0) { + Serial.println("*********"); + Serial.print("New value: "); + for (int i = 0; i < value.length(); i++) + Serial.print(value[i]); + + Serial.println(); + Serial.println("*********"); + } + } +}; + +void setup() { + Serial.begin(115200); + + Serial.println("1- Download and install an BLE scanner app in your phone"); + Serial.println("2- Scan for BLE devices in the app"); + Serial.println("3- Connect to MyESP32"); + Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); + Serial.println("5- See the magic =)"); + + BLEDevice::init("MyESP32"); + BLEServer *pServer = BLEDevice::createServer(); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + BLECharacteristic *pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + + pCharacteristic->setCallbacks(new MyCallbacks()); + + pCharacteristic->setValue("Hello World"); + pService->start(); + + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->start(); +} + +void loop() { + // put your main code here, to run repeatedly: + delay(2000); +} \ No newline at end of file diff --git a/src/HIDKeyboardTypes.h b/src/HIDKeyboardTypes.h new file mode 100644 index 00000000..4e221d57 --- /dev/null +++ b/src/HIDKeyboardTypes.h @@ -0,0 +1,402 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif diff --git a/src/HIDTypes.h b/src/HIDTypes.h new file mode 100644 index 00000000..64850ef8 --- /dev/null +++ b/src/HIDTypes.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#ifdef ARDUINO_ARCH_ESP32 +#define HIDINPUT(size) (0x80 | size) +#define HIDOUTPUT(size) (0x90 | size) +#else +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#endif +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) //bits +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) //bytes +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index d7605e96..4cb6cb94 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -91,15 +91,16 @@ void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) { m_advParams.itvl_max = maxinterval; } // setMaxInterval -/* -void BLEAdvertising::setMinPreferred(uint16_t mininterval) { - m_advData.min_interval = mininterval; + +// These are dummy functions for now for compatibility +void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) { + //m_advData.min_interval = mininterval; } // -void BLEAdvertising::setMaxPreferred(uint16_t maxinterval) { - m_advData.max_interval = maxinterval; +void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) { + //m_advData.max_interval = maxinterval; } // -*/ +////////////////////////////////////////////////////////// void NimBLEAdvertising::setScanResponse(bool set) { m_scanResp = set; @@ -463,7 +464,7 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) { char cdata[3]; cdata[0] = 2; cdata[1] = BLE_HS_ADV_TYPE_FLAGS; // 0x01 - cdata[2] = (flag | BLE_HS_ADV_F_BREDR_UNSUP); + cdata[2] = flag | BLE_HS_ADV_F_BREDR_UNSUP; addData(std::string(cdata, 3)); } // setFlag diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 617950f8..812735e2 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -79,8 +79,8 @@ class NimBLEAdvertising { void setScanResponseData(NimBLEAdvertisementData& advertisementData); void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM); -// void setMinPreferred(uint16_t); -// void setMaxPreferred(uint16_t); + void setMinPreferred(uint16_t); + void setMaxPreferred(uint16_t); void setScanResponse(bool); private: diff --git a/src/NimBLEBeacon.cpp b/src/NimBLEBeacon.cpp new file mode 100644 index 00000000..d9f32aee --- /dev/null +++ b/src/NimBLEBeacon.cpp @@ -0,0 +1,92 @@ +/* + * NimBLEBeacon2.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon.cpp + * + * Created on: Jan 4, 2018 + * Author: kolban + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) +#include +#include "NimBLEBeacon.h" +#include "NimBLELog.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + +static const char* LOG_TAG = "NimBLEBeacon"; + +NimBLEBeacon::NimBLEBeacon() { + m_beaconData.manufacturerId = 0x4c00; + m_beaconData.subType = 0x02; + m_beaconData.subTypeLength = 0x15; + m_beaconData.major = 0; + m_beaconData.minor = 0; + m_beaconData.signalPower = 0; + memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID)); +} // NimBLEBeacon + +std::string NimBLEBeacon::getData() { + return std::string((char*) &m_beaconData, sizeof(m_beaconData)); +} // getData + +uint16_t NimBLEBeacon::getMajor() { + return m_beaconData.major; +} + +uint16_t NimBLEBeacon::getManufacturerId() { + return m_beaconData.manufacturerId; +} + +uint16_t NimBLEBeacon::getMinor() { + return m_beaconData.minor; +} + +NimBLEUUID NimBLEBeacon::getProximityUUID() { + return NimBLEUUID(m_beaconData.proximityUUID, 16, false); +} + +int8_t NimBLEBeacon::getSignalPower() { + return m_beaconData.signalPower; +} + +/** + * Set the raw data for the beacon record. + */ +void NimBLEBeacon::setData(std::string data) { + if (data.length() != sizeof(m_beaconData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_beaconData)); + return; + } + memcpy(&m_beaconData, data.data(), sizeof(m_beaconData)); +} // setData + +void NimBLEBeacon::setMajor(uint16_t major) { + m_beaconData.major = ENDIAN_CHANGE_U16(major); +} // setMajor + +void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) { + m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId); +} // setManufacturerId + +void NimBLEBeacon::setMinor(uint16_t minor) { + m_beaconData.minor = ENDIAN_CHANGE_U16(minor); +} // setMinior + +void NimBLEBeacon::setProximityUUID(NimBLEUUID uuid) { + uuid = uuid.to128(); + memcpy(m_beaconData.proximityUUID, uuid.getNative()->u128.value, 16); +} // setProximityUUID + +void NimBLEBeacon::setSignalPower(int8_t signalPower) { + m_beaconData.signalPower = signalPower; +} // setSignalPower + + +#endif diff --git a/src/NimBLEBeacon.h b/src/NimBLEBeacon.h new file mode 100644 index 00000000..c0c15c8e --- /dev/null +++ b/src/NimBLEBeacon.h @@ -0,0 +1,50 @@ +/* + * NimBLEBeacon2.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEBeacon2.h + * + * Created on: Jan 4, 2018 + * Author: kolban + */ + +#ifndef MAIN_NIMBLEBEACON_H_ +#define MAIN_NIMBLEBEACON_H_ +#include "NimBLEUUID.h" +/** + * @brief Representation of a beacon. + * See: + * * https://en.wikipedia.org/wiki/IBeacon + */ +class NimBLEBeacon { +private: + struct { + uint16_t manufacturerId; + uint8_t subType; + uint8_t subTypeLength; + uint8_t proximityUUID[16]; + uint16_t major; + uint16_t minor; + int8_t signalPower; + } __attribute__((packed)) m_beaconData; +public: + NimBLEBeacon(); + std::string getData(); + uint16_t getMajor(); + uint16_t getMinor(); + uint16_t getManufacturerId(); + NimBLEUUID getProximityUUID(); + int8_t getSignalPower(); + void setData(std::string data); + void setMajor(uint16_t major); + void setMinor(uint16_t minor); + void setManufacturerId(uint16_t manufacturerId); + void setProximityUUID(NimBLEUUID uuid); + void setSignalPower(int8_t signalPower); +}; // NimBLEBeacon + +#endif /* MAIN_NIMBLEBEACON_H_ */ diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 9de0cba4..12dff9dd 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -55,6 +55,9 @@ #define BLE2902 NimBLE2902 #define BLE2904 NimBLE2904 #define BLEDescriptorCallbacks NimBLEDescriptorCallbacks +#define BLEBeacon NimBLEBeacon +#define BLEEddystoneTLM NimBLEEddystoneTLM +#define BLEEddystoneURL NimBLEEddystoneURL /** diff --git a/src/NimBLEEddystoneTLM.cpp b/src/NimBLEEddystoneTLM.cpp new file mode 100644 index 00000000..f8d724c1 --- /dev/null +++ b/src/NimBLEEddystoneTLM.cpp @@ -0,0 +1,140 @@ +/* + * NimBLEEddystoneTLM.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneTLM.h" +#include "NimBLELog.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) +#define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) + +static const char LOG_TAG[] = "NimBLEEddystoneTLM"; + + +NimBLEEddystoneTLM::NimBLEEddystoneTLM() { + beaconUUID = 0xFEAA; + m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; + m_eddystoneData.version = 0; + m_eddystoneData.volt = 3300; // 3300mV = 3.3V + m_eddystoneData.temp = (uint16_t) ((float) 23.00); + m_eddystoneData.advCount = 0; + m_eddystoneData.tmil = 0; +} // NimBLEEddystoneTLM + +std::string NimBLEEddystoneTLM::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +NimBLEUUID NimBLEEddystoneTLM::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + +uint8_t NimBLEEddystoneTLM::getVersion() { + return m_eddystoneData.version; +} // getVersion + +uint16_t NimBLEEddystoneTLM::getVolt() { + return m_eddystoneData.volt; +} // getVolt + +float NimBLEEddystoneTLM::getTemp() { + return (float)m_eddystoneData.temp; +} // getTemp + +uint32_t NimBLEEddystoneTLM::getCount() { + return m_eddystoneData.advCount; +} // getCount + +uint32_t NimBLEEddystoneTLM::getTime() { + return m_eddystoneData.tmil; +} // getTime + +std::string NimBLEEddystoneTLM::toString() { + std::string out = ""; + uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); + char val[6]; + + out += "Version " + m_eddystoneData.version; + out += "\n"; + out += "Battery Voltage " + ENDIAN_CHANGE_U16(m_eddystoneData.volt); + out += " mV\n"; + + out += "Temperature "; + snprintf(val, sizeof(val), "%d", m_eddystoneData.temp); + out += val; + out += ".0 °C\n"; + + out += "Adv. Count "; + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); + out += val; + out += "\n"; + + out += "Time "; + + snprintf(val, sizeof(val), "%04d", rawsec / 864000); + out += val; + out += "."; + + snprintf(val, sizeof(val), "%02d", (rawsec / 36000) % 24); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02d", (rawsec / 600) % 60); + out += val; + out += ":"; + + snprintf(val, sizeof(val), "%02d", (rawsec / 10) % 60); + out += val; + out += "\n"; + + return out; +} // toString + +/** + * Set the raw data for the beacon record. + */ +void NimBLEEddystoneTLM::setData(std::string data) { + if (data.length() != sizeof(m_eddystoneData)) { + log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData)); + return; + } + memcpy(&m_eddystoneData, data.data(), data.length()); +} // setData + +void NimBLEEddystoneTLM::setUUID(NimBLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + +void NimBLEEddystoneTLM::setVersion(uint8_t version) { + m_eddystoneData.version = version; +} // setVersion + +void NimBLEEddystoneTLM::setVolt(uint16_t volt) { + m_eddystoneData.volt = volt; +} // setVolt + +void NimBLEEddystoneTLM::setTemp(float temp) { + m_eddystoneData.temp = (uint16_t)temp; +} // setTemp + +void NimBLEEddystoneTLM::setCount(uint32_t advCount) { + m_eddystoneData.advCount = advCount; +} // setCount + +void NimBLEEddystoneTLM::setTime(uint32_t tmil) { + m_eddystoneData.tmil = tmil; +} // setTime + +#endif diff --git a/src/NimBLEEddystoneTLM.h b/src/NimBLEEddystoneTLM.h new file mode 100644 index 00000000..3c7219eb --- /dev/null +++ b/src/NimBLEEddystoneTLM.h @@ -0,0 +1,60 @@ +/* + * NimBLEEddystoneTLM.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneTLM.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NimBLEEddystoneTLM_H_ +#define _NimBLEEddystoneTLM_H_ +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_TLM_FRAME_TYPE 0x20 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneTLM { +public: + NimBLEEddystoneTLM(); + std::string getData(); + NimBLEUUID getUUID(); + uint8_t getVersion(); + uint16_t getVolt(); + float getTemp(); + uint32_t getCount(); + uint32_t getTime(); + std::string toString(); + void setData(std::string data); + void setUUID(NimBLEUUID l_uuid); + void setVersion(uint8_t version); + void setVolt(uint16_t volt); + void setTemp(float temp); + void setCount(uint32_t advCount); + void setTime(uint32_t tmil); + +private: + uint16_t beaconUUID; + struct { + uint8_t frameType; + uint8_t version; + uint16_t volt; + uint16_t temp; + uint32_t advCount; + uint32_t tmil; + } __attribute__((packed)) m_eddystoneData; + +}; // NimBLEEddystoneTLM + +#endif /* _NimBLEEddystoneTLM_H_ */ diff --git a/src/NimBLEEddystoneURL.cpp b/src/NimBLEEddystoneURL.cpp new file mode 100644 index 00000000..82d07135 --- /dev/null +++ b/src/NimBLEEddystoneURL.cpp @@ -0,0 +1,157 @@ +/* + * NimBLEEddystoneURL.cpp + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.cpp + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "NimBLEEddystoneURL.h" +#include "NimBLELog.h" + +static const char LOG_TAG[] = "NimBLEEddystoneURL"; + +NimBLEEddystoneURL::NimBLEEddystoneURL() { + beaconUUID = 0xFEAA; + lengthURL = 0; + m_eddystoneData.frameType = EDDYSTONE_URL_FRAME_TYPE; + m_eddystoneData.advertisedTxPower = 0; + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); +} // BLEEddystoneURL + +std::string NimBLEEddystoneURL::getData() { + return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData)); +} // getData + +NimBLEUUID NimBLEEddystoneURL::getUUID() { + return NimBLEUUID(beaconUUID); +} // getUUID + +int8_t NimBLEEddystoneURL::getPower() { + return m_eddystoneData.advertisedTxPower; +} // getPower + +std::string NimBLEEddystoneURL::getURL() { + return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url)); +} // getURL + +std::string NimBLEEddystoneURL::getDecodedURL() { + std::string decodedURL = ""; + + switch (m_eddystoneData.url[0]) { + case 0x00: + decodedURL += "http://www."; + break; + case 0x01: + decodedURL += "https://www."; + break; + case 0x02: + decodedURL += "http://"; + break; + case 0x03: + decodedURL += "https://"; + break; + default: + decodedURL += m_eddystoneData.url[0]; + } + + for (int i = 1; i < lengthURL; i++) { + if (m_eddystoneData.url[i] > 33 && m_eddystoneData.url[i] < 127) { + decodedURL += m_eddystoneData.url[i]; + } else { + switch (m_eddystoneData.url[i]) { + case 0x00: + decodedURL += ".com/"; + break; + case 0x01: + decodedURL += ".org/"; + break; + case 0x02: + decodedURL += ".edu/"; + break; + case 0x03: + decodedURL += ".net/"; + break; + case 0x04: + decodedURL += ".info/"; + break; + case 0x05: + decodedURL += ".biz/"; + break; + case 0x06: + decodedURL += ".gov/"; + break; + case 0x07: + decodedURL += ".com"; + break; + case 0x08: + decodedURL += ".org"; + break; + case 0x09: + decodedURL += ".edu"; + break; + case 0x0A: + decodedURL += ".net"; + break; + case 0x0B: + decodedURL += ".info"; + break; + case 0x0C: + decodedURL += ".biz"; + break; + case 0x0D: + decodedURL += ".gov"; + break; + default: + break; + } + } + } + return decodedURL; +} // getDecodedURL + + + +/** + * Set the raw data for the beacon record. + */ +void NimBLEEddystoneURL::setData(std::string data) { + if (data.length() > sizeof(m_eddystoneData)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and max expected %d", + data.length(), sizeof(m_eddystoneData)); + return; + } + memset(&m_eddystoneData, 0, sizeof(m_eddystoneData)); + memcpy(&m_eddystoneData, data.data(), data.length()); + lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url)); +} // setData + +void NimBLEEddystoneURL::setUUID(NimBLEUUID l_uuid) { + beaconUUID = l_uuid.getNative()->u16.value; +} // setUUID + +void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) { + m_eddystoneData.advertisedTxPower = advertisedTxPower; +} // setPower + +void NimBLEEddystoneURL::setURL(std::string url) { + if (url.length() > sizeof(m_eddystoneData.url)) { + NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d", + url.length(), sizeof(m_eddystoneData.url)); + return; + } + memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url)); + memcpy(m_eddystoneData.url, url.data(), url.length()); + lengthURL = url.length(); +} // setURL + + +#endif diff --git a/src/NimBLEEddystoneURL.h b/src/NimBLEEddystoneURL.h new file mode 100644 index 00000000..2e135886 --- /dev/null +++ b/src/NimBLEEddystoneURL.h @@ -0,0 +1,52 @@ +/* + * NimBLEEddystoneURL.h + * + * Created: on March 15 2020 + * Author H2zero + * + * Originally: + * + * BLEEddystoneURL.h + * + * Created on: Mar 12, 2018 + * Author: pcbreflux + */ + +#ifndef _NIMBLEEddystoneURL_H_ +#define _NIMBLEEddystoneURL_H_ +#include "NimBLEUUID.h" + +#include + +#define EDDYSTONE_URL_FRAME_TYPE 0x10 + +/** + * @brief Representation of a beacon. + * See: + * * https://github.com/google/eddystone + */ +class NimBLEEddystoneURL { +public: + NimBLEEddystoneURL(); + std::string getData(); + NimBLEUUID getUUID(); + int8_t getPower(); + std::string getURL(); + std::string getDecodedURL(); + void setData(std::string data); + void setUUID(NimBLEUUID l_uuid); + void setPower(int8_t advertisedTxPower); + void setURL(std::string url); + +private: + uint16_t beaconUUID; + uint8_t lengthURL; + struct { + uint8_t frameType; + int8_t advertisedTxPower; + uint8_t url[16]; + } __attribute__((packed)) m_eddystoneData; + +}; // NIMBLEEddystoneURL + +#endif /* _NIMBLEEddystoneURL_H_ */ From 8cbf91ed81af491e71d9c3d80036936fe47f998c Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 15 Mar 2020 22:54:50 -0600 Subject: [PATCH 26/62] Refactor characteristic properties. Fix eddystone issues. Refactor update connection params, Refactor examples. --- examples/BLE_notify/BLE_notify.ino | 8 ++++---- examples/BLE_server/BLE_server.ino | 4 ++-- .../BLE_server_multiconnect.ino | 8 ++++---- examples/BLE_uart/BLE_uart.ino | 4 ++-- examples/BLE_write/BLE_write.ino | 4 ++-- src/NimBLECharacteristic.cpp | 3 ++- src/NimBLECharacteristic.h | 15 ++++++++++++--- src/NimBLEEddystoneTLM.cpp | 5 ++++- src/NimBLEEddystoneURL.cpp | 2 ++ src/NimBLEServer.cpp | 4 ++-- src/NimBLEServer.h | 2 +- 11 files changed, 37 insertions(+), 22 deletions(-) diff --git a/examples/BLE_notify/BLE_notify.ino b/examples/BLE_notify/BLE_notify.ino index 396a60af..55051eb1 100644 --- a/examples/BLE_notify/BLE_notify.ino +++ b/examples/BLE_notify/BLE_notify.ino @@ -65,10 +65,10 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_INDICATE + PROPERTY_READ | + PROPERTY_WRITE | + PROPERTY_NOTIFY | + PROPERTY_INDICATE ); // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml diff --git a/examples/BLE_server/BLE_server.ino b/examples/BLE_server/BLE_server.ino index 3106c0e2..21383f09 100644 --- a/examples/BLE_server/BLE_server.ino +++ b/examples/BLE_server/BLE_server.ino @@ -23,8 +23,8 @@ void setup() { BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE + PROPERTY_READ | + PROPERTY_WRITE ); pCharacteristic->setValue("Hello World says Neil"); diff --git a/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino index 622678bf..f6a76b4d 100644 --- a/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino +++ b/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -66,10 +66,10 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_INDICATE + PROPERTY_READ | + PROPERTY_WRITE | + PROPERTY_NOTIFY | + PROPERTY_INDICATE ); // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml diff --git a/examples/BLE_uart/BLE_uart.ino b/examples/BLE_uart/BLE_uart.ino index 08d8d2f9..882bb71b 100644 --- a/examples/BLE_uart/BLE_uart.ino +++ b/examples/BLE_uart/BLE_uart.ino @@ -81,14 +81,14 @@ void setup() { // Create a BLE Characteristic pTxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_TX, - BLECharacteristic::PROPERTY_NOTIFY + PROPERTY_NOTIFY ); pTxCharacteristic->addDescriptor(new BLE2902()); BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_RX, - BLECharacteristic::PROPERTY_WRITE + PROPERTY_WRITE ); pRxCharacteristic->setCallbacks(new MyCallbacks()); diff --git a/examples/BLE_write/BLE_write.ino b/examples/BLE_write/BLE_write.ino index 7aa2c99a..ed7fecfe 100644 --- a/examples/BLE_write/BLE_write.ino +++ b/examples/BLE_write/BLE_write.ino @@ -46,8 +46,8 @@ void setup() { BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE + PROPERTY_READ | + PROPERTY_WRITE ); pCharacteristic->setCallbacks(new MyCallbacks()); diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index c0ec90ed..44fc8530 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -47,12 +47,13 @@ NimBLECharacteristic::NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties, m_pCallbacks = &defaultCallback; m_pService = pService; // Backward Compatibility - to be removed - setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); +/* setBroadcastProperty((properties & PROPERTY_BROADCAST) != 0); setReadProperty((properties & PROPERTY_READ) != 0); setWriteProperty((properties & PROPERTY_WRITE) != 0); setNotifyProperty((properties & PROPERTY_NOTIFY) != 0); setIndicateProperty((properties & PROPERTY_INDICATE) != 0); setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) != 0); +*/ /////////////////////////////////////////// } // NimBLECharacteristic diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index dc05c2c9..abaf9020 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -27,6 +27,16 @@ #include #include +#define PROPERTY_READ BLE_GATT_CHR_F_READ +#define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC +#define PROPERTY_NOTIFY BLE_GATT_CHR_F_NOTIFY +#define PROPERTY_WRITE BLE_GATT_CHR_F_WRITE +#define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC +#define PROPERTY_BROADCAST BLE_GATT_CHR_F_BROADCAST +#define PROPERTY_INDICATE BLE_GATT_CHR_F_INDICATE +#define PROPERTY_WRITE_NR BLE_GATT_CHR_F_WRITE_NO_RSP + + class NimBLEService; class NimBLEDescriptor; class NimBLECharacteristicCallbacks; @@ -93,15 +103,14 @@ class NimBLECharacteristic { void setAccessPermissions(uint16_t perm); // Backward Compatibility - to be removed - static const uint32_t PROPERTY_READ = 1<<0; +/* static const uint32_t PROPERTY_READ = 1<<0; static const uint32_t PROPERTY_WRITE = 1<<1; static const uint32_t PROPERTY_NOTIFY = 1<<2; static const uint32_t PROPERTY_BROADCAST = 1<<3; static const uint32_t PROPERTY_INDICATE = 1<<4; static const uint32_t PROPERTY_WRITE_NR = 1<<5; +*/ ////////////////////////////////////////////////////// - #define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC - #define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC private: diff --git a/src/NimBLEEddystoneTLM.cpp b/src/NimBLEEddystoneTLM.cpp index f8d724c1..de9effd2 100644 --- a/src/NimBLEEddystoneTLM.cpp +++ b/src/NimBLEEddystoneTLM.cpp @@ -17,6 +17,8 @@ #include "NimBLEEddystoneTLM.h" #include "NimBLELog.h" +#include + #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) #define ENDIAN_CHANGE_U32(x) ((((x)&0xFF000000)>>24) + (((x)&0x00FF0000)>>8)) + ((((x)&0xFF00)<<8) + (((x)&0xFF)<<24)) @@ -107,7 +109,8 @@ std::string NimBLEEddystoneTLM::toString() { */ void NimBLEEddystoneTLM::setData(std::string data) { if (data.length() != sizeof(m_eddystoneData)) { - log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_eddystoneData)); + NIMBLE_LOGE(LOG_TAG, "Unable to set the data ... length passed in was %d and expected %d", + data.length(), sizeof(m_eddystoneData)); return; } memcpy(&m_eddystoneData, data.data(), data.length()); diff --git a/src/NimBLEEddystoneURL.cpp b/src/NimBLEEddystoneURL.cpp index 82d07135..c1d2aff6 100644 --- a/src/NimBLEEddystoneURL.cpp +++ b/src/NimBLEEddystoneURL.cpp @@ -17,6 +17,8 @@ #include "NimBLEEddystoneURL.h" #include "NimBLELog.h" +#include + static const char LOG_TAG[] = "NimBLEEddystoneURL"; NimBLEEddystoneURL::NimBLEEddystoneURL() { diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 340d3a36..d11adfd3 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -515,7 +515,7 @@ void NimBLEServer::removePeerDevice(uint16_t conn_id, bool _client) { /** * Update connection parameters can be called only after connection has been established */ -void NimBLEServer::updateConnParams(ble_gap_conn_desc* desc, +void NimBLEServer::updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, uint16_t minConnTime, uint16_t maxConnTime) @@ -529,7 +529,7 @@ void NimBLEServer::updateConnParams(ble_gap_conn_desc* desc, params.min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units - int rc = ble_gap_update_params(desc->conn_handle, ¶ms); + 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)); } diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 2ef65c13..4402485b 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -80,7 +80,7 @@ class NimBLEServer { NimBLEService* getServiceByUUID(NimBLEUUID uuid); int disconnect(uint16_t connID, uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); // bool connect(BLEAddress address); - void updateConnParams(ble_gap_conn_desc* desc, + void updateConnParams(uint16_t conn_handle, uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, uint16_t minConnTime=0, uint16_t maxConnTime=0); From 5dd3b31f28596c8b922394a8c0ea01baca6c14f3 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 16 Mar 2020 15:34:25 -0600 Subject: [PATCH 27/62] Refactor descriptors to only be created by the characteristic owner. Descriptor properties now able to be set just as the characteristics. Added createDescriptor methods. Descriptor permissions currently setting incorrectly and need more work. --- src/NimBLE2902.cpp | 7 +++++- src/NimBLE2902.h | 2 +- src/NimBLE2904.cpp | 7 +++++- src/NimBLE2904.h | 3 ++- src/NimBLECharacteristic.cpp | 41 +++++++++++++++++++++++++++++++++++- src/NimBLECharacteristic.h | 20 +++++++++++++----- src/NimBLEDescriptor.cpp | 11 +++++++--- src/NimBLEDescriptor.h | 15 +++++++++---- src/NimBLEServer.cpp | 2 +- src/NimBLEService.h | 13 +++++++++--- 10 files changed, 100 insertions(+), 21 deletions(-) diff --git a/src/NimBLE2902.cpp b/src/NimBLE2902.cpp index 3f2dbf0d..d2f55893 100644 --- a/src/NimBLE2902.cpp +++ b/src/NimBLE2902.cpp @@ -22,7 +22,12 @@ #include "NimBLE2902.h" -NimBLE2902::NimBLE2902() : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902)) { +NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic) +: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902), + BLE_GATT_CHR_PROP_READ | + BLE_GATT_CHR_PROP_WRITE, + 2, pCharacterisitic) +{ uint8_t data[2] = { 0, 0 }; setValue(data, 2); } // NimBLE2902 diff --git a/src/NimBLE2902.h b/src/NimBLE2902.h index 7e4cd377..dcecbaa2 100644 --- a/src/NimBLE2902.h +++ b/src/NimBLE2902.h @@ -35,12 +35,12 @@ */ class NimBLE2902: public NimBLEDescriptor { public: - NimBLE2902(); bool getNotifications(); bool getIndications(); void setNotifications(bool flag); void setIndications(bool flag); private: + NimBLE2902(NimBLECharacteristic* pCharacterisitic); friend class NimBLECharacteristic; std::map m_subscribedMap; diff --git a/src/NimBLE2904.cpp b/src/NimBLE2904.cpp index c57d4b30..59ef0058 100644 --- a/src/NimBLE2904.cpp +++ b/src/NimBLE2904.cpp @@ -22,7 +22,12 @@ #include "NimBLE2904.h" -NimBLE2904::NimBLE2904() : NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904)) { +NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic) +: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2904), + BLE_GATT_CHR_F_READ, + sizeof(BLE2904_Data), + pCharacterisitic) +{ m_data.m_format = 0; m_data.m_exponent = 0; m_data.m_namespace = 1; // 1 = Bluetooth SIG Assigned Numbers diff --git a/src/NimBLE2904.h b/src/NimBLE2904.h index d8e23e54..b2aafb0c 100644 --- a/src/NimBLE2904.h +++ b/src/NimBLE2904.h @@ -38,7 +38,6 @@ struct BLE2904_Data { */ class NimBLE2904: public NimBLEDescriptor { public: - NimBLE2904(); static const uint8_t FORMAT_BOOLEAN = 1; static const uint8_t FORMAT_UINT2 = 2; static const uint8_t FORMAT_UINT4 = 3; @@ -74,6 +73,8 @@ class NimBLE2904: public NimBLEDescriptor { void setUnit(uint16_t unit); private: + NimBLE2904(NimBLECharacteristic* pCharacterisitic); + friend class NimBLECharacteristic; BLE2904_Data m_data; }; // BLE2904 diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 44fc8530..4f6e0cb6 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -14,6 +14,7 @@ #include "NimBLECharacteristic.h" #include "NimBLE2902.h" +#include "NimBLE2904.h" #include "NimBLEDevice.h" #include "NimBLEUtils.h" #include "NimBLELog.h" @@ -69,13 +70,51 @@ NimBLECharacteristic::~NimBLECharacteristic() { * @param [in] pDescriptor * @return N/A. */ -void BLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { +void NimBLECharacteristic::addDescriptor(NimBLEDescriptor* pDescriptor) { NIMBLE_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str()); + // Check that we don't add the same descriptor twice. + if (m_descriptorMap.getByUUID(pDescriptor->getUUID()) != nullptr) { + NIMBLE_LOGW(LOG_TAG, "<< Adding a new descriptor with the same UUID as a previous one"); + //return; + } m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor); NIMBLE_LOGD(LOG_TAG, "<< addDescriptor()"); } // addDescriptor +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) { + return createDescriptor(NimBLEUUID(uuid), properties, max_len); +} + + +/** + * @brief Create a new BLE Descriptor associated with this characteristic. + * @param [in] uuid - The UUID of the descriptor. + * @param [in] properties - The properties of the descriptor. + * @return The new BLE descriptor. + */ +NimBLEDescriptor* NimBLECharacteristic::createDescriptor(NimBLEUUID uuid, uint32_t properties, uint16_t max_len) { + NimBLEDescriptor* pDescriptor = nullptr; + if(uuid.equals(NimBLEUUID((uint16_t)0x2902))) { + pDescriptor = new NimBLE2902(this); + + } else if (uuid.equals(NimBLEUUID((uint16_t)0x2904))) { + pDescriptor = new NimBLE2904(this); + + } else { + pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this); + } + addDescriptor(pDescriptor); + return pDescriptor; +} // createCharacteristic + + /** * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic. * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index abaf9020..ab78968c 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -72,7 +72,15 @@ class NimBLEDescriptorMap { */ class NimBLECharacteristic { public: - void addDescriptor(NimBLEDescriptor* pDescriptor); + NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + uint16_t max_len = 100); + NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, + uint32_t properties = BLE_GATT_CHR_F_READ | + BLE_GATT_CHR_F_WRITE, + uint16_t max_len = 100); + NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(NimBLEUUID descriptorUUID); NimBLEUUID getUUID(); @@ -119,8 +127,10 @@ class NimBLECharacteristic { // friend class NimBLEDescriptor; // friend class NimBLECharacteristicMap; - NimBLECharacteristic(const char* uuid, uint16_t properties = 0, NimBLEService* pService = nullptr); - NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = 0, NimBLEService* pService = nullptr); + NimBLECharacteristic(const char* uuid, uint16_t properties = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE, + NimBLEService* pService = nullptr); + NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE, + NimBLEService* pService = nullptr); virtual ~NimBLECharacteristic(); NimBLEUUID m_uuid; @@ -130,9 +140,9 @@ class NimBLECharacteristic { NimBLECharacteristicCallbacks* m_pCallbacks; NimBLEService* m_pService; NimBLEValue m_value; - uint16_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; - bool m_writeEvt = false; + uint16_t m_permissions; //= BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; + void addDescriptor(NimBLEDescriptor* pDescriptor); NimBLEService* getService(); uint8_t getProperties(); void setSubscribe(struct ble_gap_event *event); diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index ed1e67ad..930871aa 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -29,20 +29,25 @@ static NimBLEDescriptorCallbacks defaultCallbacks; /** * @brief NimBLEDescriptor constructor. */ -NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t len) : NimBLEDescriptor(NimBLEUUID(uuid), len) { +NimBLEDescriptor::NimBLEDescriptor(const char* uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +: NimBLEDescriptor(NimBLEUUID(uuid), max_len, properties, pCharacteristic) { } /** * @brief NimBLEDescriptor constructor. */ -NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len) { +NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_t max_len, + NimBLECharacteristic* pCharacteristic) +{ m_uuid = uuid; m_value.attr_len = 0; // Initial length is 0. m_value.attr_max_len = max_len; // Maximum length of the data. m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCharacteristic = nullptr; // No initial characteristic. + m_permissions = properties; m_pCallbacks = &defaultCallbacks; // No initial callback. - + NIMBLE_LOGE(LOG_TAG, "%s Permissions = %d", m_uuid.toString().c_str(), m_permissions); m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. } // NimBLEDescriptor diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index e0f1af3b..47309eee 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -40,10 +40,7 @@ class NimBLEDescriptorCallbacks; */ class NimBLEDescriptor { public: - NimBLEDescriptor(const char* uuid, uint16_t max_len = 100); - NimBLEDescriptor(NimBLEUUID uuid, uint16_t max_len = 100); virtual ~NimBLEDescriptor(); - uint16_t getHandle(); // Get the handle of the descriptor. size_t getLength(); // Get the length of the value of the descriptor. NimBLEUUID getUUID(); // Get the UUID of the descriptor. @@ -59,12 +56,22 @@ class NimBLEDescriptor { friend class NimBLEDescriptorMap; friend class NimBLECharacteristic; friend class NimBLEService; + friend class NimBLE2902; + friend class NimBLE2904; + + NimBLEDescriptor(const char* uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic); + + NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, + uint16_t max_len, + NimBLECharacteristic* pCharacteristic); NimBLEUUID m_uuid; uint16_t m_handle; NimBLEDescriptorCallbacks* m_pCallbacks; NimBLECharacteristic* m_pCharacteristic; - uint8_t m_permissions = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; + uint16_t m_permissions; // = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; attr_value_t m_value; static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index d11adfd3..aef30744 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -166,7 +166,7 @@ void NimBLEServer::start() { (pChr->m_properties & BLE_GATT_CHR_F_NOTIFY)) { if(nullptr == pChr->getDescriptorByUUID("2902")) { - pChr->addDescriptor(new NimBLE2902()); + pChr->createDescriptor("2902"); } m_notifyChrMap.insert(std::pair (pChr->getHandle(), pChr)); diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 09bd1d1b..d6af767c 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -54,9 +54,14 @@ class NimBLECharacteristicMap { */ class NimBLEService { public: - void addCharacteristic(NimBLECharacteristic* pCharacteristic); - NimBLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties); - NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, uint32_t properties); + NimBLECharacteristic* createCharacteristic(const char* uuid, + uint32_t properties = BLE_GATT_CHR_PROP_READ | + BLE_GATT_CHR_PROP_WRITE); + + NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, + uint32_t properties = BLE_GATT_CHR_PROP_READ | + BLE_GATT_CHR_PROP_WRITE); + void dump(); NimBLECharacteristic* getCharacteristic(const char* uuid); NimBLECharacteristic* getCharacteristic(NimBLEUUID uuid); @@ -73,6 +78,8 @@ class NimBLEService { NimBLEService(NimBLEUUID uuid, uint16_t numHandles, NimBLEServer* pServer); friend class NimBLEServer; friend class NimBLEDevice; + + void addCharacteristic(NimBLECharacteristic* pCharacteristic); NimBLECharacteristicMap m_characteristicMap; uint16_t m_handle; From 37973fc1d0295b106d94f50d153195de2d02c955 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 16 Mar 2020 22:48:53 -0600 Subject: [PATCH 28/62] Descriptor properties fixed. Updated examples for descriptor refactoring. Updated characteristic/descriptor properties defines. Added log level check for printing db info. --- .../BLE_client/BLE_client.ino | 0 .../BLE_iBeacon/BLE_iBeacon.ino | 0 .../BLE_notify/BLE_notify.ino | 2 +- .../BLE_scan/BLE_scan.ino | 0 .../BLE_server/BLE_server.ino | 0 .../BLE_server_multiconnect.ino | 2 +- .../BLE_uart/BLE_uart.ino | 2 +- .../BLE_write/BLE_write.ino | 0 src/NimBLECharacteristic.cpp | 4 +- src/NimBLECharacteristic.h | 40 ++++++++++--------- src/NimBLEDescriptor.cpp | 37 +++++++++++++++-- src/NimBLEDescriptor.h | 9 +++-- src/NimBLEServer.cpp | 4 +- src/NimBLEService.cpp | 6 +-- src/NimBLEService.h | 8 ++-- 15 files changed, 76 insertions(+), 38 deletions(-) rename examples/{ => Original_examples}/BLE_client/BLE_client.ino (100%) rename examples/{ => Original_examples}/BLE_iBeacon/BLE_iBeacon.ino (100%) rename examples/{ => Original_examples}/BLE_notify/BLE_notify.ino (98%) rename examples/{ => Original_examples}/BLE_scan/BLE_scan.ino (100%) rename examples/{ => Original_examples}/BLE_server/BLE_server.ino (100%) rename examples/{ => Original_examples}/BLE_server_multiconnect/BLE_server_multiconnect.ino (98%) rename examples/{ => Original_examples}/BLE_uart/BLE_uart.ino (98%) rename examples/{ => Original_examples}/BLE_write/BLE_write.ino (100%) diff --git a/examples/BLE_client/BLE_client.ino b/examples/Original_examples/BLE_client/BLE_client.ino similarity index 100% rename from examples/BLE_client/BLE_client.ino rename to examples/Original_examples/BLE_client/BLE_client.ino diff --git a/examples/BLE_iBeacon/BLE_iBeacon.ino b/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino similarity index 100% rename from examples/BLE_iBeacon/BLE_iBeacon.ino rename to examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino diff --git a/examples/BLE_notify/BLE_notify.ino b/examples/Original_examples/BLE_notify/BLE_notify.ino similarity index 98% rename from examples/BLE_notify/BLE_notify.ino rename to examples/Original_examples/BLE_notify/BLE_notify.ino index 55051eb1..29226a48 100644 --- a/examples/BLE_notify/BLE_notify.ino +++ b/examples/Original_examples/BLE_notify/BLE_notify.ino @@ -73,7 +73,7 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - pCharacteristic->addDescriptor(new BLE2902()); + pCharacteristic->createDescriptor("2902"); // Start the service pService->start(); diff --git a/examples/BLE_scan/BLE_scan.ino b/examples/Original_examples/BLE_scan/BLE_scan.ino similarity index 100% rename from examples/BLE_scan/BLE_scan.ino rename to examples/Original_examples/BLE_scan/BLE_scan.ino diff --git a/examples/BLE_server/BLE_server.ino b/examples/Original_examples/BLE_server/BLE_server.ino similarity index 100% rename from examples/BLE_server/BLE_server.ino rename to examples/Original_examples/BLE_server/BLE_server.ino diff --git a/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino similarity index 98% rename from examples/BLE_server_multiconnect/BLE_server_multiconnect.ino rename to examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino index f6a76b4d..b8c58665 100644 --- a/examples/BLE_server_multiconnect/BLE_server_multiconnect.ino +++ b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -74,7 +74,7 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - pCharacteristic->addDescriptor(new BLE2902()); + pCharacteristic->createDescriptor("2902"); // Start the service pService->start(); diff --git a/examples/BLE_uart/BLE_uart.ino b/examples/Original_examples/BLE_uart/BLE_uart.ino similarity index 98% rename from examples/BLE_uart/BLE_uart.ino rename to examples/Original_examples/BLE_uart/BLE_uart.ino index 882bb71b..ffc703de 100644 --- a/examples/BLE_uart/BLE_uart.ino +++ b/examples/Original_examples/BLE_uart/BLE_uart.ino @@ -84,7 +84,7 @@ void setup() { PROPERTY_NOTIFY ); - pTxCharacteristic->addDescriptor(new BLE2902()); + pTxCharacteristic->createDescriptor("2902"); BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_RX, diff --git a/examples/BLE_write/BLE_write.ino b/examples/Original_examples/BLE_write/BLE_write.ino similarity index 100% rename from examples/BLE_write/BLE_write.ino rename to examples/Original_examples/BLE_write/BLE_write.ino diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 4f6e0cb6..e6fa5742 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -143,11 +143,11 @@ uint16_t NimBLECharacteristic::getHandle() { return m_handle; } // getHandle - +/* void NimBLECharacteristic::setAccessPermissions(uint16_t perm) { m_permissions = perm; } - +*/ uint8_t NimBLECharacteristic::getProperties() { return m_properties; diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index ab78968c..7ed5a72a 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -16,6 +16,19 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#define PROPERTY_READ BLE_GATT_CHR_F_READ +#define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC +#define PROPERTY_READ_AUTHEN BLE_GATT_CHR_F_READ_AUTHEN +#define PROPERTY_READ_AUTHOR BLE_GATT_CHR_F_READ_AUTHOR +#define PROPERTY_WRITE BLE_GATT_CHR_F_WRITE +#define PROPERTY_WRITE_NR BLE_GATT_CHR_F_WRITE_NO_RSP +#define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC +#define PROPERTY_WRITE_AUTHEN BLE_GATT_CHR_F_WRITE_AUTHEN +#define PROPERTY_WRITE_AUTHOR BLE_GATT_CHR_F_WRITE_AUTHOR +#define PROPERTY_BROADCAST BLE_GATT_CHR_F_BROADCAST +#define PROPERTY_NOTIFY BLE_GATT_CHR_F_NOTIFY +#define PROPERTY_INDICATE BLE_GATT_CHR_F_INDICATE + #include "NimBLEService.h" #include "NimBLEUUID.h" #include "NimBLEValue.h" @@ -27,20 +40,11 @@ #include #include -#define PROPERTY_READ BLE_GATT_CHR_F_READ -#define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC -#define PROPERTY_NOTIFY BLE_GATT_CHR_F_NOTIFY -#define PROPERTY_WRITE BLE_GATT_CHR_F_WRITE -#define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC -#define PROPERTY_BROADCAST BLE_GATT_CHR_F_BROADCAST -#define PROPERTY_INDICATE BLE_GATT_CHR_F_INDICATE -#define PROPERTY_WRITE_NR BLE_GATT_CHR_F_WRITE_NO_RSP - - class NimBLEService; class NimBLEDescriptor; class NimBLECharacteristicCallbacks; + /** * @brief A management structure for %BLE descriptors. */ @@ -73,13 +77,13 @@ class NimBLEDescriptorMap { class NimBLECharacteristic { public: NimBLEDescriptor* createDescriptor(const char* uuid, - uint32_t properties = BLE_GATT_CHR_F_READ | - BLE_GATT_CHR_F_WRITE, - uint16_t max_len = 100); + uint32_t properties = PROPERTY_READ | + PROPERTY_WRITE, + uint16_t max_len = 100); NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, - uint32_t properties = BLE_GATT_CHR_F_READ | - BLE_GATT_CHR_F_WRITE, - uint16_t max_len = 100); + uint32_t properties = PROPERTY_READ | + PROPERTY_WRITE, + uint16_t max_len = 100); NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); NimBLEDescriptor* getDescriptorByUUID(NimBLEUUID descriptorUUID); @@ -108,7 +112,7 @@ class NimBLECharacteristic { std::string toString(); uint16_t getHandle(); - void setAccessPermissions(uint16_t perm); +// void setAccessPermissions(uint16_t perm); // Backward Compatibility - to be removed /* static const uint32_t PROPERTY_READ = 1<<0; @@ -140,7 +144,7 @@ class NimBLECharacteristic { NimBLECharacteristicCallbacks* m_pCallbacks; NimBLEService* m_pService; NimBLEValue m_value; - uint16_t m_permissions; //= BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; +// uint16_t m_permissions; void addDescriptor(NimBLEDescriptor* pDescriptor); NimBLEService* getService(); diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index 930871aa..b2a5ebae 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -45,10 +45,35 @@ NimBLEDescriptor::NimBLEDescriptor(NimBLEUUID uuid, uint16_t properties, uint16_ m_value.attr_max_len = max_len; // Maximum length of the data. m_handle = NULL_HANDLE; // Handle is initially unknown. m_pCharacteristic = nullptr; // No initial characteristic. - m_permissions = properties; - m_pCallbacks = &defaultCallbacks; // No initial callback. - NIMBLE_LOGE(LOG_TAG, "%s Permissions = %d", m_uuid.toString().c_str(), m_permissions); - m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. + m_pCallbacks = &defaultCallbacks; // No initial callback. + m_value.attr_value = (uint8_t*) calloc(max_len,1); // Allocate storage for the value. + m_properties = 0; + + if (properties & BLE_GATT_CHR_F_READ) { // convert uint16_t properties to uint8_t + m_properties |= BLE_ATT_F_READ; + } + if (properties & (BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_WRITE)) { + m_properties |= BLE_ATT_F_WRITE; + } + if (properties & BLE_GATT_CHR_F_READ_ENC) { + m_properties |= BLE_ATT_F_READ_ENC; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHEN) { + m_properties |= BLE_ATT_F_READ_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_READ_AUTHOR) { + m_properties |= BLE_ATT_F_READ_AUTHOR; + } + if (properties & BLE_GATT_CHR_F_WRITE_ENC) { + m_properties |= BLE_ATT_F_WRITE_ENC; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHEN) { + m_properties |= BLE_ATT_F_WRITE_AUTHEN; + } + if (properties & BLE_GATT_CHR_F_WRITE_AUTHOR) { + m_properties |= BLE_ATT_F_WRITE_AUTHOR; + } + } // NimBLEDescriptor @@ -180,9 +205,13 @@ void NimBLEDescriptor::setValue(std::string value) { setValue((uint8_t*) value.data(), value.length()); } // setValue + +/* void NimBLEDescriptor::setAccessPermissions(uint8_t perm) { m_permissions = perm; } +*/ + /** * @brief Return a string representation of the descriptor. diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 47309eee..412218fa 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -16,11 +16,13 @@ #define MAIN_NIMBLEDESCRIPTOR_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include + #include "NimBLEUUID.h" #include "NimBLECharacteristic.h" #include "FreeRTOS.h" +#include + typedef struct { @@ -35,6 +37,7 @@ class NimBLEService; class NimBLECharacteristic; class NimBLEDescriptorCallbacks; + /** * @brief A model of a %BLE descriptor. */ @@ -45,7 +48,7 @@ class NimBLEDescriptor { size_t getLength(); // Get the length of the value of the descriptor. NimBLEUUID getUUID(); // Get the UUID of the descriptor. uint8_t* getValue(); // Get a pointer to the value of the descriptor. - void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. +// void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. void setCallbacks(NimBLEDescriptorCallbacks* pCallbacks); // Set callbacks to be invoked for the descriptor. void setValue(uint8_t* data, size_t size); // Set the value of the descriptor as a pointer to data. void setValue(std::string value); // Set the value of the descriptor as a data buffer. @@ -71,7 +74,7 @@ class NimBLEDescriptor { uint16_t m_handle; NimBLEDescriptorCallbacks* m_pCallbacks; NimBLECharacteristic* m_pCharacteristic; - uint16_t m_permissions; // = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE; + uint8_t m_properties; attr_value_t m_value; static int handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index aef30744..6defca2d 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -136,8 +136,10 @@ void NimBLEServer::start() { abort(); } +#if CONFIG_LOG_DEFAULT_LEVEL > 3 || ARDUHAL_LOG_LEVEL_INFO > 3 ble_gatts_show_local(); - +#endif + ble_uuid16_t svc = {BLE_UUID_TYPE_16, 0x1801}; ble_uuid16_t chr = {BLE_UUID_TYPE_16, 0x2a05}; diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index e8da9a29..98696afb 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -120,18 +120,18 @@ bool NimBLEService::start() { pChr_a[i].descriptors = NULL; } else { // Must have last descriptor uuid = 0 so we have to create 1 extra - NIMBLE_LOGE(LOG_TAG, "Adding %d descriptors", numDscs); + //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs); pDsc_a = new ble_gatt_dsc_def[numDscs+1]; NimBLEDescriptor* pDescriptor = pCharacteristic->m_descriptorMap.getFirst(); for(uint8_t d=0; d < numDscs;) { // skip 2902 if(pDescriptor->m_uuid.equals(NimBLEUUID((uint16_t)0x2902))) { - NIMBLE_LOGE(LOG_TAG, "Skipped 0x2902"); + //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902"); pDescriptor = pCharacteristic->m_descriptorMap.getNext(); continue; } pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u; - pDsc_a[d].att_flags = pDescriptor->m_permissions; + pDsc_a[d].att_flags = pDescriptor->m_properties; pDsc_a[d].min_key_size = 0; pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent; pDsc_a[d].arg = pDescriptor; diff --git a/src/NimBLEService.h b/src/NimBLEService.h index d6af767c..52558fc6 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -55,12 +55,12 @@ class NimBLECharacteristicMap { class NimBLEService { public: NimBLECharacteristic* createCharacteristic(const char* uuid, - uint32_t properties = BLE_GATT_CHR_PROP_READ | - BLE_GATT_CHR_PROP_WRITE); + uint32_t properties = PROPERTY_READ | + PROPERTY_WRITE); NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, - uint32_t properties = BLE_GATT_CHR_PROP_READ | - BLE_GATT_CHR_PROP_WRITE); + uint32_t properties = PROPERTY_READ | + PROPERTY_WRITE); void dump(); NimBLECharacteristic* getCharacteristic(const char* uuid); From 77070994d153b3267515c93c4f2bca7666d91c08 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 17 Mar 2020 09:17:39 -0600 Subject: [PATCH 29/62] Fixed resource exhaustion crash shen starting service. --- src/NimBLEService.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NimBLEService.cpp b/src/NimBLEService.cpp index 98696afb..4bf6f34f 100644 --- a/src/NimBLEService.cpp +++ b/src/NimBLEService.cpp @@ -91,6 +91,7 @@ bool NimBLEService::start() { svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY; svc[0].uuid = &m_uuid.getNative()->u; + svc[0].includes = NULL; uint8_t numChrs = m_characteristicMap.getSize(); From 58464030dac91d89f69a605c6a09f828a307936c Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 17 Mar 2020 13:02:41 -0600 Subject: [PATCH 30/62] sdkconfig renamed nimconfig, includes changed to reflect. Added defines to nimconfig to prevent arduino initialization from releasing BLE memory. --- src/esp_nimble_cfg.h | 2 +- src/nimble/nimble_npl.h | 2 +- src/nimble/nimble_port.h | 2 +- src/nimble/sdkconfig.h | 325 ------------------------------- src/{sdkconfig.h => nimconfig.h} | 6 +- src/port/src/esp_nimble_mem.c | 2 +- 6 files changed, 8 insertions(+), 331 deletions(-) delete mode 100644 src/nimble/sdkconfig.h rename src/{sdkconfig.h => nimconfig.h} (88%) diff --git a/src/esp_nimble_cfg.h b/src/esp_nimble_cfg.h index c7592de6..b2dd7443 100644 --- a/src/esp_nimble_cfg.h +++ b/src/esp_nimble_cfg.h @@ -1,6 +1,6 @@ #ifndef __ESP_NIMBLE_CFG__ #define __ESP_NIMBLE_CFG__ -#include "sdkconfig.h" +#include "nimconfig.h" /** * This macro exists to ensure code includes this header when needed. If code diff --git a/src/nimble/nimble_npl.h b/src/nimble/nimble_npl.h index 6b95cda5..02d27c9f 100644 --- a/src/nimble/nimble_npl.h +++ b/src/nimble/nimble_npl.h @@ -23,7 +23,7 @@ #include #include #include -#include "sdkconfig.h" +#include "nimconfig.h" #ifdef __cplusplus extern "C" { #endif diff --git a/src/nimble/nimble_port.h b/src/nimble/nimble_port.h index 77bbca8d..e8996a66 100644 --- a/src/nimble/nimble_port.h +++ b/src/nimble/nimble_port.h @@ -21,7 +21,7 @@ #define _NIMBLE_PORT_H #include "nimble/nimble_npl.h" -#include "sdkconfig.h" +#include "nimconfig.h" #define NIMBLE_CORE (CONFIG_BT_NIMBLE_PINNED_TO_CORE < portNUM_PROCESSORS ? CONFIG_BT_NIMBLE_PINNED_TO_CORE : tskNO_AFFINITY) #define NIMBLE_STACK_SIZE CONFIG_BT_NIMBLE_TASK_STACK_SIZE diff --git a/src/nimble/sdkconfig.h b/src/nimble/sdkconfig.h deleted file mode 100644 index b2135f15..00000000 --- a/src/nimble/sdkconfig.h +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Automatically generated file. DO NOT EDIT. - * Espressif IoT Development Framework (ESP-IDF) Configuration Header - */ -#pragma once -#define CONFIG_IDF_TARGET "esp32" -#define CONFIG_SDK_TOOLPREFIX "xtensa-esp32-elf-" -#define CONFIG_SDK_PYTHON "python" -#define CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES 1 -#define CONFIG_APP_COMPILE_TIME_DATE 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL 3 -#define CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V 1 -#define CONFIG_BOOTLOADER_WDT_ENABLE 1 -#define CONFIG_BOOTLOADER_WDT_TIME_MS 9000 -#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1 -#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 -#define CONFIG_BT_NIMBLE_MAX_BONDS 3 -#define CONFIG_BT_NIMBLE_MAX_CCCDS 8 -#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0 -#define CONFIG_BT_NIMBLE_PINNED_TO_CORE_0 1 -#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0 -#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096 -#define CONFIG_BT_NIMBLE_ROLE_CENTRAL 1 -#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL 1 -#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER 1 -#define CONFIG_BT_NIMBLE_ROLE_OBSERVER 1 -#define CONFIG_BT_NIMBLE_NVS_PERSIST 1 -#define CONFIG_BT_NIMBLE_SM_LEGACY 1 -#define CONFIG_BT_NIMBLE_SM_SC 1 -#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble" -#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31 -#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 256 -#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0 -#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12 -#define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255 -#define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70 -#define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30 -#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 -#define CONFIG_ESPTOOLPY_PORT "/dev/ttyUSB0" -#define CONFIG_ESPTOOLPY_BAUD_115200B 1 -#define CONFIG_ESPTOOLPY_BAUD_OTHER_VAL 115200 -#define CONFIG_ESPTOOLPY_BAUD 115200 -#define CONFIG_ESPTOOLPY_COMPRESSED 1 -#define CONFIG_FLASHMODE_DIO 1 -#define CONFIG_ESPTOOLPY_FLASHMODE "dio" -#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 -#define CONFIG_ESPTOOLPY_FLASHFREQ "40m" -#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1 -#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB" -#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1 -#define CONFIG_ESPTOOLPY_BEFORE_RESET 1 -#define CONFIG_ESPTOOLPY_BEFORE "default_reset" -#define CONFIG_ESPTOOLPY_AFTER_RESET 1 -#define CONFIG_ESPTOOLPY_AFTER "hard_reset" -#define CONFIG_MONITOR_BAUD_115200B 1 -#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200 -#define CONFIG_MONITOR_BAUD 115200 -#define CONFIG_BLE_SM_IO_CAP_NO_IO 1 -#define CONFIG_EXAMPLE_IO_TYPE 3 -#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 -#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" -#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" -#define CONFIG_PARTITION_TABLE_OFFSET 0x8000 -#define CONFIG_PARTITION_TABLE_MD5 1 -#define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1 -#define CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED 1 -#define CONFIG_STACK_CHECK_NONE 1 -#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 -#define CONFIG_ESP32_APPTRACE_LOCK_ENABLE 1 -#define CONFIG_BT_ENABLED 1 -#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 -#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN 3 -#define CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF 3 -#define CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF 0 -#define CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF 0 -#define CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE_0 1 -#define CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE 0 -#define CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI 1 -#define CONFIG_BTDM_CONTROLLER_MODEM_SLEEP 1 -#define CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG 1 -#define CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL 1 -#define CONFIG_BLE_SCAN_DUPLICATE 1 -#define CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR 1 -#define CONFIG_SCAN_DUPLICATE_TYPE 0 -#define CONFIG_DUPLICATE_SCAN_CACHE_SIZE 200 -#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_SUPPORTED 1 -#define CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_NUM 100 -#define CONFIG_BLE_ADV_REPORT_DISCARD_THRSHOLD 20 -#define CONFIG_BT_RESERVE_DRAM 0xdb5c -#define CONFIG_ADC2_DISABLE_DAC 1 -#define CONFIG_SPI_MASTER_ISR_IN_IRAM 1 -#define CONFIG_SPI_SLAVE_ISR_IN_IRAM 1 -#define CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4 1 -#define CONFIG_EFUSE_MAX_BLK_LEN 192 -#define CONFIG_IDF_TARGET_ESP32 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_160 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 160 -#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 -#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 -#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 -#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 -#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 2304 -#define CONFIG_MAIN_TASK_STACK_SIZE 3584 -#define CONFIG_IPC_TASK_STACK_SIZE 1024 -#define CONFIG_TIMER_TASK_STACK_SIZE 3584 -#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1 -#define CONFIG_NEWLIB_STDIN_LINE_ENDING_CR 1 -#define CONFIG_CONSOLE_UART_DEFAULT 1 -#define CONFIG_CONSOLE_UART_NUM 0 -#define CONFIG_CONSOLE_UART_BAUDRATE 115200 -#define CONFIG_ULP_COPROC_RESERVE_MEM 0 -#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 -#define CONFIG_ESP32_DEBUG_OCDAWARE 1 -#define CONFIG_ESP32_DEBUG_STUBS_ENABLE 1 -#define CONFIG_INT_WDT 1 -#define CONFIG_INT_WDT_TIMEOUT_MS 300 -#define CONFIG_INT_WDT_CHECK_CPU1 1 -#define CONFIG_TASK_WDT 1 -#define CONFIG_TASK_WDT_TIMEOUT_S 5 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0 1 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 1 -#define CONFIG_BROWNOUT_DET 1 -#define CONFIG_BROWNOUT_DET_LVL_SEL_0 1 -#define CONFIG_BROWNOUT_DET_LVL 0 -#define CONFIG_REDUCE_PHY_TX_POWER 1 -#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 -#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 -#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 -#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000 -#define CONFIG_ESP32_XTAL_FREQ_40 1 -#define CONFIG_ESP32_XTAL_FREQ 40 -#define CONFIG_ESP_ERR_TO_NAME_LOOKUP 1 -#define CONFIG_ADC_CAL_EFUSE_TP_ENABLE 1 -#define CONFIG_ADC_CAL_EFUSE_VREF_ENABLE 1 -#define CONFIG_ADC_CAL_LUT_ENABLE 1 -#define CONFIG_POST_EVENTS_FROM_ISR 1 -#define CONFIG_POST_EVENTS_FROM_IRAM_ISR 1 -#define CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS 1 -#define CONFIG_HTTPD_MAX_REQ_HDR_LEN 512 -#define CONFIG_HTTPD_MAX_URI_LEN 512 -#define CONFIG_HTTPD_ERR_RESP_NO_DELAY 1 -#define CONFIG_SW_COEXIST_ENABLE 1 -#define CONFIG_SW_COEXIST_PREFERENCE_BALANCE 1 -#define CONFIG_SW_COEXIST_PREFERENCE_VALUE 2 -#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 -#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32 -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 -#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 -#define CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED 1 -#define CONFIG_ESP32_WIFI_TX_BA_WIN 6 -#define CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED 1 -#define CONFIG_ESP32_WIFI_RX_BA_WIN 6 -#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 -#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1 -#define CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN 752 -#define CONFIG_ESP32_WIFI_MGMT_SBUF_NUM 32 -#define CONFIG_ESP32_WIFI_IRAM_OPT 1 -#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 -#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20 -#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 -#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 -#define CONFIG_DMA_RX_BUF_NUM 10 -#define CONFIG_DMA_TX_BUF_NUM 10 -#define CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE 1 -#define CONFIG_EMAC_CHECK_LINK_PERIOD_MS 2000 -#define CONFIG_EMAC_TASK_PRIORITY 20 -#define CONFIG_EMAC_TASK_STACK_SIZE 3072 -#define CONFIG_FATFS_CODEPAGE_437 1 -#define CONFIG_FATFS_CODEPAGE 437 -#define CONFIG_FATFS_LFN_NONE 1 -#define CONFIG_FATFS_FS_LOCK 0 -#define CONFIG_FATFS_TIMEOUT_MS 10000 -#define CONFIG_FATFS_PER_FILE_CACHE 1 -#define CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND 150 -#define CONFIG_MB_MASTER_DELAY_MS_CONVERT 200 -#define CONFIG_MB_QUEUE_LENGTH 20 -#define CONFIG_MB_SERIAL_TASK_STACK_SIZE 2048 -#define CONFIG_MB_SERIAL_BUF_SIZE 256 -#define CONFIG_MB_SERIAL_TASK_PRIO 10 -#define CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT 20 -#define CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE 20 -#define CONFIG_MB_CONTROLLER_STACK_SIZE 4096 -#define CONFIG_MB_EVENT_QUEUE_TIMEOUT 20 -#define CONFIG_MB_TIMER_PORT_ENABLED 1 -#define CONFIG_MB_TIMER_GROUP 0 -#define CONFIG_MB_TIMER_INDEX 0 -#define CONFIG_FREERTOS_NO_AFFINITY 0x7FFFFFFF -#define CONFIG_FREERTOS_CORETIMER_0 1 -#define CONFIG_FREERTOS_HZ 100 -#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 -#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 -#define CONFIG_FREERTOS_INTERRUPT_BACKTRACE 1 -#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1 -#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 -#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1536 -#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 -#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 -#define CONFIG_TIMER_TASK_PRIORITY 1 -#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 -#define CONFIG_TIMER_QUEUE_LENGTH 10 -#define CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE 0 -#define CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER 1 -#define CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER 1 -#define CONFIG_HEAP_POISONING_DISABLED 1 -#define CONFIG_HEAP_TRACING_OFF 1 -#define CONFIG_LIBSODIUM_USE_MBEDTLS_SHA 1 -#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 -#define CONFIG_LOG_DEFAULT_LEVEL 3 -#define CONFIG_LOG_COLORS 1 -#define CONFIG_LWIP_MAX_SOCKETS 10 -#define CONFIG_LWIP_SO_REUSE 1 -#define CONFIG_LWIP_SO_REUSE_RXTOALL 1 -#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 -#define CONFIG_ESP_GRATUITOUS_ARP 1 -#define CONFIG_GARP_TMR_INTERVAL 60 -#define CONFIG_TCPIP_RECVMBOX_SIZE 32 -#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 -#define CONFIG_LWIP_DHCPS_LEASE_UNIT 60 -#define CONFIG_LWIP_DHCPS_MAX_STATION_NUM 8 -#define CONFIG_LWIP_NETIF_LOOPBACK 1 -#define CONFIG_LWIP_LOOPBACK_MAX_PBUFS 8 -#define CONFIG_LWIP_MAX_ACTIVE_TCP 16 -#define CONFIG_LWIP_MAX_LISTENING_TCP 16 -#define CONFIG_TCP_MAXRTX 12 -#define CONFIG_TCP_SYNMAXRTX 6 -#define CONFIG_TCP_MSS 1436 -#define CONFIG_TCP_MSL 60000 -#define CONFIG_TCP_SND_BUF_DEFAULT 5744 -#define CONFIG_TCP_WND_DEFAULT 5744 -#define CONFIG_TCP_RECVMBOX_SIZE 6 -#define CONFIG_TCP_QUEUE_OOSEQ 1 -#define CONFIG_TCP_OVERSIZE_MSS 1 -#define CONFIG_LWIP_MAX_UDP_PCBS 16 -#define CONFIG_UDP_RECVMBOX_SIZE 6 -#define CONFIG_TCPIP_TASK_STACK_SIZE 3072 -#define CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY 1 -#define CONFIG_TCPIP_TASK_AFFINITY 0x7FFFFFFF -#define CONFIG_LWIP_MAX_RAW_PCBS 16 -#define CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC 1 -#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 -#define CONFIG_MBEDTLS_HARDWARE_AES 1 -#define CONFIG_MBEDTLS_HAVE_TIME 1 -#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1 -#define CONFIG_MBEDTLS_TLS_SERVER 1 -#define CONFIG_MBEDTLS_TLS_CLIENT 1 -#define CONFIG_MBEDTLS_TLS_ENABLED 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA 1 -#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA 1 -#define CONFIG_MBEDTLS_SSL_RENEGOTIATION 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1 -#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 1 -#define CONFIG_MBEDTLS_SSL_ALPN 1 -#define CONFIG_MBEDTLS_SSL_SESSION_TICKETS 1 -#define CONFIG_MBEDTLS_AES_C 1 -#define CONFIG_MBEDTLS_RC4_DISABLED 1 -#define CONFIG_MBEDTLS_CCM_C 1 -#define CONFIG_MBEDTLS_GCM_C 1 -#define CONFIG_MBEDTLS_PEM_PARSE_C 1 -#define CONFIG_MBEDTLS_PEM_WRITE_C 1 -#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1 -#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1 -#define CONFIG_MBEDTLS_ECP_C 1 -#define CONFIG_MBEDTLS_ECDH_C 1 -#define CONFIG_MBEDTLS_ECDSA_C 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1 -#define CONFIG_MBEDTLS_ECP_NIST_OPTIM 1 -#define CONFIG_MDNS_MAX_SERVICES 10 -#define CONFIG_MQTT_PROTOCOL_311 1 -#define CONFIG_MQTT_TRANSPORT_SSL 1 -#define CONFIG_MQTT_TRANSPORT_WEBSOCKET 1 -#define CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE 1 -#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1 -#define CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT 5 -#define CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT 3072 -#define CONFIG_PTHREAD_STACK_MIN 768 -#define CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY 1 -#define CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT -1 -#define CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT "pthread" -#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 -#define CONFIG_SPIFFS_MAX_PARTITIONS 3 -#define CONFIG_SPIFFS_CACHE 1 -#define CONFIG_SPIFFS_CACHE_WR 1 -#define CONFIG_SPIFFS_PAGE_CHECK 1 -#define CONFIG_SPIFFS_GC_MAX_RUNS 10 -#define CONFIG_SPIFFS_PAGE_SIZE 256 -#define CONFIG_SPIFFS_OBJ_NAME_LEN 32 -#define CONFIG_SPIFFS_USE_MAGIC 1 -#define CONFIG_SPIFFS_USE_MAGIC_LENGTH 1 -#define CONFIG_SPIFFS_META_LENGTH 4 -#define CONFIG_SPIFFS_USE_MTIME 1 -#define CONFIG_IP_LOST_TIMER_INTERVAL 120 -#define CONFIG_TCPIP_LWIP 1 -#define CONFIG_UNITY_ENABLE_FLOAT 1 -#define CONFIG_UNITY_ENABLE_DOUBLE 1 -#define CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER 1 -#define CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT 1 -#define CONFIG_SUPPORT_TERMIOS 1 -#define CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS 1 -#define CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN 128 -#define CONFIG_WL_SECTOR_SIZE_4096 1 -#define CONFIG_WL_SECTOR_SIZE 4096 - -/* List of deprecated options */ -#define CONFIG_MAKE_WARN_UNDEFINED_VARIABLES CONFIG_SDK_MAKE_WARN_UNDEFINED_VARIABLES -#define CONFIG_PYTHON CONFIG_SDK_PYTHON -#define CONFIG_TOOLPREFIX CONFIG_SDK_TOOLPREFIX diff --git a/src/sdkconfig.h b/src/nimconfig.h similarity index 88% rename from src/sdkconfig.h rename to src/nimconfig.h index 26e583b3..0510f85f 100644 --- a/src/sdkconfig.h +++ b/src/nimconfig.h @@ -1,4 +1,4 @@ -#include "Arduino.h" +#pragma once #define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1 #define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3 #define CONFIG_BT_NIMBLE_MAX_BONDS 3 @@ -22,4 +22,6 @@ #define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255 #define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70 #define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30 -#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 \ No newline at end of file +#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 +#define CONFIG_BT_ENABLED 1 +#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 \ No newline at end of file diff --git a/src/port/src/esp_nimble_mem.c b/src/port/src/esp_nimble_mem.c index 9b7a4ce6..5ea52eb7 100644 --- a/src/port/src/esp_nimble_mem.c +++ b/src/port/src/esp_nimble_mem.c @@ -21,7 +21,7 @@ #include "esp_attr.h" #include "esp_heap_caps.h" -#include "sdkconfig.h" +#include "nimconfig.h" #include "esp_nimble_mem.h" IRAM_ATTR void *nimble_platform_mem_malloc(size_t size) From 68b5faab4e2a1d150002f68ad46ea6feb8c2563e Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 17 Mar 2020 22:27:53 -0600 Subject: [PATCH 31/62] Implement static default callbacks for client. Make notes of differences in original client example from original ble library. --- .../BLE_client/BLE_client.ino | 42 +++++++++- src/NimBLEClient.cpp | 76 +++++++++++++++---- src/NimBLEClient.h | 18 ++--- src/nimconfig.h | 2 +- 4 files changed, 111 insertions(+), 27 deletions(-) diff --git a/examples/Original_examples/BLE_client/BLE_client.ino b/examples/Original_examples/BLE_client/BLE_client.ino index 94f16b56..195195f4 100644 --- a/examples/Original_examples/BLE_client/BLE_client.ino +++ b/examples/Original_examples/BLE_client/BLE_client.ino @@ -5,9 +5,13 @@ * updated by chegewara * updated for NimBLE by H2zero */ + +/** NimBLE differences highlighted in comment blocks **/ +/*******original******** +#include "BLEDevice.h" +***********************/ #include "NimBLEDevice.h" -//#include "BLEScan.h" // The remote service we wish to connect to. static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); @@ -33,6 +37,8 @@ static void notifyCallback( Serial.println((char*)pData); } +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ class MyClientCallback : public BLEClientCallbacks { void onConnect(BLEClient* pclient) { } @@ -41,6 +47,25 @@ class MyClientCallback : public BLEClientCallbacks { connected = false; Serial.println("onDisconnect"); } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client PassKeyRequest"); + return 123456; + } + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + bool onSecurityRequest(){ + ESP_LOGI(tag, "SecurityRequest"); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ }; bool connectToServer() { @@ -88,7 +113,9 @@ bool connectToServer() { pRemoteCharacteristic->registerForNotify(notifyCallback); connected = true; + return true; } + /** * Scan for BLE servers and find the first one that advertises the service we are looking for. */ @@ -96,15 +123,25 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { /** * Called for each advertising BLE server. */ +/******************************************************** + void onResult(BLEAdvertisedDevice advertisedDevice) { + Only a reference to the advertised device is passed now +********************************************************/ void onResult(BLEAdvertisedDevice* advertisedDevice) { Serial.print("BLE Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); // We have found a device, let us now see if it contains the service we are looking for. +/******************************************************************************** + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { +********************************************************************************/ if (advertisedDevice->haveServiceUUID() && advertisedDevice->isAdvertisingService(serviceUUID)) { BLEDevice::getScan()->stop(); - myDevice = new BLEAdvertisedDevice(*advertisedDevice); +/******************************************************************* + myDevice = new BLEAdvertisedDevice(advertisedDevice); +*******************************************************************/ + myDevice = new BLEAdvertisedDevice(*advertisedDevice); /** dereference to copy data now **/ doConnect = true; doScan = true; @@ -152,6 +189,7 @@ void loop() { Serial.println("Setting new characteristic value to \"" + newValue + "\""); // Set the characteristic's value to be the array of bytes that is actually a string. + /*** Note: write / read value now returns true if successful, false otherwise - try again or disconnect ***/ pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); }else if(doScan){ BLEDevice::getScan()->start(0); // this is just eample to start scan after disconnect, most likely there is better way to do it in arduino diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index a2d3190c..875aa514 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -23,6 +23,7 @@ #include static const char* LOG_TAG = "NimBLEClient"; +static NimBLEClientCallbacks defaultCallbacks; /* * Design @@ -47,7 +48,7 @@ static const char* LOG_TAG = "NimBLEClient"; NimBLEClient::NimBLEClient() { - m_pClientCallbacks = nullptr; + m_pClientCallbacks = &defaultCallbacks; m_conn_id = BLE_HS_CONN_HANDLE_NONE; m_haveServices = false; m_isConnected = false; @@ -525,9 +526,9 @@ uint16_t NimBLEClient::getMTU() { client->m_semaphoreSearchCmplEvt.give(1); client->m_semeaphoreSecEvt.give(1); - if (client->m_pClientCallbacks != nullptr) { - client->m_pClientCallbacks->onDisconnect(client); - } + //if (client->m_pClientCallbacks != nullptr) { + client->m_pClientCallbacks->onDisconnect(client); + //} //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; @@ -563,9 +564,10 @@ uint16_t NimBLEClient::getMTU() { client->m_isConnected = true; - if (client->m_pClientCallbacks != nullptr) { - client->m_pClientCallbacks->onConnect(client); - } + //if (client->m_pClientCallbacks != nullptr) { + client->m_pClientCallbacks->onConnect(client); + //} + // Incase of a multiconnecting device we ignore this device when scanning since we are already connected to it NimBLEDevice::addIgnored(client->m_peerAddress); client->m_semaphoreOpenEvt.give(0); @@ -617,13 +619,17 @@ uint16_t NimBLEClient::getMTU() { } case BLE_GAP_EVENT_ENC_CHANGE: { - if(client->m_conn_id != event->enc_change.conn_handle) + if(client->m_conn_id != event->enc_change.conn_handle){ return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + + struct ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); if(NimBLEDevice::m_securityCallbacks != nullptr) { - struct ble_gap_conn_desc desc; - rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); - assert(rc == 0); + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + } else { client->m_pClientCallbacks->onAuthenticationComplete(&desc); } @@ -650,11 +656,15 @@ uint16_t NimBLEClient::getMTU() { if(NimBLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); //////////////////////////////////////////////////// - }else if(client->m_pClientCallbacks != nullptr) { + } else { + pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + /* }else if(client->m_pClientCallbacks != nullptr) { pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); }else{ pkey.numcmp_accept = false; } + */ rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); @@ -676,14 +686,17 @@ uint16_t NimBLEClient::getMTU() { if(NimBLEDevice::m_securityCallbacks != nullptr) { pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); ///////////////////////////////////////////// - }else if(client->m_pClientCallbacks != nullptr) { + } else { + client->m_pClientCallbacks->onPassKeyRequest(); + } + /* }else if(client->m_pClientCallbacks != nullptr) { pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); }else{ pkey.passkey = 0; NIMBLE_LOGE(LOG_TAG, "No Callback! Sending 0 as the passkey"); } -; + */ rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); @@ -714,7 +727,11 @@ bool NimBLEClient::isConnected() { * @brief Set the callbacks that will be invoked. */ void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) { - m_pClientCallbacks = pClientCallbacks; + if (pClientCallbacks != nullptr){ + m_pClientCallbacks = pClientCallbacks; + } else { + m_pClientCallbacks = &defaultCallbacks; + } m_deleteCallbacks = deleteCallbacks; } // setClientCallbacks @@ -734,4 +751,33 @@ std::string NimBLEClient::toString() { } // toString +void NimBLEClientCallbacks::onConnect(NimBLEClient *pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onConnect: default"); +} + +void NimBLEClientCallbacks::onDisconnect(NimBLEClient *pClient) { + NIMBLE_LOGD("NimBLEClientCallbacks", "onDisconnect: default"); +} + +uint32_t NimBLEClientCallbacks::onPassKeyRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456"); + return 123456; +} + +void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyNotify: default: %d", pass_key); +} + +bool NimBLEClientCallbacks::onSecurityRequest(){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true"); + return true; +} +void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default"); +} +bool NimBLEClientCallbacks::onConfirmPIN(uint32_t pin){ + NIMBLE_LOGD("NimBLEClientCallbacks", "onConfirmPIN: default: true"); + return true; +} + #endif // CONFIG_BT_ENABLED diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 995becd7..1d760976 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -33,8 +33,8 @@ class NimBLEAdvertisedDevice; */ class NimBLEClient { public: - bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = false); - bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_TYPE_PUBLIC, bool refreshServices = false); // Connect to the remote BLE Server + bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true); + bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_TYPE_PUBLIC, bool refreshServices = true); // Connect to the remote BLE Server int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM); // Disconnect from the remote BLE Server NimBLEAddress getPeerAddress(); // Get the address of the remote BLE Server int getRssi(); // Get the RSSI of the remote BLE Server @@ -89,13 +89,13 @@ class NimBLEClient { class NimBLEClientCallbacks { public: virtual ~NimBLEClientCallbacks() {}; - virtual void onConnect(NimBLEClient *pClient) = 0; - virtual void onDisconnect(NimBLEClient *pClient) = 0; - virtual uint32_t onPassKeyRequest(){return 0;} - virtual void onPassKeyNotify(uint32_t pass_key){} - virtual bool onSecurityRequest(){return false;} - virtual void onAuthenticationComplete(ble_gap_conn_desc*){}; - virtual bool onConfirmPIN(uint32_t pin){return false;} + virtual void onConnect(NimBLEClient *pClient); // = 0; + virtual void onDisconnect(NimBLEClient *pClient); // = 0; + virtual uint32_t onPassKeyRequest(); //{return 0;} + virtual void onPassKeyNotify(uint32_t pass_key); //{} + virtual bool onSecurityRequest(); //{return false;} + virtual void onAuthenticationComplete(ble_gap_conn_desc*); //{}; + virtual bool onConfirmPIN(uint32_t pin); //{return false;} }; #endif // CONFIG_BT_ENABLED diff --git a/src/nimconfig.h b/src/nimconfig.h index 0510f85f..f45842a0 100644 --- a/src/nimconfig.h +++ b/src/nimconfig.h @@ -24,4 +24,4 @@ #define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30 #define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 #define CONFIG_BT_ENABLED 1 -#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 \ No newline at end of file +#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 From d29c8e158a6477d48ae0d8d21b05c21390d81e4b Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 18 Mar 2020 20:52:02 -0600 Subject: [PATCH 32/62] Update / comment ibeacon example with NimBLE differences. Add setAdvertisementType to NimBLEAdvertising --- .../BLE_iBeacon/BLE_iBeacon.ino | 18 ++++++++++- src/NimBLEAdvertising.cpp | 30 +++++++++++-------- src/NimBLEAdvertising.h | 1 + 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino b/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino index b93c4b27..74de21fb 100644 --- a/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino +++ b/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino @@ -15,8 +15,17 @@ 6. deep sleep */ -#include "sys/time.h" + +/** NimBLE differences highlighted in comment blocks **/ + + +#include "sys/time.h" +/*******original******** +#include "BLEDevice.h" +#include "BLEUtils.h" +#include "BLEBeacon.h" +***********************/ #include "NimBLEDevice.h" #include "NimBLEUtils.h" #include "NimBLEBeacon.h" @@ -65,6 +74,13 @@ void setBeacon() { pAdvertising->setAdvertisementData(oAdvertisementData); pAdvertising->setScanResponseData(oScanResponseData); + /** pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND); + * Advertising mode. Can be one of following constants: + * - BLE_GAP_CONN_MODE_NON (non-connectable; 3.C.9.3.2). + * - BLE_GAP_CONN_MODE_DIR (directed-connectable; 3.C.9.3.3). + * - BLE_GAP_CONN_MODE_UND (undirected-connectable; 3.C.9.3.4). + */ + pAdvertising->setAdvertisementType(BLE_GAP_CONN_MODE_NON); } diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 4cb6cb94..821898c2 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -35,21 +35,21 @@ NimBLEAdvertising::NimBLEAdvertising() { memset(&m_advParams, 0, sizeof m_advParams); const char *name = ble_svc_gap_device_name(); - m_advData.name = (uint8_t *)name; - m_advData.name_len = strlen(name); - m_advData.name_is_complete = 1; + m_advData.name = (uint8_t *)name; + m_advData.name_len = strlen(name); + m_advData.name_is_complete = 1; m_scanData.tx_pwr_lvl_is_present = 1; - m_scanData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; - m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); - m_advData.appearance = 0x00; - m_advData.appearance_is_present = 0; - m_advData.mfg_data_len = 0; - m_advData.mfg_data = nullptr; + m_scanData.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP); + m_advData.appearance = 0; + m_advData.appearance_is_present = 0; + m_advData.mfg_data_len = 0; + m_advData.mfg_data = nullptr; - m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; - m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; - m_advParams.itvl_min = 0x20; - m_advParams.itvl_max = 0x40; + m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND; + m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN; + m_advParams.itvl_min = 0; + m_advParams.itvl_max = 0; } // NimBLEAdvertising @@ -84,6 +84,10 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) { m_advData.appearance_is_present = 1; } // setAppearance +void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){ + m_advParams.conn_mode = adv_type; +} // setAdvertisementType + void NimBLEAdvertising::setMinInterval(uint16_t mininterval) { m_advParams.itvl_min = mininterval; } // setMinInterval diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 812735e2..fb6cc35b 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -72,6 +72,7 @@ class NimBLEAdvertising { void start(); void stop(); void setAppearance(uint16_t appearance); + void setAdvertisementType(uint8_t adv_type); void setMaxInterval(uint16_t maxinterval); void setMinInterval(uint16_t mininterval); void setAdvertisementData(NimBLEAdvertisementData& advertisementData); From 87f3bf47b7df9f8f03a1a7fba16f53dc97ed43b0 Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 18 Mar 2020 21:59:16 -0600 Subject: [PATCH 33/62] Add difference comments to BLE_Notify example. Implement static callbacks to NimBLEServer. --- .../BLE_notify/BLE_notify.ino | 43 +++++++++-- src/NimBLEServer.cpp | 72 ++++++++++++++----- src/NimBLEServer.h | 10 +-- 3 files changed, 97 insertions(+), 28 deletions(-) diff --git a/examples/Original_examples/BLE_notify/BLE_notify.ino b/examples/Original_examples/BLE_notify/BLE_notify.ino index 29226a48..7c5eb493 100644 --- a/examples/Original_examples/BLE_notify/BLE_notify.ino +++ b/examples/Original_examples/BLE_notify/BLE_notify.ino @@ -19,10 +19,18 @@ A connect hander associated with the server starts a background task that performs notification every couple of seconds. */ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ #include #include #include -#include BLEServer* pServer = NULL; BLECharacteristic* pCharacteristic = NULL; @@ -36,7 +44,8 @@ uint32_t value = 0; #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" - +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; @@ -45,10 +54,25 @@ class MyServerCallbacks: public BLEServerCallbacks { void onDisconnect(BLEServer* pServer) { deviceConnected = false; } +/***************** New - Security handled here ******************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } +/*******************************************************************/ }; - void setup() { Serial.begin(115200); @@ -65,6 +89,13 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ PROPERTY_READ | PROPERTY_WRITE | PROPERTY_NOTIFY | @@ -73,7 +104,11 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - pCharacteristic->createDescriptor("2902"); + /******* New createDescriptor method ******** + Add properties the same as characteristics now + pCharacteristic->addDescriptor(new BLE2902()); + ********************************************/ + pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); // Start the service pService->start(); diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 6defca2d..86f627fe 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -22,6 +22,7 @@ #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEServer"; +static NimBLEServerCallbacks defaultCallbacks; /** @@ -33,7 +34,7 @@ static const char* LOG_TAG = "NimBLEServer"; NimBLEServer::NimBLEServer() { m_connId = BLE_HS_CONN_HANDLE_NONE; m_svcChgChrHdl = 0xffff; - m_pServerCallbacks = nullptr; + m_pServerCallbacks = &defaultCallbacks; m_gattsStarted = false; } // BLEServer @@ -237,13 +238,13 @@ uint32_t NimBLEServer::getConnectedCount() { else { server->m_connId = event->connect.conn_handle; server->addPeerDevice((void*)server, false, server->m_connId); - if (server->m_pServerCallbacks != nullptr) { - ble_gap_conn_desc desc; - rc = ble_gap_conn_find(event->connect.conn_handle, &desc); - assert(rc == 0); - server->m_pServerCallbacks->onConnect(server); - server->m_pServerCallbacks->onConnect(server, &desc); - } + //if (server->m_pServerCallbacks != nullptr) { + ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + assert(rc == 0); + server->m_pServerCallbacks->onConnect(server); + server->m_pServerCallbacks->onConnect(server, &desc); + //} } return 0; @@ -252,9 +253,9 @@ uint32_t NimBLEServer::getConnectedCount() { case BLE_GAP_EVENT_DISCONNECT: { server->m_connId = BLE_HS_CONN_HANDLE_NONE; - if (server->m_pServerCallbacks != nullptr) { - server->m_pServerCallbacks->onDisconnect(server); - } + //if (server->m_pServerCallbacks != nullptr) { + server->m_pServerCallbacks->onDisconnect(server); + //} /* Connection terminated; resume advertising */ //NimBLEDevice::startAdvertising(); server->removePeerDevice(event->disconnect.conn.conn_handle, false); @@ -304,10 +305,12 @@ uint32_t NimBLEServer::getConnectedCount() { if(rc != 0) { return BLE_ATT_ERR_INVALID_HANDLE; } - - server->m_pServerCallbacks->onAuthenticationComplete(&desc); + // 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; @@ -318,7 +321,8 @@ uint32_t NimBLEServer::getConnectedCount() { if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; - pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer + //pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer + 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); @@ -329,12 +333,15 @@ uint32_t NimBLEServer::getConnectedCount() { if(NimBLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = NimBLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); ///////////////////////////////////////////// - }else if(server->m_pServerCallbacks != nullptr) { + } else { + pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } + /* }else if(server->m_pServerCallbacks != nullptr) { pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); }else{ pkey.numcmp_accept = false; } - + */ 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); @@ -356,14 +363,17 @@ uint32_t NimBLEServer::getConnectedCount() { if(NimBLEDevice::m_securityCallbacks != nullptr) { pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest(); ///////////////////////////////////////////// - }else if(server->m_pServerCallbacks != nullptr) { + } else { + pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + } + /* }else if(server->m_pServerCallbacks != nullptr) { pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); }else{ pkey.passkey = 0; NIMBLE_LOGE(LOG_TAG, "No Callback! Sending 0 as the passkey"); } -; + */ 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); @@ -394,7 +404,11 @@ uint32_t NimBLEServer::getConnectedCount() { * @param [in] pCallbacks The callbacks to be invoked. */ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) { - m_pServerCallbacks = pCallbacks; + if (pCallbacks != nullptr){ + m_pServerCallbacks = pCallbacks; + } else { + m_pServerCallbacks = &defaultCallbacks; + } } // setCallbacks /* @@ -471,6 +485,26 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) { NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default"); } // onDisconnect +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; +} /* multi connect support */ void NimBLEServer::updatePeerMTU(uint16_t conn_id, uint16_t mtu) { diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 4402485b..5b2d90eb 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -141,11 +141,11 @@ class NimBLEServerCallbacks { */ virtual void onDisconnect(NimBLEServer* pServer); - virtual uint32_t onPassKeyRequest(){return 0;} - virtual void onPassKeyNotify(uint32_t pass_key){} - virtual bool onSecurityRequest(){return true;} - virtual void onAuthenticationComplete(ble_gap_conn_desc*){}; - virtual bool onConfirmPIN(uint32_t pin){return true;} + virtual uint32_t onPassKeyRequest(); //{return 0;} + virtual void onPassKeyNotify(uint32_t pass_key); //{} + virtual bool onSecurityRequest(); //{return true;} + virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{}; + virtual bool onConfirmPIN(uint32_t pin);//{return true;} }; // BLEServerCallbacks From 2400767cf6b668e9bf7f7ac4d67fb4db8d76b7c7 Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 18 Mar 2020 22:10:43 -0600 Subject: [PATCH 34/62] Cleanup comments in BLE_client. Add Difference comments to BLE_scan. --- examples/Original_examples/BLE_client/BLE_client.ino | 7 +++---- examples/Original_examples/BLE_scan/BLE_scan.ino | 12 ++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/examples/Original_examples/BLE_client/BLE_client.ino b/examples/Original_examples/BLE_client/BLE_client.ino index 195195f4..663c0490 100644 --- a/examples/Original_examples/BLE_client/BLE_client.ino +++ b/examples/Original_examples/BLE_client/BLE_client.ino @@ -123,10 +123,9 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { /** * Called for each advertising BLE server. */ -/******************************************************** - void onResult(BLEAdvertisedDevice advertisedDevice) { - Only a reference to the advertised device is passed now -********************************************************/ + +/*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ void onResult(BLEAdvertisedDevice* advertisedDevice) { Serial.print("BLE Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); diff --git a/examples/Original_examples/BLE_scan/BLE_scan.ino b/examples/Original_examples/BLE_scan/BLE_scan.ino index a0725164..8de1ad8d 100644 --- a/examples/Original_examples/BLE_scan/BLE_scan.ino +++ b/examples/Original_examples/BLE_scan/BLE_scan.ino @@ -3,6 +3,15 @@ Ported to Arduino ESP32 by Evandro Copercini */ +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ + #include #include #include @@ -12,7 +21,10 @@ int scanTime = 5; //In seconds BLEScan* pBLEScan; class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ void onResult(BLEAdvertisedDevice* advertisedDevice) { + /** Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); **/ Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str()); } }; From b26221a467b3d988f20d2e7684c5bc08fd6a2ffd Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 18 Mar 2020 23:03:11 -0600 Subject: [PATCH 35/62] Add NimBLE difference comments for BLE_server, BLE_server_multiconnect, BLE_uart and BLE_write original examples. --- .../BLE_server/BLE_server.ino | 19 ++++++-- .../BLE_server_multiconnect.ino | 41 ++++++++++++++++- .../Original_examples/BLE_uart/BLE_uart.ino | 45 +++++++++++++++++-- .../Original_examples/BLE_write/BLE_write.ino | 20 +++++++-- 4 files changed, 113 insertions(+), 12 deletions(-) diff --git a/examples/Original_examples/BLE_server/BLE_server.ino b/examples/Original_examples/BLE_server/BLE_server.ino index 21383f09..b288eabc 100644 --- a/examples/Original_examples/BLE_server/BLE_server.ino +++ b/examples/Original_examples/BLE_server/BLE_server.ino @@ -4,6 +4,14 @@ updates by chegewara */ +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ + #include #include #include @@ -22,9 +30,14 @@ void setup() { BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - PROPERTY_READ | - PROPERTY_WRITE + CHARACTERISTIC_UUID, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + **********************************************/ + PROPERTY_READ | + PROPERTY_WRITE ); pCharacteristic->setValue("Hello World says Neil"); diff --git a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino index b8c58665..e2fad0e6 100644 --- a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino +++ b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -19,10 +19,18 @@ A connect hander associated with the server starts a background task that performs notification every couple of seconds. */ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ #include #include #include -#include BLEServer* pServer = NULL; BLECharacteristic* pCharacteristic = NULL; @@ -37,6 +45,8 @@ uint32_t value = 0; #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; @@ -46,6 +56,22 @@ class MyServerCallbacks: public BLEServerCallbacks { void onDisconnect(BLEServer* pServer) { deviceConnected = false; } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ }; @@ -66,6 +92,13 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_INDICATE + ); + **********************************************/ PROPERTY_READ | PROPERTY_WRITE | PROPERTY_NOTIFY | @@ -74,7 +107,11 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - pCharacteristic->createDescriptor("2902"); + /******* New createDescriptor method ******** + Add properties the same as characteristics now + pCharacteristic->addDescriptor(new BLE2902()); + ********************************************/ + pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); // Start the service pService->start(); diff --git a/examples/Original_examples/BLE_uart/BLE_uart.ino b/examples/Original_examples/BLE_uart/BLE_uart.ino index ffc703de..19e2bf08 100644 --- a/examples/Original_examples/BLE_uart/BLE_uart.ino +++ b/examples/Original_examples/BLE_uart/BLE_uart.ino @@ -19,6 +19,15 @@ In this example rxValue is the data received (only accessible inside that function). And txValue is the data to be sent, in this example just a byte incremented every second. */ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +#include +***********************/ #include #include #include @@ -38,6 +47,8 @@ uint8_t txValue = 0; #define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; @@ -46,6 +57,22 @@ class MyServerCallbacks: public BLEServerCallbacks { void onDisconnect(BLEServer* pServer) { deviceConnected = false; } + /***************** New - Security handled here ******************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server PassKeyRequest"); + return 123456; + } + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + return true; + } + + void onAuthenticationComplete(ble_gap_conn_desc desc){ + Serial.println("Starting BLE work!"); + } + /*******************************************************************/ }; class MyCallbacks: public BLECharacteristicCallbacks { @@ -81,13 +108,25 @@ void setup() { // Create a BLE Characteristic pTxCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID_TX, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_WRITE + ); + **********************************************/ PROPERTY_NOTIFY ); - - pTxCharacteristic->createDescriptor("2902"); + + /******* New createDescriptor method ******** + Add properties the same as characteristics now + pTxCharacteristic->addDescriptor(new BLE2902()); + ********************************************/ + pTxCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_RX, + CHARACTERISTIC_UUID_RX, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_WRITE + ); + **********************************************/ PROPERTY_WRITE ); diff --git a/examples/Original_examples/BLE_write/BLE_write.ino b/examples/Original_examples/BLE_write/BLE_write.ino index ed7fecfe..a92d526e 100644 --- a/examples/Original_examples/BLE_write/BLE_write.ino +++ b/examples/Original_examples/BLE_write/BLE_write.ino @@ -3,6 +3,13 @@ Ported to Arduino ESP32 by Evandro Copercini */ +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** +#include +#include +#include +***********************/ #include #include #include @@ -45,10 +52,15 @@ void setup() { BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - PROPERTY_READ | - PROPERTY_WRITE - ); + CHARACTERISTIC_UUID, + /************** Defined Values now ************ + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE + ); + **********************************************/ + PROPERTY_READ | + PROPERTY_WRITE + ); pCharacteristic->setCallbacks(new MyCallbacks()); From b0ca28237c2afe78d56edb94b624e267ddd5891c Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 19 Mar 2020 18:07:24 -0600 Subject: [PATCH 36/62] Fix Ardruino logging levels. --- src/NimBLELog.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/NimBLELog.h b/src/NimBLELog.h index 55aff696..348da13e 100644 --- a/src/NimBLELog.h +++ b/src/NimBLELog.h @@ -13,28 +13,31 @@ #include "syscfg/syscfg.h" #include "modlog/modlog.h" -//If Arduino is being used, strip out the colors and ignore log printing below ui setting. + +// If Arduino is being used, strip out the colors and ignore log printing below ui setting. +// Note: because CONFIG_LOG_DEFAULT_LEVEL is set at ERROR in Arduino we must use MODLOG_DFLT(ERROR +// otherwise no messages will be printed above that level. #ifdef ARDUINO_ARCH_ESP32 -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG -#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(DEBUG, "D %s: "#format"\n",tag,##__VA_ARGS__) +#if CORE_DEBUG_LEVEL >= 4 +#define NIMBLE_LOGD( tag, format, ... ) MODLOG_DFLT(ERROR, "D %s: "#format"\n",tag,##__VA_ARGS__) #else #define NIMBLE_LOGD( tag, format, ... ) #endif -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO -#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(INFO, "I %s: "#format"\n",tag,##__VA_ARGS__) +#if CORE_DEBUG_LEVEL >= 3 +#define NIMBLE_LOGI( tag, format, ... ) MODLOG_DFLT(ERROR, "I %s: "#format"\n",tag,##__VA_ARGS__) #else #define NIMBLE_LOGI( tag, format, ... ) #endif -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN -#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(WARN, "W %s: "#format"\n",tag,##__VA_ARGS__) +#if CORE_DEBUG_LEVEL >= 2 +#define NIMBLE_LOGW( tag, format, ... ) MODLOG_DFLT(ERROR, "W %s: "#format"\n",tag,##__VA_ARGS__) #else #define NIMBLE_LOGW( tag, format, ... ) #endif -#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#if CORE_DEBUG_LEVEL >= 1 #define NIMBLE_LOGE( tag, format, ... ) MODLOG_DFLT(ERROR, "E %s: "#format"\n",tag,##__VA_ARGS__) #else #define NIMBLE_LOGE( tag, format, ... ) From 0a035d431dff5734e6785f1d5a6302892edac4f7 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 19 Mar 2020 19:59:35 -0600 Subject: [PATCH 37/62] Fix Descriptor read/write. Reduce log spam. --- src/NimBLECharacteristic.cpp | 15 ++++++++------- src/NimBLEDescriptor.cpp | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index e6fa5742..89d0b280 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -257,16 +257,19 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { auto it = p2902->m_subscribedMap.find(event->subscribe.conn_handle); - if(it == p2902->m_subscribedMap.cend()) { + if(subVal > 0 && it == p2902->m_subscribedMap.cend()) { p2902->m_subscribedMap.insert(std::pair(event->subscribe.conn_handle, subVal)); return; + } else if(it != p2902->m_subscribedMap.cend()) { + p2902->m_subscribedMap.erase(event->subscribe.conn_handle); + return; } - +/* if(event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) { p2902->m_subscribedMap.erase(event->subscribe.conn_handle); return; } - +*/ (*it).second = subVal; } @@ -317,7 +320,7 @@ void NimBLECharacteristic::notify(bool is_notification) { os_mbuf *om; if(_mtu == 0) { - NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); + //NIMBLE_LOGD(LOG_TAG, "peer not connected, removing from map"); p2902->m_subscribedMap.erase((*it).first); it = p2902->m_subscribedMap.cbegin(); if(it == p2902->m_subscribedMap.cend()) { @@ -326,14 +329,12 @@ void NimBLECharacteristic::notify(bool is_notification) { continue; } - NIMBLE_LOGD(LOG_TAG, "Client MTU = %d", _mtu); - if (length > _mtu - 3) { NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3); } if((*it).second == 0) { - NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client"); + //NIMBLE_LOGI(LOG_TAG, "Skipping unsubscribed client"); continue; } diff --git a/src/NimBLEDescriptor.cpp b/src/NimBLEDescriptor.cpp index b2a5ebae..82833450 100644 --- a/src/NimBLEDescriptor.cpp +++ b/src/NimBLEDescriptor.cpp @@ -128,18 +128,18 @@ int NimBLEDescriptor::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle, NimBLEDescriptor* pDescriptor = (NimBLEDescriptor*)arg; NIMBLE_LOGD(LOG_TAG, "Descriptor %s %s event", pDescriptor->getUUID().toString().c_str(), - ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR ? "Read" : "Write"); + ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC ? "Read" : "Write"); uuid = ctxt->chr->uuid; if(ble_uuid_cmp(uuid, &pDescriptor->getUUID().getNative()->u) == 0){ switch(ctxt->op) { - case BLE_GATT_ACCESS_OP_READ_CHR: { + case BLE_GATT_ACCESS_OP_READ_DSC: { pDescriptor->m_pCallbacks->onRead(pDescriptor); rc = os_mbuf_append(ctxt->om, pDescriptor->getValue(), pDescriptor->getLength()); return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; } - case BLE_GATT_ACCESS_OP_WRITE_CHR: { + case BLE_GATT_ACCESS_OP_WRITE_DSC: { if (ctxt->om->om_len > BLE_ATT_ATTR_MAX_LEN) { return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN; } From f12c9e0286e14056fd520155bec8f5554e9bb1ff Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 19 Mar 2020 21:33:02 -0600 Subject: [PATCH 38/62] Reduce log spam while scanning. Scanning now only invokes callback when scan response received if active scanning. --- src/NimBLEScan.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 404530df..01df6918 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -96,7 +96,7 @@ NimBLEScan::NimBLEScan() { NimBLEAddress advertisedAddress(event->disc.addr); // Print advertisement data - print_adv_fields(&fields); + // print_adv_fields(&fields); // If we are not scanning, nothing to do with the extra results. if (pScan->m_stopped) { @@ -128,20 +128,27 @@ NimBLEScan::NimBLEScan() { NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str()); } advertisedDevice->setRSSI(event->disc.rssi); + // NIMBLE_LOGI(LOG_TAG, "advertisement type: %d, %s",advType, NimBLEUtils::advTypeToString(event->disc.event_type)); advertisedDevice->setAdvType(event->disc.event_type); advertisedDevice->parseAdvertisement(&fields); advertisedDevice->setScan(pScan); advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data); if (pScan->m_pAdvertisedDeviceCallbacks) { - pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + // If not active scanning report the result to the listener. + if(pScan->m_scan_params.passive) { + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + // Otherwise wait for the scan response so we can report all of the data at once. + } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + } //m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } return 0; } case BLE_GAP_EVENT_DISC_COMPLETE: { - NIMBLE_LOGI(LOG_TAG, "discovery complete; reason=%d\n", + NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason); pScan->m_stopped = true; From 8199d1e8b67251ce08210d2875116a30c8aa9a04 Mon Sep 17 00:00:00 2001 From: Bernd Giesecke Date: Fri, 20 Mar 2020 22:44:53 +0800 Subject: [PATCH 39/62] Fix data parsing of Eddystone TLM data frame (#2) * Fix data parsing of Eddystone TLM data frame Add Beacon scanner example to show usage of BLEEddystoneTLM class and BLEEddystoneURL class Add EddystoneTLM beacon example Add EddystoneURL beacon example * Fix copy and paste error --- .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino | 166 +++++++++++++++ .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.md | 9 + .../BLE_EddystoneTLM_Beacon.ino | 116 +++++++++++ .../BLE_EddystoneTLM_Beacon.md | 14 ++ .../BLE_EddystoneURL_Beacon.ino | 192 ++++++++++++++++++ .../BLE_EddystoneURL_Beacon.md | 14 ++ src/NimBLEEddystoneTLM.cpp | 27 ++- 7 files changed, 529 insertions(+), 9 deletions(-) create mode 100644 examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino create mode 100644 examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md create mode 100644 examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino create mode 100644 examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md create mode 100644 examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino create mode 100644 examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md diff --git a/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino new file mode 100644 index 00000000..ffc62fbd --- /dev/null +++ b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -0,0 +1,166 @@ +/* + Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp + Ported to Arduino ESP32 by Evandro Copercini +*/ + +/** NimBLE differences highlighted in comment blocks **/ + +/*******original******** + #include + #include + #include + #include + #include "BLEEddystoneURL.h" + #include "BLEEddystoneTLM.h" + #include "BLEBeacon.h" +***********************/ + +#include + +#include +#include +#include +#include +#include "NimBLEEddystoneURL.h" +#include "NimBLEEddystoneTLM.h" +#include "NimBLEBeacon.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) + +int scanTime = 5; //In seconds +BLEScan *pBLEScan; + +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks +{ + /*** Only a reference to the advertised device is passed now + void onResult(BLEAdvertisedDevice advertisedDevice) { **/ + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + if (advertisedDevice->haveName()) + { + Serial.print("Device name: "); + Serial.println(advertisedDevice->getName().c_str()); + Serial.println(""); + } + + if (advertisedDevice->haveServiceUUID()) + { + BLEUUID devUUID = advertisedDevice->getServiceUUID(); + Serial.print("Found ServiceUUID: "); + Serial.println(devUUID.toString().c_str()); + Serial.println(""); + } + else + { + if (advertisedDevice->haveManufacturerData() == true) + { + std::string strManufacturerData = advertisedDevice->getManufacturerData(); + + uint8_t cManufacturerData[100]; + strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); + + if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) + { + Serial.println("Found an iBeacon!"); + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(strManufacturerData); + Serial.printf("iBeacon Frame\n"); + Serial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()); + } + else + { + Serial.println("Found another manufacturers beacon!"); + Serial.printf("strManufacturerData: %d ", strManufacturerData.length()); + for (int i = 0; i < strManufacturerData.length(); i++) + { + Serial.printf("[%X]", cManufacturerData[i]); + } + Serial.printf("\n"); + } + } + return; + } + + uint8_t *payLoad = advertisedDevice->getPayload(); + + BLEUUID checkUrlUUID = (uint16_t)0xfeaa; + + if (advertisedDevice->getServiceUUID().equals(checkUrlUUID)) + { + if (payLoad[11] == 0x10) + { + Serial.println("Found an EddystoneURL beacon!"); + BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); + std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! + + foundEddyURL.setData(eddyContent); + std::string bareURL = foundEddyURL.getURL(); + if (bareURL[0] == 0x00) + { + size_t payLoadLen = advertisedDevice->getPayloadLength(); + Serial.println("DATA-->"); + for (int idx = 0; idx < payLoadLen; idx++) + { + Serial.printf("0x%08X ", payLoad[idx]); + } + Serial.println("\nInvalid Data"); + return; + } + + Serial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str()); + Serial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str()); + Serial.printf("TX power %d\n", foundEddyURL.getPower()); + Serial.println("\n"); + } + else if (payLoad[11] == 0x20) + { + Serial.println("Found an EddystoneTLM beacon!"); + BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); + std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! + + eddyContent = "01234567890123"; + + for (int idx = 0; idx < 14; idx++) + { + eddyContent[idx] = payLoad[idx + 11]; + } + + foundEddyURL.setData(eddyContent); + Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); + Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); + int temp = (int)payLoad[16] + (int)(payLoad[15] << 8); + float calcTemp = temp / 256.0f; + Serial.printf("Reported temperature from data: %.2fC\n", calcTemp); + Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); + Serial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime()); + Serial.println("\n"); + Serial.print(foundEddyURL.toString().c_str()); + Serial.println("\n"); + } + } + } +}; + +void setup() +{ + Serial.begin(115200); + Serial.println("Scanning..."); + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); //create new scan + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); // less or equal setInterval value +} + +void loop() +{ + // put your main code here, to run repeatedly: + BLEScanResults foundDevices = pBLEScan->start(scanTime, false); + Serial.print("Devices found: "); + Serial.println(foundDevices.getCount()); + Serial.println("Scan done!"); + pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory + delay(2000); +} diff --git a/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md new file mode 100644 index 00000000..558c3e7a --- /dev/null +++ b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.md @@ -0,0 +1,9 @@ +## BLE Beacon Scanner + +Initiates a BLE device scan. +Checks if the discovered devices are +- an iBeacon +- an Eddystone TLM beacon +- an Eddystone URL beacon + +and sends the decoded beacon information over Serial log \ No newline at end of file diff --git a/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino new file mode 100644 index 00000000..58f0fa94 --- /dev/null +++ b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino @@ -0,0 +1,116 @@ +/* + EddystoneTLM beacon for NimBLE by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino + EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ +#include "sys/time.h" + +#include + +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" +#include "NimBLEBeacon.h" +#include "NimBLEAdvertising.h" +#include "NimBLEEddystoneURL.h" + +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval nowTimeStruct; + +time_t lastTenth; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +// Check +// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md +// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm +// for the temperature value. It is a 8.8 fixed-point notation +void setBeacon() +{ + char beacon_data[25]; + uint16_t beconUUID = 0xFEAA; + uint16_t volt = random(2800, 3700); // 3300mV = 3.3V + float tempFloat = random(2000, 3100) / 100.0f; + Serial.printf("Random temperature is %.2fC\n", tempFloat); + int temp = (int)(tempFloat * 256); //(uint16_t)((float)23.00); + Serial.printf("Converted to 8.8 format %0X%0X\n", (temp >> 8), (temp & 0xFF)); + + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + oScanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + oScanResponseData.setCompleteServices(BLEUUID(beconUUID)); + + beacon_data[0] = 0x20; // Eddystone Frame Type (Unencrypted Eddystone-TLM) + beacon_data[1] = 0x00; // TLM version + beacon_data[2] = (volt >> 8); // Battery voltage, 1 mV/bit i.e. 0xCE4 = 3300mV = 3.3V + beacon_data[3] = (volt & 0xFF); // + beacon_data[4] = (temp >> 8); // Beacon temperature + beacon_data[5] = (temp & 0xFF); // + beacon_data[6] = ((bootcount & 0xFF000000) >> 24); // Advertising PDU count + beacon_data[7] = ((bootcount & 0xFF0000) >> 16); // + beacon_data[8] = ((bootcount & 0xFF00) >> 8); // + beacon_data[9] = (bootcount & 0xFF); // + beacon_data[10] = ((lastTenth & 0xFF000000) >> 24); // Time since power-on or reboot as 0.1 second resolution counter + beacon_data[11] = ((lastTenth & 0xFF0000) >> 16); // + beacon_data[12] = ((lastTenth & 0xFF00) >> 8); // + beacon_data[13] = (lastTenth & 0xFF); // + + oScanResponseData.setServiceData(BLEUUID(beconUUID), std::string(beacon_data, 14)); + oAdvertisementData.setName("TLMBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&nowTimeStruct, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last); + + last = nowTimeStruct.tv_sec; + lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter + + // Create the BLE Device + BLEDevice::init("TLMBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started for 10s ..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep for 10s\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md new file mode 100644 index 00000000..2e34029d --- /dev/null +++ b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone TLM beacon +EddystoneTLM beacon by BeeGee based on +[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino) + +[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md) + + Create a BLE server that will send periodic Eddystone TLM frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino new file mode 100644 index 00000000..235dd66a --- /dev/null +++ b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino @@ -0,0 +1,192 @@ +/* + EddystoneURL beacon for NimBLE by BeeGee + EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md + +*/ + +/* + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep + +*/ +#include "sys/time.h" + +#include + +#include "NimBLEDevice.h" +#include "NimBLEUtils.h" +#include "NimBLEBeacon.h" +#include "NimBLEAdvertising.h" +#include "NimBLEEddystoneURL.h" + +#include "esp_sleep.h" + +#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up +RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory +RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ +BLEAdvertising *pAdvertising; +struct timeval now; + +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) + +static const char *eddystone_url_prefix_subs[] = { + "http://www.", + "https://www.", + "http://", + "https://", + "urn:uuid:", + NULL +}; + +static const char *eddystone_url_suffix_subs[] = { + ".com/", + ".org/", + ".edu/", + ".net/", + ".info/", + ".biz/", + ".gov/", + ".com", + ".org", + ".edu", + ".net", + ".info", + ".biz", + ".gov", + NULL +}; + +static int string_begin_with(const char *str, const char *prefix) +{ + int prefix_len = strlen(prefix); + if (strncmp(prefix, str, prefix_len) == 0) + { + return prefix_len; + } + return 0; +} + +void setBeacon() +{ + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + + const char url[] = "https://d.giesecke.tk"; + + int scheme_len, ext_len = 1, i, idx, url_idx; + char *ret_data; + int url_len = strlen(url); + + ret_data = (char *)calloc(1, url_len + 13); + + ret_data[0] = 2; // Len + ret_data[1] = 0x01; // Type Flags + ret_data[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04 + ret_data[3] = 3; // Len + ret_data[4] = 0x03; // Type 16-Bit UUID + ret_data[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[6] = 0xFE; // Eddystone UUID 1 MSB + ret_data[7] = 19; // Length of Beacon Data + ret_data[8] = 0x16; // Type Service Data + ret_data[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB + ret_data[10] = 0xFE; // Eddystone UUID 1 MSB + ret_data[11] = 0x10; // Eddystone Frame Type + ret_data[12] = 0xF4; // Beacons TX power at 0m + + i = 0, idx = 13, url_idx = 0; + + //replace prefix + scheme_len = 0; + while (eddystone_url_prefix_subs[i] != NULL) + { + if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0) + { + ret_data[idx] = i; + idx++; + url_idx += scheme_len; + break; + } + i++; + } + while (url_idx < url_len) + { + i = 0; + ret_data[idx] = url[url_idx]; + ext_len = 1; + while (eddystone_url_suffix_subs[i] != NULL) + { + if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0) + { + ret_data[idx] = i; + break; + } + else + { + ext_len = 1; //inc 1 + } + i++; + } + url_idx += ext_len; + idx++; + } + ret_data[7] = idx - 8; + + Serial.printf("struct size %d url size %d reported len %d\n", + url_len + 13, + url_len, ret_data[7]); + + Serial.printf("URL in data %s\n", &ret_data[13]); + + std::string eddyStoneData(ret_data); + + oAdvertisementData.addData(eddyStoneData); + oScanResponseData.setName("MeBeacon"); + pAdvertising->setAdvertisementData(oAdvertisementData); + pAdvertising->setScanResponseData(oScanResponseData); +} + +void setup() +{ + + Serial.begin(115200); + gettimeofday(&now, NULL); + + Serial.printf("start ESP32 %d\n", bootcount++); + + Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last); + + last = now.tv_sec; + + // Create the BLE Device + BLEDevice::init("MeBeacon"); + + BLEDevice::setPower(ESP_PWR_LVL_N12); + + // Create the BLE Server + // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage + + pAdvertising = BLEDevice::getAdvertising(); + + setBeacon(); + // Start advertising + pAdvertising->start(); + Serial.println("Advertizing started..."); + delay(10000); + pAdvertising->stop(); + Serial.printf("enter deep sleep\n"); + esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION); + Serial.printf("in deep sleep\n"); +} + +void loop() +{ +} diff --git a/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md new file mode 100644 index 00000000..2baf1cc5 --- /dev/null +++ b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.md @@ -0,0 +1,14 @@ +## Eddystone URL beacon +EddystoneURL beacon by BeeGee based on +[pcbreflux ESP32 Eddystone URL deepsleep](https://github.com/pcbreflux/espressif/tree/master/esp32/arduino/sketchbook/ESP32_Eddystone_URL_deepsleep) + +[EddystoneURL frame specification](https://github.com/google/eddystone/blob/master/eddystone-url/README.md) + + Create a BLE server that will send periodic Eddystone URL frames. + The design of creating the BLE server is: + 1. Create a BLE Server + 2. Create advertising data + 3. Start advertising. + 4. wait + 5. Stop advertising. + 6. deep sleep diff --git a/src/NimBLEEddystoneTLM.cpp b/src/NimBLEEddystoneTLM.cpp index de9effd2..257ce818 100644 --- a/src/NimBLEEddystoneTLM.cpp +++ b/src/NimBLEEddystoneTLM.cpp @@ -30,7 +30,7 @@ NimBLEEddystoneTLM::NimBLEEddystoneTLM() { m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE; m_eddystoneData.version = 0; m_eddystoneData.volt = 3300; // 3300mV = 3.3V - m_eddystoneData.temp = (uint16_t) ((float) 23.00); + m_eddystoneData.temp = (uint16_t) ((float) 23.00 * 256); // 8.8 fixed format m_eddystoneData.advCount = 0; m_eddystoneData.tmil = 0; } // NimBLEEddystoneTLM @@ -48,19 +48,19 @@ uint8_t NimBLEEddystoneTLM::getVersion() { } // getVersion uint16_t NimBLEEddystoneTLM::getVolt() { - return m_eddystoneData.volt; + return ENDIAN_CHANGE_U16(m_eddystoneData.volt); } // getVolt float NimBLEEddystoneTLM::getTemp() { - return (float)m_eddystoneData.temp; + return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f; } // getTemp uint32_t NimBLEEddystoneTLM::getCount() { - return m_eddystoneData.advCount; + return ENDIAN_CHANGE_U32(m_eddystoneData.advCount); } // getCount uint32_t NimBLEEddystoneTLM::getTime() { - return m_eddystoneData.tmil; + return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10; } // getTime std::string NimBLEEddystoneTLM::toString() { @@ -68,21 +68,30 @@ std::string NimBLEEddystoneTLM::toString() { uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); char val[6]; - out += "Version " + m_eddystoneData.version; + out += "Version "; // + std::string(m_eddystoneData.version); + snprintf(val, sizeof(val), "%d", m_eddystoneData.version); + out += val; out += "\n"; - out += "Battery Voltage " + ENDIAN_CHANGE_U16(m_eddystoneData.volt); + out += "Battery Voltage "; // + ENDIAN_CHANGE_U16(m_eddystoneData.volt); + snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U16(m_eddystoneData.volt)); + out += val; out += " mV\n"; out += "Temperature "; - snprintf(val, sizeof(val), "%d", m_eddystoneData.temp); + snprintf(val, sizeof(val), "%.2f", ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f); out += val; - out += ".0 °C\n"; + out += " C\n"; out += "Adv. Count "; snprintf(val, sizeof(val), "%d", ENDIAN_CHANGE_U32(m_eddystoneData.advCount)); out += val; out += "\n"; + out += "Time in seconds "; + snprintf(val, sizeof(val), "%d", rawsec/10); + out += val; + out += "\n"; + out += "Time "; snprintf(val, sizeof(val), "%04d", rawsec / 864000); From c04b2d4c1a76e4a331184fc6195849d14d994c56 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 20 Mar 2020 14:52:44 -0600 Subject: [PATCH 40/62] Add setInitialConnParams and updateConnParams to client. --- src/NimBLEClient.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++- src/NimBLEClient.h | 7 ++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 875aa514..3e45ab4f 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -52,6 +52,8 @@ NimBLEClient::NimBLEClient() m_conn_id = BLE_HS_CONN_HANDLE_NONE; m_haveServices = false; m_isConnected = false; + m_connectTimeout = 30000; + m_pConnParams = nullptr; } // NimBLEClient @@ -152,7 +154,7 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ * timeout. Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. */ do{ - rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, 30000, NULL, + rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, m_pConnParams, NimBLEClient::handleGapEvent, this); }while(rc == BLE_HS_EBUSY); @@ -233,6 +235,61 @@ int NimBLEClient::disconnect(uint8_t reason) { } // disconnect +/** + * @brief Set the connection paramaters to use when connecting to a server. + */ +void NimBLEClient::setInitialConnectionParams(uint16_t itvl_min, uint16_t itvl_max, + uint16_t latency, uint16_t supervision_timeout) +{ + if(m_pConnParams == nullptr) { + m_pConnParams = (ble_gap_conn_params*)calloc(1, sizeof(ble_gap_conn_params)); + if(m_pConnParams == nullptr) { + NIMBLE_LOGE(LOG_TAG, "setInitialConnectionParams: Error No Mem"); + return; + } + }else if(0 == (itvl_min | itvl_max | latency | supervision_timeout)) { + free(m_pConnParams); + m_pConnParams = nullptr; + return; + } + + m_pConnParams->itvl_min = itvl_min; + m_pConnParams->itvl_max = itvl_max; + m_pConnParams->latency = latency; + m_pConnParams->supervision_timeout = supervision_timeout; + + int rc = ble_gap_check_conn_params( BLE_HCI_LE_PHY_2M, m_pConnParams); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG,"setInitialConnectionParams : %s", NimBLEUtils::returnCodeToString(rc)); + free(m_pConnParams); + m_pConnParams = nullptr; + } +} + + +/** + * Update connection parameters can be called only after connection has been established + */ +void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime, uint16_t maxConnTime) +{ + 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 = minConnTime; // Minimum length of connection event in 0.625ms units + params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = ble_gap_update_params(m_conn_id, ¶ms); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + } +} + + /** * @brief Get the connection id for this client. * @return The connection id. diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 1d760976..79181650 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -49,6 +49,11 @@ class NimBLEClient { uint16_t getConnId(); uint16_t getMTU(); bool secureConnection(); + void setInitialConnectionParams(uint16_t itvl_min, uint16_t itvl_max, + uint16_t latency, uint16_t supervision_timeout); + void updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime, uint16_t maxConnTime); private: @@ -69,6 +74,8 @@ class NimBLEClient { bool m_isConnected = false; // Are we currently connected. bool m_waitingToConnect =false; bool m_deleteCallbacks = true; + int32_t m_connectTimeout; + ble_gap_conn_params* m_pConnParams; //uint16_t m_mtu = 23; From 10e35cf5faa0ccf5782766a9cc95ade2ec22bd49 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 21 Mar 2020 15:59:39 -0600 Subject: [PATCH 41/62] Add setting and updating of client connection params. Add client mtu exchange on connect. Start adding host reset handling to server classes. --- .../BLE_client/BLE_client.ino | 4 - .../BLE_notify/BLE_notify.ino | 1 - src/NimBLEAdvertising.cpp | 5 + src/NimBLEAdvertising.h | 2 + src/NimBLECharacteristic.cpp | 3 +- src/NimBLEClient.cpp | 116 ++++++++++++++---- src/NimBLEClient.h | 14 ++- src/NimBLEDevice.cpp | 16 ++- src/NimBLEDevice.h | 1 + src/NimBLEServer.cpp | 10 +- src/NimBLEServer.h | 2 +- src/NimBLEUtils.cpp | 32 +++++ src/NimBLEUtils.h | 1 + 13 files changed, 166 insertions(+), 41 deletions(-) diff --git a/examples/Original_examples/BLE_client/BLE_client.ino b/examples/Original_examples/BLE_client/BLE_client.ino index 663c0490..6810c0a1 100644 --- a/examples/Original_examples/BLE_client/BLE_client.ino +++ b/examples/Original_examples/BLE_client/BLE_client.ino @@ -57,10 +57,6 @@ class MyClientCallback : public BLEClientCallbacks { Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); return true; } - bool onSecurityRequest(){ - ESP_LOGI(tag, "SecurityRequest"); - return true; - } void onAuthenticationComplete(ble_gap_conn_desc desc){ Serial.println("Starting BLE work!"); diff --git a/examples/Original_examples/BLE_notify/BLE_notify.ino b/examples/Original_examples/BLE_notify/BLE_notify.ino index 7c5eb493..48fdb303 100644 --- a/examples/Original_examples/BLE_notify/BLE_notify.ino +++ b/examples/Original_examples/BLE_notify/BLE_notify.ino @@ -109,7 +109,6 @@ void setup() { pCharacteristic->addDescriptor(new BLE2902()); ********************************************/ pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); - // Start the service pService->start(); diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index 821898c2..b9d801bc 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -384,6 +384,11 @@ void NimBLEAdvertising::stop() { } // stop +void NimBLEAdvertising::onHostReset() { + // m_advSvcsSet = false; +} + + /** * @brief Add data to the payload to be advertised. * @param [in] data The data to be added to the payload. diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index fb6cc35b..9a895fce 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -85,6 +85,8 @@ class NimBLEAdvertising { void setScanResponse(bool); private: + friend class NimBLEDevice; + void onHostReset(); ble_hs_adv_fields m_advData; ble_hs_adv_fields m_scanData; ble_gap_adv_params m_advParams; diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 89d0b280..274ba4f7 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -241,7 +241,8 @@ void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) { subVal |= NIMBLE_DESC_FLAG_INDICATE; } - m_semaphoreConfEvt.give(subVal == 2 ? 0 : NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); + m_semaphoreConfEvt.give((subVal | NIMBLE_DESC_FLAG_INDICATE) ? 0 : + NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED); NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d", event->subscribe.conn_handle, subVal); diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 3e45ab4f..901894ba 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -69,6 +69,10 @@ NimBLEClient::~NimBLEClient() { if(m_deleteCallbacks) { delete m_pClientCallbacks; } + + if(m_pConnParams != nullptr) { + free(m_pConnParams); + } } // ~NimBLEClient @@ -238,29 +242,33 @@ int NimBLEClient::disconnect(uint8_t reason) { /** * @brief Set the connection paramaters to use when connecting to a server. */ -void NimBLEClient::setInitialConnectionParams(uint16_t itvl_min, uint16_t itvl_max, - uint16_t latency, uint16_t supervision_timeout) +void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime, uint16_t maxConnTime) { if(m_pConnParams == nullptr) { m_pConnParams = (ble_gap_conn_params*)calloc(1, sizeof(ble_gap_conn_params)); if(m_pConnParams == nullptr) { - NIMBLE_LOGE(LOG_TAG, "setInitialConnectionParams: Error No Mem"); + NIMBLE_LOGE(LOG_TAG, "setConnectionParams: Error No Mem"); return; } - }else if(0 == (itvl_min | itvl_max | latency | supervision_timeout)) { + }else if(0 == (minInterval | maxInterval | latency | timeout)) { free(m_pConnParams); m_pConnParams = nullptr; return; } - - m_pConnParams->itvl_min = itvl_min; - m_pConnParams->itvl_max = itvl_max; - m_pConnParams->latency = latency; - m_pConnParams->supervision_timeout = supervision_timeout; - - int rc = ble_gap_check_conn_params( BLE_HCI_LE_PHY_2M, m_pConnParams); + m_pConnParams->scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default) + m_pConnParams->scan_window = 16; // Scan window in 0.625ms units (NimBLE Default) + m_pConnParams->itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms + m_pConnParams->itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms + m_pConnParams->latency = latency; // number of packets allowed to skip (extends max interval) + m_pConnParams->supervision_timeout = timeout; // timeout = 400*10ms = 4000ms + m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units + m_pConnParams->max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + + int rc = NimBLEUtils::checkConnParams(m_pConnParams); if(rc != 0) { - NIMBLE_LOGE(LOG_TAG,"setInitialConnectionParams : %s", NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG,"setConnectionParams : %s", NimBLEUtils::returnCodeToString(rc)); free(m_pConnParams); m_pConnParams = nullptr; } @@ -272,21 +280,34 @@ void NimBLEClient::setInitialConnectionParams(uint16_t itvl_min, uint16_t itvl_m */ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, - uint16_t minConnTime, uint16_t maxConnTime) + uint16_t minConnTime, uint16_t maxConnTime) { + if(m_pConnParams == nullptr) { + setConnectionParams(minInterval, maxInterval, latency, timeout, minConnTime, maxConnTime); + } + 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 = minConnTime; // Minimum length of connection event in 0.625ms units - params.max_ce_len = maxConnTime; // Maximum length of connection event in 0.625ms units + params.itvl_max = maxInterval; + params.itvl_min = minInterval; + params.supervision_timeout = timeout; + params.min_ce_len = minConnTime; + params.max_ce_len = maxConnTime; int rc = ble_gap_update_params(m_conn_id, ¶ms); if(rc != 0) { NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); - } + } +} + + +/** + * @brief Set the timeout to wait for connection attempt to complete + * @params[in] Time to wait in seconds. + */ +void NimBLEClient::setConnectTimeout(uint8_t time) { + m_connectTimeout = (uint32_t)(time * 1000); } @@ -536,7 +557,6 @@ uint16_t NimBLEClient::getMTU() { } - /** * @brief Handle a received GAP event. * @@ -550,7 +570,7 @@ uint16_t NimBLEClient::getMTU() { //struct ble_hs_adv_fields fields; int rc; - NIMBLE_LOGI(LOG_TAG, "Got Client event %s for conn handle: %d this handle is: %d", NimBLEUtils::gapEventToString(event->type), event->connect.conn_handle, client->m_conn_id); + NIMBLE_LOGI(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); // Execute handler code based on the type of event received. switch(event->type) { @@ -628,6 +648,12 @@ uint16_t NimBLEClient::getMTU() { // Incase of a multiconnecting device we ignore this device when scanning since we are already connected to it NimBLEDevice::addIgnored(client->m_peerAddress); client->m_semaphoreOpenEvt.give(0); + + rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); + if(rc != 0) { + NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, + NimBLEUtils::returnCodeToString(rc)); + } } else { // Connection attempt failed @@ -670,10 +696,40 @@ uint16_t NimBLEClient::getMTU() { return 0; } // BLE_GAP_EVENT_NOTIFY_RX + case BLE_GAP_EVENT_CONN_UPDATE_REQ: case BLE_GAP_EVENT_L2CAP_UPDATE_REQ: { + if(client->m_conn_id != event->conn_update_req.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); + NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d", + event->conn_update_req.peer_params->itvl_min, + event->conn_update_req.peer_params->itvl_max, + event->conn_update_req.peer_params->latency, + event->conn_update_req.peer_params->supervision_timeout); + rc = 0; + // if we set connection params and the peer is asking for new ones, reject them. + if(client->m_pConnParams != nullptr) { + + if(event->conn_update_req.peer_params->itvl_min != client->m_pConnParams->itvl_min || + event->conn_update_req.peer_params->itvl_max != client->m_pConnParams->itvl_max || + event->conn_update_req.peer_params->latency != client->m_pConnParams->latency || + event->conn_update_req.peer_params->supervision_timeout != client->m_pConnParams->supervision_timeout) + { + //event->conn_update_req.self_params->itvl_min = 6;//client->m_pConnParams->itvl_min; + rc = BLE_ERR_CONN_PARMS; + } + } + if(rc != 0) { + NIMBLE_LOGD(LOG_TAG, "Rejected peer params"); + } + return rc; + } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ + + case BLE_GAP_EVENT_CONN_UPDATE: { + NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); return 0; - } + } // BLE_GAP_EVENT_CONN_UPDATE case BLE_GAP_EVENT_ENC_CHANGE: { if(client->m_conn_id != event->enc_change.conn_handle){ @@ -692,7 +748,19 @@ uint16_t NimBLEClient::getMTU() { client->m_semeaphoreSecEvt.give(event->enc_change.status); return 0; - } + } //BLE_GAP_EVENT_ENC_CHANGE + + case BLE_GAP_EVENT_MTU: { + if(client->m_conn_id != event->mtu.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", + event->mtu.conn_handle, + event->mtu.value); + + //client->m_mtu = event->mtu.value; + return 0; + } // BLE_GAP_EVENT_MTU case BLE_GAP_EVENT_PASSKEY_ACTION: { struct ble_sm_io pkey = {0}; @@ -762,7 +830,7 @@ uint16_t NimBLEClient::getMTU() { } return 0; - } + } // BLE_GAP_EVENT_PASSKEY_ACTION default: { return 0; diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 79181650..6b41607a 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -49,11 +49,15 @@ class NimBLEClient { uint16_t getConnId(); uint16_t getMTU(); bool secureConnection(); - void setInitialConnectionParams(uint16_t itvl_min, uint16_t itvl_max, - uint16_t latency, uint16_t supervision_timeout); - void updateConnParams(uint16_t minInterval, uint16_t maxInterval, + void setConnectTimeout(uint8_t timeout); + void setConnectionParams(uint16_t minInterval, uint16_t maxInterval, uint16_t latency, uint16_t timeout, - uint16_t minConnTime, uint16_t maxConnTime); + uint16_t minConnTime=16, uint16_t maxConnTime=768); + void updateConnParams(uint16_t minInterval, uint16_t maxInterval, + uint16_t latency, uint16_t timeout, + uint16_t minConnTime=16, uint16_t maxConnTime=768); + + private: @@ -76,7 +80,7 @@ class NimBLEClient { bool m_deleteCallbacks = true; int32_t m_connectTimeout; ble_gap_conn_params* m_pConnParams; - //uint16_t m_mtu = 23; + //uint16_t m_mtu = 23; NimBLEClientCallbacks* m_pClientCallbacks = nullptr; diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index c51e4911..97ce8b47 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -260,8 +260,16 @@ void NimBLEDevice::stopAdvertising() { m_pScan->onHostReset(); } - NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d", reason); - NIMBLE_LOGE(LOG_TAG,"%s", NimBLEUtils::returnCodeToString(reason)); + if(m_pServer != nullptr) { + m_pServer->onHostReset(); + } + + if(m_bleAdvertising != nullptr) { + m_bleAdvertising->onHostReset(); + } + + NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, + NimBLEUtils::returnCodeToString(reason)); } // onReset @@ -484,10 +492,10 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) { * @returns host return code 0 if success. */ /* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) { - if(m_securityCallbacks != nullptr) { + /* if(m_securityCallbacks != nullptr) { m_securityCallbacks->onSecurityRequest(); } - + */ 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)); diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 12dff9dd..99fc8c2c 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -105,6 +105,7 @@ class NimBLEDevice { friend class NimBLEServer; friend class NimBLEClient; friend class NimBLEScan; + friend class NimBLEAdvertising; // friend class NimBLERemoteService; // friend class NimBLERemoteCharacteristic; diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 86f627fe..d754cded 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -295,7 +295,7 @@ uint32_t NimBLEServer::getConnectedCount() { } // BLE_GAP_EVENT_NOTIFY_TX case BLE_GAP_EVENT_CONN_UPDATE: { - NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters"); + NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); return 0; } // BLE_GAP_EVENT_CONN_UPDATE @@ -571,4 +571,12 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle, } } + +void NimBLEServer::onHostReset() { + /* for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) { + (*it).second->m_semaphoreConfEvt.give(0); + } + */ +} + #endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 5b2d90eb..874fb6fc 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -101,7 +101,7 @@ class NimBLEServer { friend class NimBLECharacteristic; friend class NimBLEDevice; friend class NimBLEAdvertising; - + void onHostReset(); // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint16_t m_svcChgChrHdl; diff --git a/src/NimBLEUtils.cpp b/src/NimBLEUtils.cpp index 038c00b8..ad4e7c68 100644 --- a/src/NimBLEUtils.cpp +++ b/src/NimBLEUtils.cpp @@ -44,6 +44,38 @@ void NimBLEUtils::memrcpy(uint8_t* target, uint8_t* source, uint32_t size) { } } // memrcpy +int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) { + /* Check connection interval min */ + if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_min > BLE_HCI_CONN_ITVL_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + /* Check connection interval max */ + if ((params->itvl_max < BLE_HCI_CONN_ITVL_MIN) || + (params->itvl_max > BLE_HCI_CONN_ITVL_MAX) || + (params->itvl_max < params->itvl_min)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection latency */ + if (params->latency > BLE_HCI_CONN_LATENCY_MAX) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check supervision timeout */ + if ((params->supervision_timeout < BLE_HCI_CONN_SPVN_TIMEOUT_MIN) || + (params->supervision_timeout > BLE_HCI_CONN_SPVN_TIMEOUT_MAX)) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + /* Check connection event length */ + if (params->min_ce_len > params->max_ce_len) { + return BLE_ERR_INV_HCI_CMD_PARMS; + } + + return 0; +} + const char* NimBLEUtils::returnCodeToString(int rc) { switch(rc) { diff --git a/src/NimBLEUtils.h b/src/NimBLEUtils.h index ff842439..3c7021db 100644 --- a/src/NimBLEUtils.h +++ b/src/NimBLEUtils.h @@ -29,6 +29,7 @@ class NimBLEUtils { static const char* advTypeToString(uint8_t advType); static const char* returnCodeToString(int rc); static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size); + static int checkConnParams(ble_gap_conn_params* params); }; From 360510ce9f287e5d7d31674574e145c3acb27608 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 21 Mar 2020 22:49:15 -0600 Subject: [PATCH 42/62] Cleanup examples. --- .../BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino | 2 -- .../BLE_EddystoneTLM_Beacon.ino | 13 +++++-------- .../BLE_EddystoneURL_Beacon.ino | 17 +++++------------ .../BLE_iBeacon/BLE_iBeacon.ino | 1 - .../Original_examples/BLE_notify/BLE_notify.ino | 13 ++++++++----- .../Original_examples/BLE_scan/BLE_scan.ino | 3 --- .../Original_examples/BLE_server/BLE_server.ino | 4 +--- .../BLE_server_multiconnect.ino | 13 ++++++++----- .../Original_examples/BLE_uart/BLE_uart.ino | 13 +++++++------ .../Original_examples/BLE_write/BLE_write.ino | 2 -- src/NimBLEScan.cpp | 2 +- 11 files changed, 35 insertions(+), 48 deletions(-) diff --git a/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino index ffc62fbd..fea6b7d5 100644 --- a/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino +++ b/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino @@ -18,8 +18,6 @@ #include #include -#include -#include #include #include "NimBLEEddystoneURL.h" #include "NimBLEEddystoneTLM.h" diff --git a/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino index 58f0fa94..32e0b1e9 100644 --- a/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino +++ b/examples/BLE_EddystoneTLM_Beacon/BLE_EddystoneTLM_Beacon.ino @@ -14,31 +14,28 @@ 6. deep sleep */ -#include "sys/time.h" - -#include #include "NimBLEDevice.h" -#include "NimBLEUtils.h" #include "NimBLEBeacon.h" #include "NimBLEAdvertising.h" #include "NimBLEEddystoneURL.h" +#include "sys/time.h" #include "esp_sleep.h" #define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ BLEAdvertising *pAdvertising; struct timeval nowTimeStruct; time_t lastTenth; -#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) - // Check // https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md // and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm diff --git a/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino index 235dd66a..07879b25 100644 --- a/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino +++ b/examples/BLE_EddystoneURL_Beacon/BLE_EddystoneURL_Beacon.ino @@ -15,29 +15,25 @@ 6. deep sleep */ -#include "sys/time.h" - -#include #include "NimBLEDevice.h" -#include "NimBLEUtils.h" #include "NimBLEBeacon.h" -#include "NimBLEAdvertising.h" #include "NimBLEEddystoneURL.h" +#include "sys/time.h" #include "esp_sleep.h" #define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up + +// UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) +#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" + RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory -// See the following for generating UUIDs: -// https://www.uuidgenerator.net/ BLEAdvertising *pAdvertising; struct timeval now; -#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/) - static const char *eddystone_url_prefix_subs[] = { "http://www.", "https://www.", @@ -171,9 +167,6 @@ void setup() BLEDevice::setPower(ESP_PWR_LVL_N12); - // Create the BLE Server - // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage - pAdvertising = BLEDevice::getAdvertising(); setBeacon(); diff --git a/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino b/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino index 74de21fb..86b97def 100644 --- a/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino +++ b/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino @@ -27,7 +27,6 @@ #include "BLEBeacon.h" ***********************/ #include "NimBLEDevice.h" -#include "NimBLEUtils.h" #include "NimBLEBeacon.h" #include "esp_sleep.h" diff --git a/examples/Original_examples/BLE_notify/BLE_notify.ino b/examples/Original_examples/BLE_notify/BLE_notify.ino index 48fdb303..64412ae2 100644 --- a/examples/Original_examples/BLE_notify/BLE_notify.ino +++ b/examples/Original_examples/BLE_notify/BLE_notify.ino @@ -29,8 +29,6 @@ #include ***********************/ #include -#include -#include BLEServer* pServer = NULL; BLECharacteristic* pCharacteristic = NULL; @@ -104,10 +102,15 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - /******* New createDescriptor method ******** - Add properties the same as characteristics now + /*********** New createDescriptor method ************ + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications + or indications are enabled on a characteristic. + pCharacteristic->addDescriptor(new BLE2902()); - ********************************************/ + ****************************************************/ + /** Add properties the same way as characteristics now **/ + pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); // Start the service pService->start(); diff --git a/examples/Original_examples/BLE_scan/BLE_scan.ino b/examples/Original_examples/BLE_scan/BLE_scan.ino index 8de1ad8d..86cdaf46 100644 --- a/examples/Original_examples/BLE_scan/BLE_scan.ino +++ b/examples/Original_examples/BLE_scan/BLE_scan.ino @@ -13,9 +13,6 @@ ***********************/ #include -#include -#include -#include int scanTime = 5; //In seconds BLEScan* pBLEScan; diff --git a/examples/Original_examples/BLE_server/BLE_server.ino b/examples/Original_examples/BLE_server/BLE_server.ino index b288eabc..53be7a63 100644 --- a/examples/Original_examples/BLE_server/BLE_server.ino +++ b/examples/Original_examples/BLE_server/BLE_server.ino @@ -13,8 +13,6 @@ ***********************/ #include -#include -#include // See the following for generating UUIDs: // https://www.uuidgenerator.net/ @@ -36,7 +34,7 @@ void setup() { BLECharacteristic::PROPERTY_WRITE ); **********************************************/ - PROPERTY_READ | + PROPERTY_READ_ENC | PROPERTY_WRITE ); diff --git a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino index e2fad0e6..b12bcc4a 100644 --- a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino +++ b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -29,8 +29,6 @@ #include ***********************/ #include -#include -#include BLEServer* pServer = NULL; BLECharacteristic* pCharacteristic = NULL; @@ -107,10 +105,15 @@ void setup() { // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml // Create a BLE Descriptor - /******* New createDescriptor method ******** - Add properties the same as characteristics now + /*********** New createDescriptor method ************ + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications + or indications are enabled on a characteristic. + pCharacteristic->addDescriptor(new BLE2902()); - ********************************************/ + ****************************************************/ + /** Add properties the same way as characteristics now **/ + pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); // Start the service diff --git a/examples/Original_examples/BLE_uart/BLE_uart.ino b/examples/Original_examples/BLE_uart/BLE_uart.ino index 19e2bf08..75745b33 100644 --- a/examples/Original_examples/BLE_uart/BLE_uart.ino +++ b/examples/Original_examples/BLE_uart/BLE_uart.ino @@ -29,9 +29,6 @@ #include ***********************/ #include -#include -#include -#include BLEServer *pServer = NULL; BLECharacteristic * pTxCharacteristic; @@ -115,10 +112,14 @@ void setup() { PROPERTY_NOTIFY ); - /******* New createDescriptor method ******** - Add properties the same as characteristics now - pTxCharacteristic->addDescriptor(new BLE2902()); + /******* New createDescriptor method ******** + NOTE: There is no need to create the 2902 descriptor + as it will be created automatically if notifications or + indications are enabled on a characteristic. + + pCharacteristic->addDescriptor(new BLE2902()); ********************************************/ + /** Add properties the same way as characteristics now **/ pTxCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( diff --git a/examples/Original_examples/BLE_write/BLE_write.ino b/examples/Original_examples/BLE_write/BLE_write.ino index a92d526e..593014c9 100644 --- a/examples/Original_examples/BLE_write/BLE_write.ino +++ b/examples/Original_examples/BLE_write/BLE_write.ino @@ -11,8 +11,6 @@ #include ***********************/ #include -#include -#include // See the following for generating UUIDs: // https://www.uuidgenerator.net/ diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 01df6918..2222d5d3 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -105,7 +105,7 @@ NimBLEScan::NimBLEScan() { // Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected if(NimBLEDevice::isIgnored(advertisedAddress)) { - NIMBLE_LOGI(LOG_TAG, "Ignoring device address: %s", advertisedAddress.toString().c_str()); + NIMBLE_LOGI(LOG_TAG, "Ignoring device: address: %s", advertisedAddress.toString().c_str()); return 0; } From da4080686bb84eb8dcf6189008c2ebb30c8dbb15 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 21 Mar 2020 23:29:05 -0600 Subject: [PATCH 43/62] Fix backward compatibility of static security pin. --- src/NimBLEServer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index d754cded..427b3317 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -321,8 +321,13 @@ uint32_t NimBLEServer::getConnectedCount() { if (event->passkey.params.action == BLE_SM_IOACT_DISP) { pkey.action = event->passkey.params.action; - //pkey.passkey = NimBLEDevice::m_passkey; // This is the passkey to be entered on peer - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + // 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); From 7be772f8f94b20156e3dc655a9974c5a9f48a667 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 22 Mar 2020 07:28:17 -0600 Subject: [PATCH 44/62] Fixed security function typos. --- src/NimBLEDevice.cpp | 4 ++-- src/NimBLEDevice.h | 4 ++-- src/NimBLESecurity.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 97ce8b47..fbadea50 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -442,7 +442,7 @@ bool NimBLEDevice::getInitialized() { ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK */ -/*STATIC*/void NimBLEDevice::setsScurityInitKey(uint8_t init_key) { +/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { ble_hs_cfg.sm_our_key_dist = init_key; } // setsScurityInitKey @@ -456,7 +456,7 @@ bool NimBLEDevice::getInitialized() { ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK */ -/*STATIC*/void NimBLEDevice::setsScurityRespKey(uint8_t init_key) { +/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) { ble_hs_cfg.sm_their_key_dist = init_key; } // setsScurityRespKey diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 99fc8c2c..d2b279f9 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -85,8 +85,8 @@ class NimBLEDevice { static void setSecuityAuth(bool bonding, bool mitm, bool sc); static void setSecuityAuth(uint8_t auth_req); static void setSecurityIOCap(uint8_t iocap); - static void setsScurityInitKey(uint8_t init_key); - static void setsScurityRespKey(uint8_t init_key); + static void setSecurityInitKey(uint8_t init_key); + static void setSecurityRespKey(uint8_t init_key); static void setSecurityPasskey(uint32_t pin); static uint32_t getSecurityPasskey(); static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks); diff --git a/src/NimBLESecurity.cpp b/src/NimBLESecurity.cpp index 69f0e84a..8dc72966 100644 --- a/src/NimBLESecurity.cpp +++ b/src/NimBLESecurity.cpp @@ -55,7 +55,7 @@ void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) { * @param key_size is value between 7 and 16 */ void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) { - NimBLEDevice::setsScurityInitKey(init_key); + NimBLEDevice::setSecurityInitKey(init_key); } // setInitEncryptionKey @@ -64,7 +64,7 @@ void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) { * @param key_size is value between 7 and 16 */ void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) { - NimBLEDevice::setsScurityRespKey(resp_key); + NimBLEDevice::setSecurityRespKey(resp_key); } // setRespEncryptionKey From 022cddb4dd9d3ffd504b7d05e241a546853d6d5c Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 22 Mar 2020 09:40:58 -0600 Subject: [PATCH 45/62] Fix server example properties. --- examples/Original_examples/BLE_server/BLE_server.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Original_examples/BLE_server/BLE_server.ino b/examples/Original_examples/BLE_server/BLE_server.ino index 53be7a63..aaac50a3 100644 --- a/examples/Original_examples/BLE_server/BLE_server.ino +++ b/examples/Original_examples/BLE_server/BLE_server.ino @@ -34,7 +34,7 @@ void setup() { BLECharacteristic::PROPERTY_WRITE ); **********************************************/ - PROPERTY_READ_ENC | + PROPERTY_READ | PROPERTY_WRITE ); From 9be886b04e305ab0403e3cfd6fa421405c598428 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 22 Mar 2020 22:50:06 -0600 Subject: [PATCH 46/62] BREAKING CHANGE: characteristic / descriptor properties now an enum defined as NIMBLE_PROPERTY::* to avoid any define conflicts with libraries. Add New server example. --- examples/NimBLE_Server/NimBLE_Server.ino | 220 +++++++++++++++++++++++ src/NimBLECharacteristic.h | 16 +- src/NimBLEDevice.h | 19 +- src/NimBLEServer.cpp | 5 +- src/NimBLEServer.h | 1 + src/NimBLEService.h | 9 +- 6 files changed, 256 insertions(+), 14 deletions(-) create mode 100644 examples/NimBLE_Server/NimBLE_Server.ino diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino new file mode 100644 index 00000000..5111047a --- /dev/null +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -0,0 +1,220 @@ + +/** NimBLE_Server example + * + * Created: on March 22 2020 + * Author: H2zero + * +*/ + +#include +#include +#include + + +NimBLEServer* pServer; + +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ +class ServerCallbacks: public NimBLEServerCallbacks { + void onConnect(NimBLEServer* pServer) { + Serial.println("Client connected"); + Serial.println("Multi-connect support advertising"); + NimBLEDevice::startAdvertising(); + }; + + void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + Serial.print("Client address: "); + Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); + //pServer->updateConnParams( desc->conn_handle, 0x10, 0x10, 0, 400 ); + }; + void onDisconnect(NimBLEServer* pServer) { + Serial.println("Client disconnected - starting advertising"); + NimBLEDevice::startAdvertising(); + }; + +/********************* Security handled here ********************** +****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Server Passkey Request"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!desc->sec_state.encrypted) { + // createServer returns the current server reference unless one is not already created + NimBLEDevice::createServer()->disconnect(desc->conn_handle); + Serial.println("Encrypt connection failed - disconnecting client"); + return; + } + Serial.println("Starting BLE work!"); + } +}; + + +class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { + void onRead(NimBLECharacteristic* pCharacteristic){ + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onRead(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + + void onWrite(NimBLECharacteristic* pCharacteristic){ + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onWrite(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + + void onNotify(NimBLECharacteristic* pCharacteristic){ + Serial.println("Sending notification to client"); + }; + + void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code){ + Serial.print("Notification/Indication status code: "); + Serial.print(s); + Serial.print(", return code: "); + Serial.println(code); + } +}; + + +class DescriptorCallbacks : public NimBLEDescriptorCallbacks +{ + void onWrite(NimBLEDescriptor* pDescriptor) + { + if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))){ + NimBLE2902* p2902 = (NimBLE2902*)pDescriptor; + if(p2902->getNotifications()) + { + Serial.println("Client Subscribed to notfications"); + } + else + { + Serial.println("Client Unubscribed to notfications"); + } + } + else { + std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength()); + Serial.print("Descriptor witten value:"); + Serial.println(dscVal.c_str()); + } + }; + + void onRead(NimBLEDescriptor* pDescriptor) + { + Serial.println("Descriptor onread()"); + }; +}; + +static DescriptorCallbacks dscCallbacks; +static CharacteristicCallbacks chrCallbacks; + +void setup() { + Serial.begin(115200); + Serial.println("Starting NimBLE Server"); + + NimBLEDevice::init("NimBLE-Arduino"); // sets device name + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - The DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecuityAuth(false, false, true); + NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( + "BEEF", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + /** Require a secure connection for read and write access **/ + NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted + NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted + ); + + pBeefCharacteristic->setValue("Burger"); + pBeefCharacteristic->setCallbacks(&chrCallbacks); + + /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with + * either of those uuid's it will create the associated class with the correct properties + * and sizes. However we must cast the returned reference to the correct type as the method + * only returns a pointer to the base NimBLEDescriptor class. + **/ + NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); + pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); + pBeef2904->setCallbacks(&dscCallbacks); + + + NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( + "F00D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY + ); + + pFoodCharacteristic->setValue("Fries"); + pFoodCharacteristic->setCallbacks(&chrCallbacks); + + /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value **/ + NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( + "C01D", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE| + NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted + 20 + ); + pC01Ddsc->setValue("No tip!"); + pC01Ddsc->setCallbacks(&dscCallbacks); + + /** Note a 2902 descriptor does NOT need to be created as any chactateristic with + * notification or indication properties will have one created autmatically. + * Manually creating it is only useful if you wish to handle callback functions + * as shown here. Otherwise this can be removed without loss of functionality. + **/ + NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); + pFood2902->setCallbacks(&dscCallbacks); + + pDeadService->start(); + pBaadService->start(); + + NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(pDeadService->getUUID()); + pAdvertising->addServiceUUID(pBaadService->getUUID()); + pAdvertising->setScanResponse(true); + pAdvertising->start(); + + Serial.println("Advertising Started"); +} + +void loop() { + if(pServer->getConnectedCount()) { + NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); + if(pSvc) { + NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); + if(pChr) { + pChr->notify(true); + } + } + } + + delay(2000); +} \ No newline at end of file diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index 7ed5a72a..e85569b4 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -15,7 +15,7 @@ #define MAIN_NIMBLECHARACTERISTIC_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) - +/* #define PROPERTY_READ BLE_GATT_CHR_F_READ #define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC #define PROPERTY_READ_AUTHEN BLE_GATT_CHR_F_READ_AUTHEN @@ -28,7 +28,7 @@ #define PROPERTY_BROADCAST BLE_GATT_CHR_F_BROADCAST #define PROPERTY_NOTIFY BLE_GATT_CHR_F_NOTIFY #define PROPERTY_INDICATE BLE_GATT_CHR_F_INDICATE - +*/ #include "NimBLEService.h" #include "NimBLEUUID.h" #include "NimBLEValue.h" @@ -77,12 +77,12 @@ class NimBLEDescriptorMap { class NimBLECharacteristic { public: NimBLEDescriptor* createDescriptor(const char* uuid, - uint32_t properties = PROPERTY_READ | - PROPERTY_WRITE, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, uint16_t max_len = 100); NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, - uint32_t properties = PROPERTY_READ | - PROPERTY_WRITE, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, uint16_t max_len = 100); NimBLEDescriptor* getDescriptorByUUID(const char* descriptorUUID); @@ -131,9 +131,9 @@ class NimBLECharacteristic { // friend class NimBLEDescriptor; // friend class NimBLECharacteristicMap; - NimBLECharacteristic(const char* uuid, uint16_t properties = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE, + NimBLECharacteristic(const char* uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, NimBLEService* pService = nullptr); - NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = BLE_GATT_CHR_PROP_READ | BLE_GATT_CHR_PROP_WRITE, + NimBLECharacteristic(NimBLEUUID uuid, uint16_t properties = NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE, NimBLEService* pService = nullptr); virtual ~NimBLECharacteristic(); diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index d2b279f9..ea4017e4 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -17,6 +17,23 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "host/ble_gap.h" + +typedef enum { + READ = BLE_GATT_CHR_F_READ, + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE = BLE_GATT_CHR_F_WRITE, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, + WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, + WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, + WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, + BROADCAST = BLE_GATT_CHR_F_BROADCAST, + NOTIFY = BLE_GATT_CHR_F_NOTIFY, + INDICATE = BLE_GATT_CHR_F_INDICATE +} NIMBLE_PROPERTY; + #include "NimbleScan.h" #include "NimBLEUtils.h" #include "NimBLEClient.h" @@ -59,7 +76,7 @@ #define BLEEddystoneTLM NimBLEEddystoneTLM #define BLEEddystoneURL NimBLEEddystoneURL - + /** * @brief BLE functions. */ diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 427b3317..df0fd54c 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -15,10 +15,10 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "NimBLEDevice.h" #include "NimBLEServer.h" #include "NimBLE2902.h" #include "NimBLEUtils.h" -#include "NimBLEDevice.h" #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEServer"; @@ -416,6 +416,7 @@ void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) { } } // setCallbacks + /* * Remove service */ @@ -426,6 +427,8 @@ void BLEServer::removeService(BLEService* service) { m_serviceMap.removeService(service); } */ + + /** * @brief Start advertising. * diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 874fb6fc..1bbf73a0 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -30,6 +30,7 @@ class NimBLEService; class NimBLECharacteristic; class NimBLEServerCallbacks; + /* TODO possibly refactor this struct */ typedef struct { void *peer_device; // peer device BLEClient or BLEServer - maybe its better to have 2 structures or union here diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 52558fc6..14f4cd5e 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -18,6 +18,7 @@ #if defined(CONFIG_BT_ENABLED) #include "NimBLEServer.h" +#include "NimBLEDevice.h" #include "NimBLEUUID.h" #include "FreeRTOS.h" #include "NimBLECharacteristic.h" @@ -55,12 +56,12 @@ class NimBLECharacteristicMap { class NimBLEService { public: NimBLECharacteristic* createCharacteristic(const char* uuid, - uint32_t properties = PROPERTY_READ | - PROPERTY_WRITE); + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE); NimBLECharacteristic* createCharacteristic(NimBLEUUID uuid, - uint32_t properties = PROPERTY_READ | - PROPERTY_WRITE); + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE); void dump(); NimBLECharacteristic* getCharacteristic(const char* uuid); From 0a6c4c482cc67ff57162d733f93be8597c6db593 Mon Sep 17 00:00:00 2001 From: Bernd Giesecke Date: Mon, 23 Mar 2020 17:59:34 +0800 Subject: [PATCH 47/62] Fix buffer size for .toString() --- src/NimBLEEddystoneTLM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NimBLEEddystoneTLM.cpp b/src/NimBLEEddystoneTLM.cpp index 257ce818..b601d8de 100644 --- a/src/NimBLEEddystoneTLM.cpp +++ b/src/NimBLEEddystoneTLM.cpp @@ -66,7 +66,7 @@ uint32_t NimBLEEddystoneTLM::getTime() { std::string NimBLEEddystoneTLM::toString() { std::string out = ""; uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil); - char val[6]; + char val[12]; out += "Version "; // + std::string(m_eddystoneData.version); snprintf(val, sizeof(val), "%d", m_eddystoneData.version); From 01f601672de231a6bbabf5f717158ae8d0aac856 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 23 Mar 2020 16:07:59 -0600 Subject: [PATCH 48/62] Fix Drunken code changes: Define NIMBLE_PROPERTY enum in NimBLECharacteristic. Refactor original examples to use NIMBLE_PROPERTY --- .../BLE_notify/BLE_notify.ino | 12 +++--- .../BLE_server/BLE_server.ino | 8 ++-- .../BLE_server_multiconnect.ino | 12 +++--- .../Original_examples/BLE_uart/BLE_uart.ino | 26 ++++++------- .../Original_examples/BLE_write/BLE_write.ino | 12 +++--- src/NimBLECharacteristic.h | 37 ++++++++++--------- src/NimBLEDescriptor.h | 2 +- src/NimBLEDevice.h | 17 --------- src/NimBLEServer.cpp | 2 +- src/NimBLEService.h | 4 +- 10 files changed, 59 insertions(+), 73 deletions(-) diff --git a/examples/Original_examples/BLE_notify/BLE_notify.ino b/examples/Original_examples/BLE_notify/BLE_notify.ino index 64412ae2..f57c52e2 100644 --- a/examples/Original_examples/BLE_notify/BLE_notify.ino +++ b/examples/Original_examples/BLE_notify/BLE_notify.ino @@ -87,17 +87,17 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - /************** Defined Values now ************ + /******* Enum Type NIMBLE_PROPERTY now ******* BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE ); **********************************************/ - PROPERTY_READ | - PROPERTY_WRITE | - PROPERTY_NOTIFY | - PROPERTY_INDICATE + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE ); // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml @@ -111,7 +111,7 @@ void setup() { ****************************************************/ /** Add properties the same way as characteristics now **/ - pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); + pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); // Start the service pService->start(); diff --git a/examples/Original_examples/BLE_server/BLE_server.ino b/examples/Original_examples/BLE_server/BLE_server.ino index aaac50a3..82aa70aa 100644 --- a/examples/Original_examples/BLE_server/BLE_server.ino +++ b/examples/Original_examples/BLE_server/BLE_server.ino @@ -29,13 +29,13 @@ void setup() { BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - /************** Defined Values now ************ + /***** Enum Type NIMBLE_PROPERTY now ***** BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); - **********************************************/ - PROPERTY_READ | - PROPERTY_WRITE + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE ); pCharacteristic->setValue("Hello World says Neil"); diff --git a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino index b12bcc4a..02526665 100644 --- a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino +++ b/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino @@ -90,17 +90,17 @@ void setup() { // Create a BLE Characteristic pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - /************** Defined Values now ************ + /******* Enum Type NIMBLE_PROPERTY now ******* BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE ); **********************************************/ - PROPERTY_READ | - PROPERTY_WRITE | - PROPERTY_NOTIFY | - PROPERTY_INDICATE + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::INDICATE ); // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml @@ -114,7 +114,7 @@ void setup() { ****************************************************/ /** Add properties the same way as characteristics now **/ - pCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); + pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); // Start the service pService->start(); diff --git a/examples/Original_examples/BLE_uart/BLE_uart.ino b/examples/Original_examples/BLE_uart/BLE_uart.ino index 75745b33..4c2561c9 100644 --- a/examples/Original_examples/BLE_uart/BLE_uart.ino +++ b/examples/Original_examples/BLE_uart/BLE_uart.ino @@ -104,13 +104,13 @@ void setup() { // Create a BLE Characteristic pTxCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_TX, - /************** Defined Values now ************ + CHARACTERISTIC_UUID_TX, + /******* Enum Type NIMBLE_PROPERTY now ******* BLECharacteristic::PROPERTY_WRITE ); **********************************************/ - PROPERTY_NOTIFY - ); + NIMBLE_PROPERTY::WRITE + ); /******* New createDescriptor method ******** NOTE: There is no need to create the 2902 descriptor @@ -120,16 +120,16 @@ void setup() { pCharacteristic->addDescriptor(new BLE2902()); ********************************************/ /** Add properties the same way as characteristics now **/ - pTxCharacteristic->createDescriptor("2902" /** , PROPERTY_READ | PROPERTY_WRITE **/); + pTxCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/); BLECharacteristic * pRxCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID_RX, - /************** Defined Values now ************ + CHARACTERISTIC_UUID_RX, + /******* Enum Type NIMBLE_PROPERTY now ******* BLECharacteristic::PROPERTY_WRITE ); - **********************************************/ - PROPERTY_WRITE - ); + *********************************************/ + NIMBLE_PROPERTY::WRITE + ); pRxCharacteristic->setCallbacks(new MyCallbacks()); @@ -147,8 +147,8 @@ void loop() { pTxCharacteristic->setValue(&txValue, 1); pTxCharacteristic->notify(); txValue++; - delay(10); // bluetooth stack will go into congestion, if too many packets are sent - } + delay(10); // bluetooth stack will go into congestion, if too many packets are sent + } // disconnecting if (!deviceConnected && oldDeviceConnected) { @@ -159,7 +159,7 @@ void loop() { } // connecting if (deviceConnected && !oldDeviceConnected) { - // do stuff here on connecting + // do stuff here on connecting oldDeviceConnected = deviceConnected; } } diff --git a/examples/Original_examples/BLE_write/BLE_write.ino b/examples/Original_examples/BLE_write/BLE_write.ino index 593014c9..b1eb0f83 100644 --- a/examples/Original_examples/BLE_write/BLE_write.ino +++ b/examples/Original_examples/BLE_write/BLE_write.ino @@ -51,14 +51,14 @@ void setup() { BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, - /************** Defined Values now ************ + /***** Enum Type NIMBLE_PROPERTY now ***** BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE - ); - **********************************************/ - PROPERTY_READ | - PROPERTY_WRITE + BLECharacteristic::PROPERTY_WRITE ); + *****************************************/ + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE + ); pCharacteristic->setCallbacks(new MyCallbacks()); diff --git a/src/NimBLECharacteristic.h b/src/NimBLECharacteristic.h index e85569b4..743178ae 100644 --- a/src/NimBLECharacteristic.h +++ b/src/NimBLECharacteristic.h @@ -15,31 +15,34 @@ #define MAIN_NIMBLECHARACTERISTIC_H_ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -/* -#define PROPERTY_READ BLE_GATT_CHR_F_READ -#define PROPERTY_READ_ENC BLE_GATT_CHR_F_READ_ENC -#define PROPERTY_READ_AUTHEN BLE_GATT_CHR_F_READ_AUTHEN -#define PROPERTY_READ_AUTHOR BLE_GATT_CHR_F_READ_AUTHOR -#define PROPERTY_WRITE BLE_GATT_CHR_F_WRITE -#define PROPERTY_WRITE_NR BLE_GATT_CHR_F_WRITE_NO_RSP -#define PROPERTY_WRITE_ENC BLE_GATT_CHR_F_WRITE_ENC -#define PROPERTY_WRITE_AUTHEN BLE_GATT_CHR_F_WRITE_AUTHEN -#define PROPERTY_WRITE_AUTHOR BLE_GATT_CHR_F_WRITE_AUTHOR -#define PROPERTY_BROADCAST BLE_GATT_CHR_F_BROADCAST -#define PROPERTY_NOTIFY BLE_GATT_CHR_F_NOTIFY -#define PROPERTY_INDICATE BLE_GATT_CHR_F_INDICATE -*/ + +#include "host/ble_hs.h" + +typedef enum { + READ = BLE_GATT_CHR_F_READ, + READ_ENC = BLE_GATT_CHR_F_READ_ENC, + READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, + READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, + WRITE = BLE_GATT_CHR_F_WRITE, + WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, + WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, + WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, + WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, + BROADCAST = BLE_GATT_CHR_F_BROADCAST, + NOTIFY = BLE_GATT_CHR_F_NOTIFY, + INDICATE = BLE_GATT_CHR_F_INDICATE +} NIMBLE_PROPERTY; + #include "NimBLEService.h" +#include "NimBLEDescriptor.h" #include "NimBLEUUID.h" #include "NimBLEValue.h" -#include "NimBLEDescriptor.h" #include "FreeRTOS.h" -#include "host/ble_hs.h" - #include #include + class NimBLEService; class NimBLEDescriptor; class NimBLECharacteristicCallbacks; diff --git a/src/NimBLEDescriptor.h b/src/NimBLEDescriptor.h index 412218fa..4f376a99 100644 --- a/src/NimBLEDescriptor.h +++ b/src/NimBLEDescriptor.h @@ -17,8 +17,8 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include "NimBLEUUID.h" #include "NimBLECharacteristic.h" +#include "NimBLEUUID.h" #include "FreeRTOS.h" #include diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index ea4017e4..a9c15da3 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -17,23 +17,6 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include "host/ble_gap.h" - -typedef enum { - READ = BLE_GATT_CHR_F_READ, - READ_ENC = BLE_GATT_CHR_F_READ_ENC, - READ_AUTHEN = BLE_GATT_CHR_F_READ_AUTHEN, - READ_AUTHOR = BLE_GATT_CHR_F_READ_AUTHOR, - WRITE = BLE_GATT_CHR_F_WRITE, - WRITE_NR = BLE_GATT_CHR_F_WRITE_NO_RSP, - WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC, - WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN, - WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR, - BROADCAST = BLE_GATT_CHR_F_BROADCAST, - NOTIFY = BLE_GATT_CHR_F_NOTIFY, - INDICATE = BLE_GATT_CHR_F_INDICATE -} NIMBLE_PROPERTY; - #include "NimbleScan.h" #include "NimBLEUtils.h" #include "NimBLEClient.h" diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index df0fd54c..66df591e 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -15,10 +15,10 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) -#include "NimBLEDevice.h" #include "NimBLEServer.h" #include "NimBLE2902.h" #include "NimBLEUtils.h" +#include "NimBLEDevice.h" #include "NimBLELog.h" static const char* LOG_TAG = "NimBLEServer"; diff --git a/src/NimBLEService.h b/src/NimBLEService.h index 14f4cd5e..70fed3bb 100644 --- a/src/NimBLEService.h +++ b/src/NimBLEService.h @@ -17,11 +17,11 @@ #include "sdkconfig.h" #if defined(CONFIG_BT_ENABLED) +#include "NimBLECharacteristic.h" #include "NimBLEServer.h" -#include "NimBLEDevice.h" #include "NimBLEUUID.h" #include "FreeRTOS.h" -#include "NimBLECharacteristic.h" + class NimBLEServer; class NimBLECharacteristic; From 54870cc1ac17d47b9d4305648278a4b4024dbb17 Mon Sep 17 00:00:00 2001 From: h2zero Date: Mon, 23 Mar 2020 21:25:57 -0600 Subject: [PATCH 49/62] NimBLE_Server demo complete. Minor changes to NimBLECharacteristic notification return codes. NimBLEUtils returnCodeToString, added Success return code string. --- examples/NimBLE_Server/NimBLE_Server.ino | 56 ++++++++++++++++++------ src/NimBLECharacteristic.cpp | 7 +-- src/NimBLEUtils.cpp | 2 + 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino index 5111047a..eac3bcfe 100644 --- a/examples/NimBLE_Server/NimBLE_Server.ino +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -1,5 +1,7 @@ -/** NimBLE_Server example +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE server library. * * Created: on March 22 2020 * Author: H2zero @@ -11,21 +13,25 @@ #include -NimBLEServer* pServer; - /** None of these are required as they will be handled by the library with defaults. ** ** Remove as you see fit for your needs */ class ServerCallbacks: public NimBLEServerCallbacks { void onConnect(NimBLEServer* pServer) { Serial.println("Client connected"); - Serial.println("Multi-connect support advertising"); + Serial.println("Multi-connect support: start advertising"); NimBLEDevice::startAdvertising(); }; - + /** Alternative onConnect() method to extract details of the connection. + * See: src/ble_gap.h for the details of the ble_gap_conn_desc struct. + */ void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { Serial.print("Client address: "); Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); - //pServer->updateConnParams( desc->conn_handle, 0x10, 0x10, 0, 400 ); + /** We can use the connection handle here to ask for different connection parameters. + * Args: connection handle, min connection interval, max connection interval + * latency, supervision timeout. + */ + pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 800); }; void onDisconnect(NimBLEServer* pServer) { Serial.println("Client disconnected - starting advertising"); @@ -78,12 +84,16 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { Serial.println("Sending notification to client"); }; + + /** The status returned in s is defined in NimBLECharacteristic.h. + * The value returned in code is the NimBLE host return code. + */ void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code){ Serial.print("Notification/Indication status code: "); Serial.print(s); Serial.print(", return code: "); - Serial.println(code); - } + Serial.println(NimBLEUtils::returnCodeToString(code)); + }; }; @@ -92,6 +102,7 @@ class DescriptorCallbacks : public NimBLEDescriptorCallbacks void onWrite(NimBLEDescriptor* pDescriptor) { if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))){ + /** Cast to NimBLE2902 to use the class specific functions. **/ NimBLE2902* p2902 = (NimBLE2902*)pDescriptor; if(p2902->getNotifications()) { @@ -111,25 +122,37 @@ class DescriptorCallbacks : public NimBLEDescriptorCallbacks void onRead(NimBLEDescriptor* pDescriptor) { - Serial.println("Descriptor onread()"); + Serial.print(pDescriptor->getUUID().toString().c_str()); + Serial.println(" Descriptor read"); }; }; + +/** Define callback instances globally to use for multiple Charateristics \ Descriptors **/ static DescriptorCallbacks dscCallbacks; static CharacteristicCallbacks chrCallbacks; +static NimBLEServer* pServer; + void setup() { Serial.begin(115200); Serial.println("Starting NimBLE Server"); NimBLEDevice::init("NimBLE-Arduino"); // sets device name + + /** Uncomment this if you encounter watchdog timer resets after pairing with the device. + * Known bug in the NimBLE host that still needs to be resolved. + */ + //ble_store_clear(); + /** Set the IO capabilities of the device, each option will trigger a different pairing method. * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing - * BLE_HS_IO_NO_INPUT_OUTPUT - The DEFAULT setting - just works pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing */ //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + /** 2 different ways to set security - both calls achieve the same result. * no bonding, no man in the middle protection, secure connections. * These are the default values, only shown here for demonstration. @@ -157,7 +180,7 @@ void setup() { * either of those uuid's it will create the associated class with the correct properties * and sizes. However we must cast the returned reference to the correct type as the method * only returns a pointer to the base NimBLEDescriptor class. - **/ + */ NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); pBeef2904->setCallbacks(&dscCallbacks); @@ -189,23 +212,30 @@ void setup() { * notification or indication properties will have one created autmatically. * Manually creating it is only useful if you wish to handle callback functions * as shown here. Otherwise this can be removed without loss of functionality. - **/ + */ NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); pFood2902->setCallbacks(&dscCallbacks); - + + /** Start the services when finished creating all Characteristics and Descriptors **/ pDeadService->start(); pBaadService->start(); NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + /** Add the services to the advertisment data **/ pAdvertising->addServiceUUID(pDeadService->getUUID()); pAdvertising->addServiceUUID(pBaadService->getUUID()); + /** If your device is battery powered you may consider setting scan response + * to false as it will extend battery life at the expense of less data sent. + */ pAdvertising->setScanResponse(true); pAdvertising->start(); Serial.println("Advertising Started"); } + void loop() { + /** Do your thing here, this just spams notifications to all connected clients */ if(pServer->getConnectedCount()) { NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); if(pSvc) { diff --git a/src/NimBLECharacteristic.cpp b/src/NimBLECharacteristic.cpp index 274ba4f7..e58e7125 100644 --- a/src/NimBLECharacteristic.cpp +++ b/src/NimBLECharacteristic.cpp @@ -308,9 +308,6 @@ void NimBLECharacteristic::notify(bool is_notification) { m_pCallbacks->onNotify(this); int rc = 0; - //os_mbuf *om; - //size_t length = m_value.getValue().length(); - //uint8_t* data = (uint8_t*)m_value.getValue().data(); NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID((uint16_t)0x2902); for (auto it = p2902->m_subscribedMap.cbegin(); it != p2902->m_subscribedMap.cend(); ++it) { @@ -368,7 +365,7 @@ void NimBLECharacteristic::notify(bool is_notification) { rc = m_semaphoreConfEvt.wait(); if(rc == BLE_HS_ETIMEOUT) { - m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, 0); + m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_TIMEOUT, rc); } else if(rc == BLE_HS_EDONE) { m_pCallbacks->onStatus(this, NimBLECharacteristicCallbacks::Status::SUCCESS_INDICATE, rc); } else { @@ -383,7 +380,7 @@ void NimBLECharacteristic::notify(bool is_notification) { } } } - + NIMBLE_LOGD(LOG_TAG, "<< notify"); } // Notify diff --git a/src/NimBLEUtils.cpp b/src/NimBLEUtils.cpp index ad4e7c68..1ac609d4 100644 --- a/src/NimBLEUtils.cpp +++ b/src/NimBLEUtils.cpp @@ -79,6 +79,8 @@ int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) { const char* NimBLEUtils::returnCodeToString(int rc) { switch(rc) { + case 0: + return "SUCCESS"; case BLE_HS_EAGAIN: return "Temporary failure; try again."; case BLE_HS_EALREADY: From f1505044dd3aeb948f13a60cc22db79d8c934d1b Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 24 Mar 2020 15:07:59 -0600 Subject: [PATCH 50/62] Update NimBLE stack to esp-nimble-1.2.0-idf @c4af628. Rename Origianl examples folder to Refactored_original_examples. --- examples/NimBLE_Client/NimBLE_Client.ino | 0 .../BLE_client/BLE_client.ino | 0 .../BLE_iBeacon/BLE_iBeacon.ino | 0 .../BLE_notify/BLE_notify.ino | 0 .../BLE_scan/BLE_scan.ino | 0 .../BLE_server/BLE_server.ino | 0 .../BLE_server_multiconnect.ino | 0 .../BLE_uart/BLE_uart.ino | 0 .../BLE_write/BLE_write.ino | 0 src/esp_nimble_cfg.h | 23 +- src/host/ble_hs_pvcy.h | 40 + src/host/ble_store.h | 7 +- src/nimble/host/src/ble_gap.c | 32 + src/nimble/host/src/ble_hs_conn.c | 30 + src/nimble/host/src/ble_hs_hci.c | 14 +- src/nimble/host/src/ble_hs_hci_evt.c | 26 + src/nimble/host/src/ble_hs_id.c | 76 +- src/nimble/host/src/ble_hs_id_priv.h | 4 + src/nimble/host/src/ble_hs_pvcy.c | 78 +- src/nimble/host/src/ble_hs_pvcy_priv.h | 3 + src/nimble/host/src/ble_hs_resolv.c | 711 ++++++++++++++++++ src/nimble/host/src/ble_hs_resolv_priv.h | 108 +++ src/nimble/host/src/ble_sm.c | 38 + src/nimble/host/src/ble_sm_alg.c | 2 +- src/nimble/host/src/ble_sm_priv.h | 4 + .../store/config/src/ble_store_config_priv.h | 3 + .../host/store/config/src/ble_store_nvs.c | 230 +++++- src/nimconfig.h | 6 + 28 files changed, 1379 insertions(+), 56 deletions(-) create mode 100644 examples/NimBLE_Client/NimBLE_Client.ino rename examples/{Original_examples => Refactored_original_examples}/BLE_client/BLE_client.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_iBeacon/BLE_iBeacon.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_notify/BLE_notify.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_scan/BLE_scan.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_server/BLE_server.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_server_multiconnect/BLE_server_multiconnect.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_uart/BLE_uart.ino (100%) rename examples/{Original_examples => Refactored_original_examples}/BLE_write/BLE_write.ino (100%) create mode 100644 src/host/ble_hs_pvcy.h create mode 100644 src/nimble/host/src/ble_hs_resolv.c create mode 100644 src/nimble/host/src/ble_hs_resolv_priv.h diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino new file mode 100644 index 00000000..e69de29b diff --git a/examples/Original_examples/BLE_client/BLE_client.ino b/examples/Refactored_original_examples/BLE_client/BLE_client.ino similarity index 100% rename from examples/Original_examples/BLE_client/BLE_client.ino rename to examples/Refactored_original_examples/BLE_client/BLE_client.ino diff --git a/examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino b/examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino similarity index 100% rename from examples/Original_examples/BLE_iBeacon/BLE_iBeacon.ino rename to examples/Refactored_original_examples/BLE_iBeacon/BLE_iBeacon.ino diff --git a/examples/Original_examples/BLE_notify/BLE_notify.ino b/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino similarity index 100% rename from examples/Original_examples/BLE_notify/BLE_notify.ino rename to examples/Refactored_original_examples/BLE_notify/BLE_notify.ino diff --git a/examples/Original_examples/BLE_scan/BLE_scan.ino b/examples/Refactored_original_examples/BLE_scan/BLE_scan.ino similarity index 100% rename from examples/Original_examples/BLE_scan/BLE_scan.ino rename to examples/Refactored_original_examples/BLE_scan/BLE_scan.ino diff --git a/examples/Original_examples/BLE_server/BLE_server.ino b/examples/Refactored_original_examples/BLE_server/BLE_server.ino similarity index 100% rename from examples/Original_examples/BLE_server/BLE_server.ino rename to examples/Refactored_original_examples/BLE_server/BLE_server.ino diff --git a/examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino similarity index 100% rename from examples/Original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino rename to examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino diff --git a/examples/Original_examples/BLE_uart/BLE_uart.ino b/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino similarity index 100% rename from examples/Original_examples/BLE_uart/BLE_uart.ino rename to examples/Refactored_original_examples/BLE_uart/BLE_uart.ino diff --git a/examples/Original_examples/BLE_write/BLE_write.ino b/examples/Refactored_original_examples/BLE_write/BLE_write.ino similarity index 100% rename from examples/Original_examples/BLE_write/BLE_write.ino rename to examples/Refactored_original_examples/BLE_write/BLE_write.ino diff --git a/src/esp_nimble_cfg.h b/src/esp_nimble_cfg.h index b2dd7443..8e7ab7ac 100644 --- a/src/esp_nimble_cfg.h +++ b/src/esp_nimble_cfg.h @@ -1,3 +1,4 @@ + #ifndef __ESP_NIMBLE_CFG__ #define __ESP_NIMBLE_CFG__ #include "nimconfig.h" @@ -14,9 +15,9 @@ /*** kernel/os */ #ifndef MYNEWT_VAL_MSYS_1_BLOCK_COUNT #ifdef CONFIG_BT_NIMBLE_MESH -#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (20) +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT + 8) #else -#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (12) +#define MYNEWT_VAL_MSYS_1_BLOCK_COUNT CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT #endif #endif @@ -451,19 +452,23 @@ #endif #ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL +#ifdef CONFIG_BT_NIMBLE_HS_FLOW_CTRL +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL (1) +#else #define MYNEWT_VAL_BLE_HS_FLOW_CTRL (0) #endif +#endif #ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL -#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL (1000) +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_ITVL CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL #endif #ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH -#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH (2) +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_THRESH CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH #endif #ifndef MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT -#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT (0) +#define MYNEWT_VAL_BLE_HS_FLOW_CTRL_TX_ON_DISCONNECT CONFIG_BT_NIMBLE_FLOW_CTRL_TX_ON_DISCONNECT #endif #ifndef MYNEWT_VAL_BLE_HS_PHONY_HCI_ACKS @@ -538,8 +543,12 @@ #define MYNEWT_VAL_BLE_MONITOR_UART_DEV ("uart0") #endif +#ifndef MYNEWT_VAL_BLE_HOST_BASED_PRIVACY +#define MYNEWT_VAL_BLE_HOST_BASED_PRIVACY (1) +#endif + #ifndef MYNEWT_VAL_BLE_RPA_TIMEOUT -#define MYNEWT_VAL_BLE_RPA_TIMEOUT (300) +#define MYNEWT_VAL_BLE_RPA_TIMEOUT (CONFIG_BT_NIMBLE_RPA_TIMEOUT) #endif #ifndef MYNEWT_VAL_BLE_SM_BONDING @@ -1089,4 +1098,4 @@ #define MYNEWT_VAL_BLE_HCI_UART_STOP_BITS (1) #endif -#endif \ No newline at end of file +#endif diff --git a/src/host/ble_hs_pvcy.h b/src/host/ble_hs_pvcy.h new file mode 100644 index 00000000..0ff32b80 --- /dev/null +++ b/src/host/ble_hs_pvcy.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "host/ble_hs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/* Called to configure local(own) privacy (RPA) when using host based privacy. In + * Host based privacy as controller is not aware of RPA, we do it via + * 'BLE_ADDR_RANDOM' addr_type route. + * + * @param enable RPA when enable is not 0 + * disable RPA otherwise + * + * @return return 0 when successful. + * return appropriate error code otherwise + */ +int ble_hs_pvcy_rpa_config(uint8_t enable); +#endif diff --git a/src/host/ble_store.h b/src/host/ble_store.h index 30a5666c..a3eca5d2 100644 --- a/src/host/ble_store.h +++ b/src/host/ble_store.h @@ -27,9 +27,10 @@ extern "C" { #endif -#define BLE_STORE_OBJ_TYPE_OUR_SEC 1 -#define BLE_STORE_OBJ_TYPE_PEER_SEC 2 -#define BLE_STORE_OBJ_TYPE_CCCD 3 +#define BLE_STORE_OBJ_TYPE_OUR_SEC 1 +#define BLE_STORE_OBJ_TYPE_PEER_SEC 2 +#define BLE_STORE_OBJ_TYPE_CCCD 3 +#define BLE_STORE_OBJ_TYPE_PEER_DEV_REC 4 /** Failed to persist record; insufficient storage capacity. */ #define BLE_STORE_EVENT_OVERFLOW 1 diff --git a/src/nimble/host/src/ble_gap.c b/src/nimble/host/src/ble_gap.c index 2d2afdca..0df2d19c 100644 --- a/src/nimble/host/src/ble_gap.c +++ b/src/nimble/host/src/ble_gap.c @@ -24,6 +24,7 @@ #include "host/ble_hs_adv.h" #include "host/ble_hs_hci.h" #include "ble_hs_priv.h" +#include "ble_hs_resolv_priv.h" #if MYNEWT #include "bsp/bsp.h" @@ -381,6 +382,23 @@ ble_gap_fill_conn_desc(struct ble_hs_conn *conn, ble_hs_conn_addrs(conn, &addrs); desc->our_id_addr = addrs.our_id_addr; +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + /* Check if the privacy is enabled, change the address type accordingly + * */ + if (ble_host_rpa_enabled()) + { + uint8_t *local_id = NULL; + struct ble_hs_resolv_entry *rl = NULL; + rl = ble_hs_resolv_list_find(conn->bhc_peer_addr.val); + + if (rl != NULL) { + /* Get public ID address here */ + ble_hs_id_addr(BLE_ADDR_PUBLIC, (const uint8_t **) &local_id, NULL); + memcpy(desc->our_id_addr.val, local_id, BLE_DEV_ADDR_LEN); + desc->our_id_addr.type = BLE_ADDR_PUBLIC; + } + } +#endif desc->peer_id_addr = addrs.peer_id_addr; desc->our_ota_addr = addrs.our_ota_addr; desc->peer_ota_addr = addrs.peer_ota_addr; @@ -1853,6 +1871,11 @@ ble_gap_wl_busy(void) return BLE_HS_ENOTSUP; #endif +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (ble_host_rpa_enabled()) { + return BLE_HS_ENOTSUP; + } +#endif /* Check if an auto or selective connection establishment procedure is in * progress. */ @@ -1904,6 +1927,11 @@ ble_gap_wl_set(const ble_addr_t *addrs, uint8_t white_list_count) return BLE_HS_ENOTSUP; #endif +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (ble_host_rpa_enabled()) { + return BLE_HS_ENOTSUP; + } +#endif int rc; int i; @@ -5196,11 +5224,15 @@ ble_gap_unpair(const ble_addr_t *peer_addr) return BLE_HS_EINVAL; } + ble_hs_lock(); + conn = ble_hs_conn_find_by_addr(peer_addr); if (conn != NULL) { ble_gap_terminate(conn->bhc_handle, BLE_ERR_REM_USER_CONN_TERM); } + ble_hs_unlock(); + ble_hs_pvcy_remove_entry(peer_addr->type, peer_addr->val); diff --git a/src/nimble/host/src/ble_hs_conn.c b/src/nimble/host/src/ble_hs_conn.c index e1e5e7e4..035150b9 100644 --- a/src/nimble/host/src/ble_hs_conn.c +++ b/src/nimble/host/src/ble_hs_conn.c @@ -23,6 +23,7 @@ #include "os/os.h" #include "host/ble_hs_id.h" #include "ble_hs_priv.h" +#include "ble_hs_resolv_priv.h" /** At least three channels required per connection (sig, att, sm). */ #define BLE_HS_CONN_MIN_CHANS 3 @@ -406,6 +407,35 @@ ble_hs_conn_addrs(const struct ble_hs_conn *conn, /* Determine peer address information. */ addrs->peer_id_addr = conn->bhc_peer_addr; addrs->peer_ota_addr = conn->bhc_peer_addr; + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + /* RPA: Override peer address information. */ + struct ble_hs_resolv_entry *rl = NULL; + + ble_addr_t bhc_peer_addr; + bhc_peer_addr.type = conn->bhc_peer_addr.type; + memcpy(bhc_peer_addr.val, conn->bhc_peer_addr.val, BLE_DEV_ADDR_LEN); + if (ble_host_rpa_enabled()) { + + uint8_t *local_id = NULL; + ble_hs_id_addr(BLE_ADDR_PUBLIC, (const uint8_t **) &local_id, NULL); + + rl = ble_hs_resolv_list_find(bhc_peer_addr.val); + if (rl != NULL) { + memcpy(addrs->peer_ota_addr.val, addrs->peer_id_addr.val, BLE_DEV_ADDR_LEN); + memcpy(addrs->peer_id_addr.val, rl->rl_identity_addr, BLE_DEV_ADDR_LEN); + + addrs->peer_id_addr.type = rl->rl_addr_type; + + /* RL is present: populate our id addr with public ID */ + memcpy(addrs->our_id_addr.val, local_id, BLE_DEV_ADDR_LEN); + addrs->our_id_addr.type = BLE_ADDR_PUBLIC; + BLE_HS_LOG(DEBUG, "Revised our id addr:\n"); + ble_hs_log_flat_buf(our_id_addr_val, BLE_DEV_ADDR_LEN); + BLE_HS_LOG(DEBUG, "\n"); + } + } +#endif switch (conn->bhc_peer_addr.type) { case BLE_ADDR_PUBLIC: case BLE_ADDR_RANDOM: diff --git a/src/nimble/host/src/ble_hs_hci.c b/src/nimble/host/src/ble_hs_hci.c index 53712fed..94126679 100644 --- a/src/nimble/host/src/ble_hs_hci.c +++ b/src/nimble/host/src/ble_hs_hci.c @@ -380,18 +380,20 @@ ble_hs_hci_rx_ack(uint8_t *ack_ev) int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg) { - int enqueue; + int enqueue = 0; BLE_HS_DBG_ASSERT(hci_ev != NULL); switch (hci_ev[0]) { case BLE_HCI_EVCODE_COMMAND_COMPLETE: - case BLE_HCI_EVCODE_COMMAND_STATUS: if (hci_ev[3] == 0 && hci_ev[4] == 0) { enqueue = 1; - } else { - ble_hs_hci_rx_ack(hci_ev); - enqueue = 0; + } + break; + + case BLE_HCI_EVCODE_COMMAND_STATUS: + if (hci_ev[4] == 0 && hci_ev[5] == 0) { + enqueue = 1; } break; @@ -402,6 +404,8 @@ ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg) if (enqueue) { ble_hs_enqueue_hci_event(hci_ev); + } else { + ble_hs_hci_rx_ack(hci_ev); } return 0; diff --git a/src/nimble/host/src/ble_hs_hci_evt.c b/src/nimble/host/src/ble_hs_hci_evt.c index 1c4978ab..43cb5b58 100644 --- a/src/nimble/host/src/ble_hs_hci_evt.c +++ b/src/nimble/host/src/ble_hs_hci_evt.c @@ -27,6 +27,7 @@ #include "host/ble_monitor.h" #include "ble_hs_priv.h" #include "ble_hs_dbg_priv.h" +#include "ble_hs_resolv_priv.h" _Static_assert(sizeof (struct hci_data_hdr) == BLE_HCI_DATA_HDR_SZ, "struct hci_data_hdr must be 4 bytes"); @@ -325,6 +326,23 @@ ble_hs_hci_evt_le_conn_complete(uint8_t subevent, uint8_t *data, int len) memcpy(evt.local_rpa, data + 12, BLE_DEV_ADDR_LEN); memcpy(evt.peer_rpa, data + 18, BLE_DEV_ADDR_LEN); extended_offset = 12; +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + /* RPA needs to be resolved here, as controller is not aware of the + * address is RPA in Host based RPA */ + if (ble_host_rpa_enabled()) { + uint8_t *local_id_rpa = ble_hs_get_rpa_local(); + memcpy(evt.local_rpa, local_id_rpa, 6); + + struct ble_hs_resolv_entry *rl = NULL; + ble_rpa_replace_peer_params_with_rl(evt.peer_addr, + &evt.peer_addr_type, &rl); + if (rl == NULL) { + if (ble_rpa_resolv_add_peer_rec(evt.peer_addr) != 0) { + BLE_HS_LOG(DEBUG, "Memory unavailable for new peer record\n"); + } + } + } +#endif } else { memset(evt.local_rpa, 0, BLE_DEV_ADDR_LEN); memset(evt.peer_rpa, 0, BLE_DEV_ADDR_LEN); @@ -420,6 +438,14 @@ ble_hs_hci_evt_le_adv_rpt(uint8_t subevent, uint8_t *data, int len) memcpy(desc.addr.val, data + off, 6); off += 6; +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (ble_host_rpa_enabled()) { + /* Now RPA to be resolved here, since controller is unaware of the + * address is RPA */ + ble_rpa_replace_peer_params_with_rl(desc.addr.val, + &desc.addr.type, NULL); + } +#endif desc.length_data = data[off]; ++off; diff --git a/src/nimble/host/src/ble_hs_id.c b/src/nimble/host/src/ble_hs_id.c index 5346210b..43380d1f 100644 --- a/src/nimble/host/src/ble_hs_id.c +++ b/src/nimble/host/src/ble_hs_id.c @@ -20,10 +20,23 @@ #include #include "host/ble_hs_id.h" #include "ble_hs_priv.h" +#include "ble_hs_resolv_priv.h" static uint8_t ble_hs_id_pub[6]; static uint8_t ble_hs_id_rnd[6]; +bool +ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type) +{ + bool rc = 0; + /* According to spec v4.2, Vol 6, Part B, section 1.3.2.2, the two most + * significant bits of RPA shall be equal to 0 and 1 */ + if (addr_type && ((addr[5] & 0xc0) == 0x40)) { + rc = 1; + } + return rc; +} + void ble_hs_id_set_pub(const uint8_t *pub_addr) { @@ -53,19 +66,76 @@ ble_hs_id_gen_rnd(int nrpa, ble_addr_t *out_addr) return 0; } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/** + * Sets the device's pseudo RPA address when 'Host based privacy' is in use. + * The address type (RPA) is inferred from the most-significant bits. The + * address is specified in host byte order (little-endian!). + * + * @param rnd_addr The RPA address to set. + * + * @return 0 on success; + * BLE_HS_EINVAL if the specified address is not a + * resolvable private address. + * Other nonzero on error. + */ +int +ble_hs_id_set_pseudo_rnd(const uint8_t *rnd_addr) +{ + uint8_t addr_type_byte; + int rc; + int i, rnd_part_sum = 0; + + ble_hs_lock(); + + /* Make sure all bits of rnd_addr are neither one nor zero (3rd, 4th and + * 5th bytes of rnd_addr(RPA) are prand) Vol 6, Part B, section 1.3.2.2 + * The two most significant bits of RPA shall be equal to 0 and 1 */ + addr_type_byte = rnd_addr[5] & 0xc0; + for (i = 3; i < BLE_DEV_ADDR_LEN; i++) { + rnd_part_sum += *(rnd_addr + i); + } + rnd_part_sum -= addr_type_byte; + + /* All ones in random part: 3*(0xFF) - 0x40 = 0x2BD */ + if ((addr_type_byte != 0x40) || + (rnd_part_sum == 0) || (rnd_part_sum == 0x2BD)) { + rc = BLE_HS_EINVAL; + goto done; + } + /* set the RPA address as pseudo random address in controller */ + rc = ble_hs_hci_util_set_random_addr(rnd_addr); + if (rc != 0) { + goto done; + } + + memcpy(ble_hs_id_rnd, rnd_addr, BLE_DEV_ADDR_LEN); + +done: + ble_hs_unlock(); + return rc; +} +#endif + int ble_hs_id_set_rnd(const uint8_t *rnd_addr) { uint8_t addr_type_byte; int rc; - uint8_t all_zeros[BLE_DEV_ADDR_LEN] = {0}, all_ones[BLE_DEV_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + int i, rnd_part_sum = 0; ble_hs_lock(); - /* Make sure all bits of rnd_addr are neither one nor zero */ + /* Make sure random part of rnd_addr is not all ones or zeros */ addr_type_byte = rnd_addr[5] & 0xc0; + for (i = 0; i < BLE_DEV_ADDR_LEN; i++) { + rnd_part_sum += *(rnd_addr + i); + } + rnd_part_sum -= addr_type_byte; + + /* All ones in random part: 5*(0xFF) + 0x3F = 0x53A */ if ((addr_type_byte != 0x00 && addr_type_byte != 0xc0) || - !memcmp(rnd_addr, all_zeros, BLE_DEV_ADDR_LEN) || !memcmp(rnd_addr, all_ones, BLE_DEV_ADDR_LEN)) { + (rnd_part_sum == 0) || (rnd_part_sum == 0x53A)) { rc = BLE_HS_EINVAL; goto done; } diff --git a/src/nimble/host/src/ble_hs_id_priv.h b/src/nimble/host/src/ble_hs_id_priv.h index aa2827d4..c031b951 100644 --- a/src/nimble/host/src/ble_hs_id_priv.h +++ b/src/nimble/host/src/ble_hs_id_priv.h @@ -33,6 +33,10 @@ int ble_hs_id_use_addr(uint8_t addr_type); void ble_hs_id_reset(void); void ble_hs_id_rnd_reset(void); +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +bool ble_hs_is_rpa(uint8_t *addr, uint8_t addr_type); +int ble_hs_id_set_pseudo_rnd(const uint8_t *); +#endif #ifdef __cplusplus } #endif diff --git a/src/nimble/host/src/ble_hs_pvcy.c b/src/nimble/host/src/ble_hs_pvcy.c index e744b55b..32f15974 100644 --- a/src/nimble/host/src/ble_hs_pvcy.c +++ b/src/nimble/host/src/ble_hs_pvcy.c @@ -21,6 +21,7 @@ #include #include "stats/stats.h" #include "ble_hs_priv.h" +#include "ble_hs_resolv_priv.h" static uint8_t ble_hs_pvcy_started; static uint8_t ble_hs_pvcy_irk[16]; @@ -34,6 +35,10 @@ const uint8_t ble_hs_pvcy_default_irk[16] = { static int ble_hs_pvcy_set_addr_timeout(uint16_t timeout) { +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + return ble_hs_resolv_set_rpa_tmo(timeout); +#endif + uint8_t buf[BLE_HCI_SET_RESOLV_PRIV_ADDR_TO_LEN]; int rc; @@ -48,6 +53,7 @@ ble_hs_pvcy_set_addr_timeout(uint16_t timeout) buf, sizeof(buf), NULL, 0, NULL); } +#if (!MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)) static int ble_hs_pvcy_set_resolve_enabled(int enable) { @@ -68,6 +74,7 @@ ble_hs_pvcy_set_resolve_enabled(int enable) return 0; } +#endif int ble_hs_pvcy_remove_entry(uint8_t addr_type, const uint8_t *addr) @@ -81,9 +88,13 @@ ble_hs_pvcy_remove_entry(uint8_t addr_type, const uint8_t *addr) return rc; } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + rc = ble_hs_resolv_list_rmv(buf[0], &buf[1]); +#else rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_RMV_RESOLV_LIST), buf, sizeof(buf), NULL, 0, NULL); +#endif if (rc != 0) { return rc; } @@ -91,6 +102,7 @@ ble_hs_pvcy_remove_entry(uint8_t addr_type, const uint8_t *addr) return 0; } +#if (!MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)) static int ble_hs_pvcy_clear_entries(void) { @@ -105,6 +117,7 @@ ble_hs_pvcy_clear_entries(void) return 0; } +#endif static int ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type, @@ -112,7 +125,6 @@ ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type, { struct hci_add_dev_to_resolving_list add; uint8_t buf[BLE_HCI_ADD_TO_RESOLV_LIST_LEN]; - ble_addr_t peer_addr; int rc; add.addr_type = addr_type; @@ -124,6 +136,14 @@ ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type, if (rc != 0) { return rc; } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + rc = ble_hs_resolv_list_add(buf); + if (rc != 0) { + return rc; + } + +#else + ble_addr_t peer_addr; rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), @@ -144,6 +164,7 @@ ble_hs_pvcy_add_entry_hci(const uint8_t *addr, uint8_t addr_type, if (rc != 0) { return rc; } +#endif return 0; } @@ -160,6 +181,9 @@ ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type, * list (Vol 2, Part E, 7.8.38). Stop all GAP procedures and temporarily * prevent any new ones from being started. */ +#if (MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)) + rc = ble_hs_pvcy_add_entry_hci(addr, addr_type, irk); +#else ble_gap_preempt(); /* Try to add the entry now that GAP is halted. */ @@ -168,6 +192,7 @@ ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addr_type, /* Allow GAP procedures to be started again. */ ble_gap_preempt_done(); +#endif if (rc != 0) { STATS_INC(ble_hs_stats, pvcy_add_entry_fail); } @@ -184,6 +209,11 @@ ble_hs_pvcy_ensure_started(void) return 0; } +#if (MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)) + /*This is to be called only once*/ + ble_hs_resolv_init(); +#endif + /* Set up the periodic change of our RPA. */ rc = ble_hs_pvcy_set_addr_timeout(MYNEWT_VAL(BLE_RPA_TIMEOUT)); if (rc != 0) { @@ -212,6 +242,21 @@ ble_hs_pvcy_set_our_irk(const uint8_t *irk) if (memcmp(ble_hs_pvcy_irk, new_irk, 16) != 0) { memcpy(ble_hs_pvcy_irk, new_irk, 16); +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (irk != NULL) { + bool rpa_state = false; + + if ((rpa_state = ble_host_rpa_enabled()) == true) { + ble_hs_resolv_enable(0); + } + + ble_hs_resolv_list_clear_all(); + + if (rpa_state) { + ble_hs_resolv_enable(1); + } + } +#else rc = ble_hs_pvcy_set_resolve_enabled(0); if (rc != 0) { return rc; @@ -227,6 +272,7 @@ ble_hs_pvcy_set_our_irk(const uint8_t *irk) return rc; } +#endif /* * Add local IRK entry with 00:00:00:00:00:00 address. This entry will * be used to generate RPA for non-directed advertising if own_addr_type @@ -270,3 +316,33 @@ ble_hs_pvcy_set_mode(const ble_addr_t *addr, uint8_t priv_mode) BLE_HCI_OCF_LE_SET_PRIVACY_MODE), buf, sizeof(buf), NULL, 0, NULL); } + +bool +ble_hs_pvcy_enabled(void) +{ + return ble_hs_pvcy_started; +} + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +int +ble_hs_pvcy_rpa_config(uint8_t enable) +{ + int rc = 0; + + if (enable != 0) { + rc = ble_hs_pvcy_ensure_started(); + if (rc != 0) { + return rc; + } + + ble_hs_resolv_enable(true); + + /* Generate local RPA address and set it in controller */ + rc = ble_hs_gen_own_rpa_random(); + } else { + ble_hs_resolv_enable(false); + } + + return rc; +} +#endif diff --git a/src/nimble/host/src/ble_hs_pvcy_priv.h b/src/nimble/host/src/ble_hs_pvcy_priv.h index 7f0aa4b9..86157da0 100644 --- a/src/nimble/host/src/ble_hs_pvcy_priv.h +++ b/src/nimble/host/src/ble_hs_pvcy_priv.h @@ -35,6 +35,9 @@ int ble_hs_pvcy_add_entry(const uint8_t *addr, uint8_t addrtype, const uint8_t *irk); int ble_hs_pvcy_ensure_started(void); int ble_hs_pvcy_set_mode(const ble_addr_t *addr, uint8_t priv_mode); +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +bool ble_hs_pvcy_enabled(void); +#endif #ifdef __cplusplus } diff --git a/src/nimble/host/src/ble_hs_resolv.c b/src/nimble/host/src/ble_hs_resolv.c new file mode 100644 index 00000000..7f241389 --- /dev/null +++ b/src/nimble/host/src/ble_hs_resolv.c @@ -0,0 +1,711 @@ +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "syscfg/syscfg.h" +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + +#include +#include +#include "ble_hs_priv.h" +#include "host/ble_hs_id.h" +#include "nimble/ble.h" +#include "nimble/nimble_opt.h" +#include "ble_hs_resolv_priv.h" +#include "store/config/ble_store_config.h" +#include "../store/config/src/ble_store_config_priv.h" + +/* Resolve list size, additional space to save local device's configuration */ +#define BLE_RESOLV_LIST_SIZE (MYNEWT_VAL(BLE_STORE_MAX_BONDS) + 1) +#define BLE_MAX_RPA_TIMEOUT_VAL 0xA1B8 + +static struct ble_hs_resolv_data g_ble_hs_resolv_data; +static struct ble_hs_resolv_entry g_ble_hs_resolv_list[BLE_RESOLV_LIST_SIZE]; +/* Allocate one extra space for peer_records than no. of Bonds, it will take + * care of storage overflow */ +static struct ble_hs_dev_records peer_dev_rec[BLE_RESOLV_LIST_SIZE]; +static int ble_store_num_peer_dev_rec; + +struct ble_hs_resolv_data { + uint8_t addr_res_enabled; + uint8_t rl_cnt; + uint32_t rpa_tmo; + struct ble_npl_callout rpa_timer; +}; + +/*** APIs for Peer Device Records. + * + * These Peer records are necessary to take care of Peers with RPA address when + * they are not yet added to Resolving list ***/ + +struct ble_hs_dev_records * +ble_rpa_get_peer_dev_records(void) +{ + return &peer_dev_rec[0]; +} + +int +ble_rpa_get_num_peer_dev_records(void) +{ + return ble_store_num_peer_dev_rec; +} + +void +ble_rpa_set_num_peer_dev_records(int num_rec) +{ + ble_store_num_peer_dev_rec = num_rec; +} + +int +ble_rpa_remove_peer_dev_rec(struct ble_hs_dev_records *p_dev_rec) +{ + int i; + + for (i = 0; i < ble_store_num_peer_dev_rec; i++) { + if (!(memcmp(p_dev_rec, &peer_dev_rec[i], sizeof(struct + ble_hs_dev_records)))) { + memset(&peer_dev_rec[i], 0, sizeof(struct ble_hs_dev_records)); + break; + } + } + + if (i >= ble_store_num_peer_dev_rec) { + return BLE_HS_EUNKNOWN; + } + + ble_store_num_peer_dev_rec--; + if ((i != ble_store_num_peer_dev_rec) && (ble_store_num_peer_dev_rec != 0)) { + memmove(&peer_dev_rec[i], &peer_dev_rec[i + 1], + (ble_store_num_peer_dev_rec - i + 1) * sizeof(struct ble_hs_dev_records )); + } + + BLE_HS_LOG(DEBUG, " RPA: removed device at index = %d, no. of peer records" + " = %d\n", i, ble_store_num_peer_dev_rec); + + ble_store_persist_peer_records(); + return 0; +} + +static void +ble_rpa_peer_dev_rec_clear_all(void) +{ + uint8_t i; + + /* As NVS record need to be deleted one by one, we loop through + * peer_records */ + for (i = 0; i < ble_store_num_peer_dev_rec; i++) { + ble_store_num_peer_dev_rec--; + + if ((i != ble_store_num_peer_dev_rec) && (ble_store_num_peer_dev_rec != 0)) { + memmove(&peer_dev_rec[i], &peer_dev_rec[i + 1], + (ble_store_num_peer_dev_rec - i + 1) * sizeof(struct ble_hs_dev_records )); + } + + ble_store_persist_peer_records(); + } + return; +} + +/* Find peer device record with the address value. + * + * @return peer_dev_record if the addr matches with either of rand_addr, + * pseudo_addr or identity_addr + * NULL otherwise + */ +struct ble_hs_dev_records * +ble_rpa_find_peer_dev_rec(uint8_t *addr) +{ + struct ble_hs_dev_records *p_dev_rec = &peer_dev_rec[0]; + uint8_t i; + + for (i = 0; i < ble_store_num_peer_dev_rec; i++, p_dev_rec++) { + if (p_dev_rec->rec_used) { + if (!(memcmp(p_dev_rec->rand_addr, addr, BLE_DEV_ADDR_LEN)) || + !(memcmp(p_dev_rec->pseudo_addr, addr, BLE_DEV_ADDR_LEN)) || + !(memcmp(p_dev_rec->identity_addr, addr, BLE_DEV_ADDR_LEN))) { + return p_dev_rec; + } + } + } + return NULL; +} + +/* Find peer device record with the IRK */ +static struct ble_hs_dev_records * +ble_rpa_find_peer_dev_by_irk(uint8_t *irk) +{ + struct ble_hs_dev_records *p_dev_rec = &peer_dev_rec[0]; + uint8_t i; + + for (i = 0; i < ble_store_num_peer_dev_rec; i++, p_dev_rec++) { + if ((p_dev_rec->rec_used) && + (!memcmp(irk, p_dev_rec->peer_sec.irk, 16))) { + return p_dev_rec; + } + } + return NULL; +} + +/* Find out if the peer record at perticular index resolves received peer + * address. + * + * @return true if the RPA is resolvable, false otherwise */ +static bool +is_rpa_resolvable_by_peer_rec(struct ble_hs_dev_records *p_dev_rec, uint8_t *peer_add) +{ + if (p_dev_rec->peer_sec.irk_present) { + if (ble_hs_resolv_rpa(peer_add, p_dev_rec->peer_sec.irk) == 0) { + return true; + } + } + return false; +} + +/* Add peer to peer device records. + * + * @return 0 if added successfully, + * BLE_HS_ESTORE_CAP if the no. of peer device records are exceeding + * maximum allowed value (No. of Bonds + 1) */ +int +ble_rpa_resolv_add_peer_rec(uint8_t *peer_addr) +{ + struct ble_hs_dev_records *p_dev_rec = + &peer_dev_rec[ble_store_num_peer_dev_rec]; + uint8_t idx = 0; + + while (p_dev_rec->rec_used) { + p_dev_rec++; + idx++; + if (idx > MYNEWT_VAL(BLE_STORE_MAX_BONDS)) { + return BLE_HS_ESTORE_CAP; + } + } + + p_dev_rec->rec_used = 1; + memcpy(p_dev_rec->pseudo_addr, peer_addr, BLE_DEV_ADDR_LEN); + memcpy(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN); + memcpy(p_dev_rec->identity_addr, peer_addr, BLE_DEV_ADDR_LEN); + ble_store_num_peer_dev_rec++; + + return 0; +} + +static struct ble_hs_resolv_entry * +ble_rpa_find_rl_from_peer_records(uint8_t *peer_addr, uint8_t *peer_addr_type) +{ + struct ble_hs_dev_records *p_dev_rec = NULL; + struct ble_hs_resolv_entry *rl = NULL; + int i; + int rc = 0; + + for (i = (ble_store_num_peer_dev_rec - 1); i >= 0; i--) { + p_dev_rec = &peer_dev_rec[i]; + /* If the record is not used, skip */ + if (!(p_dev_rec->rec_used)) { + continue; + } + + if (is_rpa_resolvable_by_peer_rec(p_dev_rec, peer_addr)) { + memcpy(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN); + rl = ble_hs_resolv_list_find(p_dev_rec->identity_addr); + if (rl) { + memcpy(peer_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN); + *peer_addr_type = p_dev_rec->peer_sec.peer_addr.type; + } else if ((rl = ble_hs_resolv_list_find(p_dev_rec->pseudo_addr)) + != NULL) { + memcpy(peer_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN); + *peer_addr_type = p_dev_rec->peer_sec.peer_addr.type; + } else { + /* Peer record exists, but RL does not + * exist, remove peer record */ + BLE_HS_LOG(DEBUG, "RPA resolvable but RL doesn't exist; remove" + " peer rec at index = %d \n", i); + rc = ble_rpa_remove_peer_dev_rec(p_dev_rec); + if (rc != 0) { + BLE_HS_LOG(ERROR, "Unexpected error; index exceeds max capacity\n"); + } + } + /* Break loop, unique entry */ + break; + } else { + /* As peer_dev_rec is added as soon as peer RPA + * is spotted, we might encounter duplications, + * remove peer_dev_rec here itself */ + if ((!memcmp(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN)) || + (!memcmp(p_dev_rec->pseudo_addr, peer_addr, BLE_DEV_ADDR_LEN)) || + (!memcmp(p_dev_rec->identity_addr, peer_addr, BLE_DEV_ADDR_LEN))) { + BLE_HS_LOG(DEBUG, "RPA NOT resolvable; remove" + " peer rec at index = %d \n", i); + rc = ble_rpa_remove_peer_dev_rec(p_dev_rec); + if (rc != 0) { + BLE_HS_LOG(ERROR, "Unexpected error; index exceeds max capacity\n"); + break; + } + } + } + } + + return rl; +} + +void +ble_rpa_replace_peer_params_with_rl(uint8_t *peer_addr, uint8_t *addr_type, + struct ble_hs_resolv_entry **rl) +{ + struct ble_hs_resolv_entry *rl_tmp = NULL; + bool is_rpa = ble_hs_is_rpa(peer_addr, *addr_type); + + if (is_rpa) { + ble_hs_log_flat_buf(peer_addr, BLE_DEV_ADDR_LEN); + rl_tmp = ble_hs_resolv_list_find(peer_addr); + + /* Try to find from your peer_device records, if RL doesn't + * exist */ + if (rl_tmp == NULL) { + rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type); + } + } + + if (rl != NULL) { + *rl = rl_tmp; + } +} + +/**** APIs for Resolving List: ****/ + +/** + * Returns whether or not address resolution is enabled. + * + * @return uint8_t + */ +static uint8_t +is_ble_hs_resolv_enabled(void) +{ + return g_ble_hs_resolv_data.addr_res_enabled; +} + +/* Check if the Host based RPA is enabled */ +bool +ble_host_rpa_enabled(void) +{ + if (is_ble_hs_resolv_enabled() && ble_hs_pvcy_enabled()) { + return true; + } + return false; +} + +static void +ble_hs_rand_prand_get(uint8_t *prand) +{ + uint16_t sum; + uint8_t rc; + + while (1) { + /* Get 24 bits of random data */ + rc = ble_hs_hci_util_rand(prand, 3); + if (rc != 0) { + return; + } + + /* Prand cannot be all zeros or 1's. */ + sum = prand[0] + prand[1] + prand[2]; + if ((sum != 0) && (sum != (3 * 0xff))) { + break; + } + } + + /* Upper two bits must be 01 */ + prand[2] &= ~0xc0; + prand[2] |= 0x40; +} + +/** + * Called to generate a resolvable private address in rl structure + * + * @param rl + * @param local + */ +static void +ble_hs_resolv_gen_priv_addr(struct ble_hs_resolv_entry *rl, int local) +{ + uint8_t *irk = NULL; + uint8_t *prand = NULL; + struct ble_encryption_block ecb = {0}; + uint8_t *addr = NULL; + + if (local) { + addr = rl->rl_local_rpa; + irk = rl->rl_local_irk; + } else { + addr = rl->rl_peer_rpa; + irk = rl->rl_peer_irk; + } + + /* Get prand */ + prand = addr + 3; + ble_hs_rand_prand_get(prand); + + /* Calculate hash, hash = ah(local IRK, prand) */ + memcpy(ecb.key, irk, 16); + + memset(ecb.plain_text, 0, 13); + ecb.plain_text[13] = prand[2]; + ecb.plain_text[14] = prand[1]; + ecb.plain_text[15] = prand[0]; + + swap_in_place(ecb.key, 16); + swap_in_place(ecb.plain_text, 16); + + /* Calculate hash */ + if (ble_sm_alg_encrypt(ecb.key, ecb.plain_text, ecb.cipher_text) != 0) { + /* We can't do much here if the encryption fails */ + return; + } + swap_in_place(ecb.cipher_text, 16); + + addr[0] = ecb.cipher_text[15]; + addr[1] = ecb.cipher_text[14]; + addr[2] = ecb.cipher_text[13]; +} + +/* Called to generate RPA address and this address is set in controller as + * Random address. This is necessary in Host based privacy because controller is unaware of RPA + * address is being used */ +int +ble_hs_gen_own_rpa_random(void) +{ + struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[0]; + + ble_hs_resolv_gen_priv_addr(rl, 1); + return ble_hs_id_set_pseudo_rnd(rl->rl_local_rpa); +} + +/* Called to fetch local RPA address */ +uint8_t * +ble_hs_get_rpa_local(void) +{ + struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[0]; + return rl->rl_local_rpa; +} + +/** + * Called when the Resolvable private address timer expires. This timer + * is used to regenerate local and peers RPA's in the resolving list. + */ +static void +ble_hs_resolv_rpa_timer_cb(struct ble_npl_event *ev) +{ + if (ble_host_rpa_enabled()) { + BLE_HS_LOG(DEBUG, "RPA Timeout; start active adv & scan with new RPA\n"); + + ble_gap_preempt(); + /* Generate local RPA */ + ble_hs_gen_own_rpa_random(); + ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer, + (int32_t)g_ble_hs_resolv_data.rpa_tmo); + ble_gap_preempt_done(); + } + + return; +} + +static bool +is_irk_nonzero(uint8_t *irk) +{ + int i; + bool rc = false; + + for (i = 0; i < 16; ++i) { + if (*irk != 0) { + rc = true; + break; + } + ++irk; + } + + return rc; +} + +/** + * Used to determine if the device is on the resolving list. + * + * @param addr + * @param addr_type Public address (0) or random address (1) + * + * @return int 0: device is not on resolving list; otherwise the return value + * is the 'position' of the device in the resolving list (the index of the + * element). + */ +static int +ble_hs_is_on_resolv_list(uint8_t *addr, uint8_t addr_type) +{ + int i; + struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[1]; + + for (i = 1; i < g_ble_hs_resolv_data.rl_cnt; ++i) { + if ((rl->rl_addr_type == addr_type) && + (!memcmp(rl->rl_identity_addr, addr, BLE_DEV_ADDR_LEN))) { + return i; + } + ++rl; + } + + return 0; +} + +/** + * Used to find Resolving list entry with the provided address field. + * + * @param addr + * + * @return Pointer to resolving list entry or NULL if no entry found. + */ +struct ble_hs_resolv_entry * +ble_hs_resolv_list_find(uint8_t *addr) +{ + int i; + struct ble_hs_resolv_entry *rl = &g_ble_hs_resolv_list[1]; + + for (i = 1; i < g_ble_hs_resolv_data.rl_cnt; ++i) { + if (memcmp(rl->rl_identity_addr, addr, BLE_DEV_ADDR_LEN) == 0) { + return rl; + } + + if (memcmp(rl->rl_pseudo_id, addr, BLE_DEV_ADDR_LEN) == 0) { + return rl; + } + + if (memcmp(rl->rl_peer_rpa, addr, BLE_DEV_ADDR_LEN) == 0) { + return rl; + } + ++rl; + } + return NULL; +} + +/** + * Add a device to the resolving list + * + * @return int + */ +int +ble_hs_resolv_list_add(uint8_t *cmdbuf) +{ + uint8_t addr_type; + uint8_t *ident_addr; + struct ble_hs_resolv_entry *rl; + struct ble_hs_dev_records *p_dev_rec = NULL; + + /* Check if we have any open entries */ + if (g_ble_hs_resolv_data.rl_cnt >= BLE_RESOLV_LIST_SIZE) { + return BLE_HS_ENOMEM; + } + + addr_type = cmdbuf[0]; + ident_addr = cmdbuf + 1; + + if (ble_hs_is_on_resolv_list(ident_addr, addr_type)) { + return BLE_HS_EINVAL; + } + + rl = &g_ble_hs_resolv_list[g_ble_hs_resolv_data.rl_cnt]; + memset(rl, 0, sizeof(*rl)); + + rl->rl_addr_type = addr_type; + memcpy(rl->rl_identity_addr, ident_addr, BLE_DEV_ADDR_LEN); + swap_buf(rl->rl_peer_irk, cmdbuf + 7, 16); + swap_buf(rl->rl_local_irk, cmdbuf + 23, 16); + + if (is_irk_nonzero(rl->rl_peer_irk)) { + p_dev_rec = ble_rpa_find_peer_dev_by_irk(rl->rl_peer_irk); + + if (p_dev_rec != NULL) { + memcpy(p_dev_rec->identity_addr, ident_addr, 6); + memcpy(rl->rl_pseudo_id, p_dev_rec->pseudo_addr, 6); + } + } + + /* generate a local and peer RPAs now, those will be updated by timer + * when resolution is enabled + */ + ble_hs_resolv_gen_priv_addr(rl, 1); + ble_hs_resolv_gen_priv_addr(rl, 0); + ++(g_ble_hs_resolv_data.rl_cnt); + BLE_HS_LOG(DEBUG, "Device added to RL, Resolving list count = %d\n", g_ble_hs_resolv_data.rl_cnt); + + return 0; +} + +/** + * Remove a device from the resolving list + * + * @return int 0: success, BLE Host error code otherwise + */ +int +ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr) +{ + int position; + + /* Remove from IRK records */ + position = ble_hs_is_on_resolv_list(ident_addr, addr_type); + if (position) { + + memmove(&g_ble_hs_resolv_list[position], + &g_ble_hs_resolv_list[position + 1], + (g_ble_hs_resolv_data.rl_cnt - position) * sizeof (struct + ble_hs_resolv_entry)); + --g_ble_hs_resolv_data.rl_cnt; + + return 0; + } + + return BLE_HS_ENOENT; +} + +/** + * Clear the resolving list + */ +void +ble_hs_resolv_list_clear_all(void) +{ + g_ble_hs_resolv_data.rl_cnt = 0; + memset(g_ble_hs_resolv_list, 0, BLE_RESOLV_LIST_SIZE * sizeof(struct + ble_hs_resolv_entry)); + + /* Now delete peer device records as well */ + ble_rpa_peer_dev_rec_clear_all(); + + return; +} + +/** + * Called to enable or disable address resolution in the host + * + * @param bool + * + * @return int + */ +void +ble_hs_resolv_enable(bool enable) +{ + int32_t tmo; + + /* If we change state, we need to disable/enable the RPA timer */ + if (enable ^ g_ble_hs_resolv_data.addr_res_enabled) { + if (enable) { + tmo = (int32_t)g_ble_hs_resolv_data.rpa_tmo; + ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer, tmo); + } else { + ble_npl_callout_stop(&g_ble_hs_resolv_data.rpa_timer); + } + g_ble_hs_resolv_data.addr_res_enabled = enable; + } + + return; +} + +/** + * Set the resolvable private address timeout. + * + * @param cmdbuf + * + * @return int + */ +int +ble_hs_resolv_set_rpa_tmo(uint16_t tmo_secs) +{ + /* Though the check validates smaller timeout values, it is recommended to + * set it to enough bigger value (~15 minutes). There is no point in + * changing RPA address aggressively and ending up sacrificing normal BLE + * operations. Max RPA_TIMEOUT is ~11.5HRS (Spec v4.2, Vol 2, Part E, + * section 7.8.45) */ + if (!((tmo_secs > 0) && (tmo_secs <= BLE_MAX_RPA_TIMEOUT_VAL))) { + return BLE_HS_EINVAL; + } + + g_ble_hs_resolv_data.rpa_tmo = ble_npl_time_ms_to_ticks32(tmo_secs * 1000); + + /* If resolving is not enabled, we are done here. */ + if (!is_ble_hs_resolv_enabled()) { + return 0; + } + + /* Reset timeout if resolving is enabled */ + ble_npl_callout_reset(&g_ble_hs_resolv_data.rpa_timer, + (int32_t)g_ble_hs_resolv_data.rpa_tmo); + + return 0; +} + +/** + * Resolve a Resolvable Private Address + * + * @param rpa + * @param index + * + * @return int + */ +int +ble_hs_resolv_rpa(uint8_t *rpa, uint8_t *irk) +{ + int rc; + struct ble_encryption_block ecb = {0}; + + if (!(is_irk_nonzero(irk))) { + return BLE_HS_EINVAL; + } + + swap_buf(ecb.key, irk, 16); + memset(ecb.plain_text, 0, 16); + + ecb.plain_text[15] = rpa[3]; + ecb.plain_text[14] = rpa[4]; + ecb.plain_text[13] = rpa[5]; + + swap_in_place(ecb.plain_text, 16); + + /* Send the data to ble_sm_alg_encrypt in little-endian style */ + rc = ble_sm_alg_encrypt(ecb.key, ecb.plain_text, ecb.cipher_text); + if (rc != 0) { + return rc; + } + swap_in_place(ecb.cipher_text, 16); + + if ((ecb.cipher_text[15] == rpa[0]) && (ecb.cipher_text[14] == rpa[1]) && + (ecb.cipher_text[13] == rpa[2])) { + rc = 0; + } else { + rc = BLE_HS_ENOENT; + } + + return rc; +} + +void ble_hs_resolv_init(void) +{ + g_ble_hs_resolv_data.rpa_tmo = ble_npl_time_ms_to_ticks32(MYNEWT_VAL(BLE_RPA_TIMEOUT) * 1000); + + ble_npl_callout_init(&g_ble_hs_resolv_data.rpa_timer, + ble_hs_evq_get(), + ble_hs_resolv_rpa_timer_cb, + NULL); +} + +#endif /* if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) */ diff --git a/src/nimble/host/src/ble_hs_resolv_priv.h b/src/nimble/host/src/ble_hs_resolv_priv.h new file mode 100644 index 00000000..568aa89a --- /dev/null +++ b/src/nimble/host/src/ble_hs_resolv_priv.h @@ -0,0 +1,108 @@ +/* + * Copyright 2020 Espressif Systems (Shanghai) PTE LTD + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/* + * An entry in the resolving list. + */ +struct ble_hs_resolv_entry { + uint8_t rl_addr_type; + uint8_t rl_local_irk[16]; + uint8_t rl_peer_irk[16]; + uint8_t rl_identity_addr[BLE_DEV_ADDR_LEN]; + uint8_t rl_pseudo_id[BLE_DEV_ADDR_LEN]; + uint8_t rl_local_rpa[BLE_DEV_ADDR_LEN]; + uint8_t rl_peer_rpa[BLE_DEV_ADDR_LEN]; +}; + +#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) +/* Persist peer records in NVS. XXX Need to handle this in `store` module */ +int ble_store_persist_peer_records(void); +#endif + +struct ble_hs_peer_sec { + ble_addr_t peer_addr; + uint8_t irk[16]; + uint8_t irk_present: 1; +}; +/* + * BLE host peer device record, this helps in storing peer RPA before bond is + * created and IRKs are exchanged. + */ +struct ble_hs_dev_records { + bool rec_used; + uint8_t pseudo_addr[BLE_DEV_ADDR_LEN]; + uint8_t rand_addr[BLE_DEV_ADDR_LEN]; + uint8_t identity_addr[BLE_DEV_ADDR_LEN]; + struct ble_hs_peer_sec peer_sec; +}; + +/* Add a device to the resolving list */ +int ble_hs_resolv_list_add(uint8_t *cmdbuf); +int ble_hs_gen_own_rpa_random(void); +uint8_t *ble_hs_get_rpa_local(void); + +/* Remove a device from the resolving list */ +int ble_hs_resolv_list_rmv(uint8_t, uint8_t *); +/* Clear the resolving list and peer dev record */ +void ble_hs_resolv_list_clear_all(void); + +/* Address resolution enable command */ +void ble_hs_resolv_enable(bool); + +/* Finds 'addr' in resolving list. Doesnt check if address resolution enabled */ +struct ble_hs_resolv_entry * +ble_hs_resolv_list_find(uint8_t *addr); + +/* Returns true if host based RPA (privacy) is enabled */ +bool ble_host_rpa_enabled(void); + +/* Searches peer device records (RPA) and fetches matching RL, peer_address + * into input parameters if RL is found */ +void +ble_rpa_replace_peer_params_with_rl(uint8_t *, uint8_t *, struct ble_hs_resolv_entry **); + +int ble_rpa_resolv_add_peer_rec(uint8_t *); + +struct ble_hs_dev_records *ble_rpa_get_peer_dev_records(void); +int ble_rpa_get_num_peer_dev_records(void); +void ble_rpa_set_num_peer_dev_records(int); +int ble_rpa_remove_peer_dev_rec(struct ble_hs_dev_records *); +struct ble_hs_dev_records *ble_rpa_find_peer_dev_rec(uint8_t *); + +/* Set the resolvable private address timeout */ +int ble_hs_resolv_set_rpa_tmo(uint16_t); + +/* Resolve a resolvable private address */ +int ble_hs_resolv_rpa(uint8_t *rpa, uint8_t *irk); + +/* Initialize resolv*/ +void ble_hs_resolv_init(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/nimble/host/src/ble_sm.c b/src/nimble/host/src/ble_sm.c index b0eee86c..e5756251 100644 --- a/src/nimble/host/src/ble_sm.c +++ b/src/nimble/host/src/ble_sm.c @@ -47,6 +47,8 @@ #include "nimble/nimble_opt.h" #include "host/ble_sm.h" #include "ble_hs_priv.h" +#include "ble_hs_resolv_priv.h" +#include "../store/config/src/ble_store_config_priv.h" #if NIMBLE_BLE_SM @@ -545,6 +547,29 @@ ble_sm_persist_keys(struct ble_sm_proc *proc) } identity_ev = 1; +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + if (ble_host_rpa_enabled()) + { + struct ble_hs_dev_records *p_dev_rec = + ble_rpa_find_peer_dev_rec(conn->bhc_peer_rpa_addr.val); + if (p_dev_rec == NULL) { + if (!ble_rpa_resolv_add_peer_rec(conn->bhc_peer_rpa_addr.val)) { + p_dev_rec = ble_rpa_find_peer_dev_rec(conn->bhc_peer_rpa_addr.val); + } + } + + if (p_dev_rec != NULL) { + /* Once bonded, copy the peer device records */ + swap_buf(p_dev_rec->peer_sec.irk, proc->peer_keys.irk, 16); + p_dev_rec->peer_sec.irk_present = proc->peer_keys.irk_valid; + memcpy(p_dev_rec->peer_sec.peer_addr.val, + proc->peer_keys.addr, 6); + p_dev_rec->peer_sec.peer_addr.type = proc->peer_keys.addr_type; + + ble_store_persist_peer_records(); + } + } +#endif } } else { peer_addr = conn->bhc_peer_addr; @@ -2126,6 +2151,19 @@ ble_sm_key_exch_exec(struct ble_sm_proc *proc, struct ble_sm_result *res, conn = ble_hs_conn_find_assert(proc->conn_handle); ble_hs_conn_addrs(conn, &addrs); +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + /* Check if the privacy is enabled, change the address accordingly + * Overriding the addrs field to get the ID address remain unique + * (public address) */ + if (ble_host_rpa_enabled()) + { + uint8_t *local_id_rpa = NULL; + + ble_hs_id_addr(BLE_ADDR_PUBLIC, (void *) &local_id_rpa, NULL); + memcpy(addrs.our_id_addr.val, local_id_rpa, 6); + addrs.our_id_addr.type = BLE_ADDR_PUBLIC; + } +#endif addr_info->addr_type = addrs.our_id_addr.type; memcpy(addr_info->bd_addr, addrs.our_id_addr.val, 6); diff --git a/src/nimble/host/src/ble_sm_alg.c b/src/nimble/host/src/ble_sm_alg.c index f70ec654..93453c60 100644 --- a/src/nimble/host/src/ble_sm_alg.c +++ b/src/nimble/host/src/ble_sm_alg.c @@ -75,7 +75,7 @@ ble_sm_alg_xor_128(uint8_t *p, uint8_t *q, uint8_t *r) } } -static int +int ble_sm_alg_encrypt(uint8_t *key, uint8_t *plaintext, uint8_t *enc_data) { uint8_t tmp[16]; diff --git a/src/nimble/host/src/ble_sm_priv.h b/src/nimble/host/src/ble_sm_priv.h index 3f64b778..74205bd0 100644 --- a/src/nimble/host/src/ble_sm_priv.h +++ b/src/nimble/host/src/ble_sm_priv.h @@ -399,6 +399,7 @@ int ble_sm_slave_initiate(uint16_t conn_handle); int ble_sm_enc_initiate(uint16_t conn_handle, uint8_t key_size, const uint8_t *ltk, uint16_t ediv, uint64_t rand_val, int auth); +int ble_sm_alg_encrypt(uint8_t *key, uint8_t *plaintext, uint8_t *enc_data); int ble_sm_init(void); #define BLE_SM_LOG_CMD(is_tx, cmd_name, conn_handle, log_cb, cmd) \ @@ -419,6 +420,9 @@ int ble_sm_init(void); #define ble_sm_init() 0 +#define ble_sm_alg_encrypt(key, plaintext, enc_data) \ + BLE_HS_ENOTSUP + #endif struct ble_l2cap_chan *ble_sm_create_chan(uint16_t handle); diff --git a/src/nimble/host/store/config/src/ble_store_config_priv.h b/src/nimble/host/store/config/src/ble_store_config_priv.h index bae90e97..61b43c53 100644 --- a/src/nimble/host/store/config/src/ble_store_config_priv.h +++ b/src/nimble/host/store/config/src/ble_store_config_priv.h @@ -50,6 +50,9 @@ static inline int ble_store_config_persist_peer_secs(void) { return 0; } static inline int ble_store_config_persist_cccds(void) { return 0; } static inline void ble_store_config_conf_init(void) { } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +static inline int ble_store_persist_peer_records(void) { return 0; } +#endif #endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */ #ifdef __cplusplus diff --git a/src/nimble/host/store/config/src/ble_store_nvs.c b/src/nimble/host/store/config/src/ble_store_nvs.c index a9241b2b..450b0e7f 100644 --- a/src/nimble/host/store/config/src/ble_store_nvs.c +++ b/src/nimble/host/store/config/src/ble_store_nvs.c @@ -32,11 +32,14 @@ #include "ble_store_config_priv.h" #include "esp_log.h" #include "nvs.h" +#include "../../../src/ble_hs_resolv_priv.h" + #define NIMBLE_NVS_STR_NAME_MAX_LEN 16 #define NIMBLE_NVS_PEER_SEC_KEY "peer_sec" #define NIMBLE_NVS_OUR_SEC_KEY "our_sec" #define NIMBLE_NVS_CCCD_SEC_KEY "cccd_sec" +#define NIMBLE_NVS_PEER_RECORDS_KEY "p_dev_rec" #define NIMBLE_NVS_NAMESPACE "nimble_bond" typedef uint32_t nvs_handle_t; @@ -50,12 +53,16 @@ static const char *TAG = "NIMBLE_NVS"; static void get_nvs_key_string(int obj_type, int index, char *key_string) { - if (obj_type == BLE_STORE_OBJ_TYPE_PEER_SEC) { - sprintf(key_string, "%s_%d", NIMBLE_NVS_PEER_SEC_KEY, index); - } else if (obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC) { - sprintf(key_string, "%s_%d", NIMBLE_NVS_OUR_SEC_KEY, index); + if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + sprintf(key_string, "%s_%d", NIMBLE_NVS_PEER_RECORDS_KEY, index); } else { - sprintf(key_string, "%s_%d", NIMBLE_NVS_CCCD_SEC_KEY, index); + if (obj_type == BLE_STORE_OBJ_TYPE_PEER_SEC) { + sprintf(key_string, "%s_%d", NIMBLE_NVS_PEER_SEC_KEY, index); + } else if (obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC) { + sprintf(key_string, "%s_%d", NIMBLE_NVS_OUR_SEC_KEY, index); + } else { + sprintf(key_string, "%s_%d", NIMBLE_NVS_CCCD_SEC_KEY, index); + } } } @@ -81,18 +88,53 @@ get_nvs_matching_index(void *nvs_val, void *db_list, int db_num, size_t } static int -get_nvs_max_bonds(int obj_type) +get_nvs_max_obj_value(int obj_type) { - if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { - return MYNEWT_VAL(BLE_STORE_MAX_CCCDS); + /* If host based privacy is enabled */ + if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + return (MYNEWT_VAL(BLE_STORE_MAX_BONDS) + 1); } else { - return MYNEWT_VAL(BLE_STORE_MAX_BONDS); + if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { + return MYNEWT_VAL(BLE_STORE_MAX_CCCDS); + } else { + return MYNEWT_VAL(BLE_STORE_MAX_BONDS); + } } } /***************************************************************************** * $ NVS * *****************************************************************************/ +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +static int +get_nvs_peer_record(char *key_string, struct ble_hs_dev_records *p_dev_rec) +{ + esp_err_t err; + size_t required_size = 0; + nvs_handle_t nimble_handle; + + err = nvs_open(NIMBLE_NVS_NAMESPACE, NVS_READWRITE, &nimble_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "NVS open operation failed"); + return BLE_HS_ESTORE_FAIL; + } + + err = nvs_get_blob(nimble_handle, key_string, NULL, &required_size); + + /* if Address pointer for value is NULL, filling of value not needed */ + if (err != ESP_OK || p_dev_rec == NULL) { + goto end; + } + + err = nvs_get_blob(nimble_handle, key_string, p_dev_rec, + &required_size); + +end: + nvs_close(nimble_handle); + return err; +} +#endif + static int get_nvs_db_value(int obj_type, char *key_string, union ble_store_value *val) { @@ -138,16 +180,21 @@ static int get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value) { union ble_store_value cur = {0}; + struct ble_hs_dev_records p_dev_rec = {0}; esp_err_t err; int i, count = 0, max_limit = 0; char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN]; - max_limit = get_nvs_max_bonds(obj_type); + max_limit = get_nvs_max_obj_value(obj_type); for (i = 1; i <= max_limit; i++) { get_nvs_key_string(obj_type, i, key_string); - err = get_nvs_db_value(obj_type, key_string, &cur); + if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + err = get_nvs_db_value(obj_type, key_string, &cur); + } else { + err = get_nvs_peer_record(key_string, &p_dev_rec); + } /* Check if the user is searching for empty index to write to */ if (err == ESP_ERR_NVS_NOT_FOUND) { if (empty) { @@ -159,12 +206,17 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value) /* If user has provided value, then the purpose is to find * non-matching entry from NVS */ if (value) { - if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { - err = get_nvs_matching_index(&cur.sec, value, num_value, - sizeof(struct ble_store_value_sec)); + if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + err = get_nvs_matching_index(&p_dev_rec, value, num_value, + sizeof(struct ble_hs_dev_records)); } else { - err = get_nvs_matching_index(&cur.sec, value, num_value, - sizeof(struct ble_store_value_cccd)); + if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { + err = get_nvs_matching_index(&cur.sec, value, num_value, + sizeof(struct ble_store_value_sec)); + } else { + err = get_nvs_matching_index(&cur.sec, value, num_value, + sizeof(struct ble_store_value_cccd)); + } } /* If found non-matching/odd entry of NVS with entries in the * internal database, return NVS index so can be deleted */ @@ -196,8 +248,8 @@ ble_nvs_delete_value(int obj_type, int8_t index) nvs_handle_t nimble_handle; char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN]; - if (index > get_nvs_max_bonds(obj_type)) { - ESP_LOGD(TAG, "Invalid index provided to delete"); + if (index > get_nvs_max_obj_value(obj_type)) { + ESP_LOGE(TAG, "Invalid index provided to delete"); return BLE_HS_EUNKNOWN; } @@ -273,7 +325,7 @@ ble_store_nvs_write(int obj_type, const union ble_store_value *val) if (write_key_index == -1) { ESP_LOGE(TAG, "NVS operation failed !!"); return BLE_HS_ESTORE_FAIL; - } else if (write_key_index > get_nvs_max_bonds(obj_type)) { + } else if (write_key_index > get_nvs_max_obj_value(obj_type)) { /* bare-bone config code will take care of capacity overflow event, * however another check added for consistency */ @@ -292,36 +344,84 @@ ble_store_nvs_write(int obj_type, const union ble_store_value *val) } } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +/* If Host based privacy is enabled */ +static int +ble_store_nvs_peer_records(int obj_type, const struct ble_hs_dev_records *p_dev_rec) +{ + char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN]; + int8_t write_key_index = 0; + + write_key_index = get_nvs_db_attribute(obj_type, 1, NULL, 0); + if (write_key_index == -1) { + ESP_LOGE(TAG, "NVS operation failed !!"); + return BLE_HS_ESTORE_FAIL; + } else if (write_key_index > get_nvs_max_obj_value(obj_type)) { + + /* bare-bone config code will take care of capacity overflow event, + * however another check added for consistency */ + ESP_LOGD(TAG, "NVS size overflow."); + return BLE_HS_ESTORE_CAP; + } + + get_nvs_key_string(obj_type, write_key_index, key_string); + + return ble_nvs_write_key_value(key_string, p_dev_rec, sizeof(struct + ble_hs_dev_records)); +} +#endif + static int populate_db_from_nvs(int obj_type, void *dst, int *db_num) { uint8_t *db_item = (uint8_t *)dst; union ble_store_value cur = {0}; + struct ble_hs_dev_records p_dev_rec = {0}; + esp_err_t err; int i; char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN]; - for (i = 1; i <= get_nvs_max_bonds(obj_type); i++) { + for (i = 1; i <= get_nvs_max_obj_value(obj_type); i++) { get_nvs_key_string(obj_type, i, key_string); - err = get_nvs_db_value(obj_type, key_string, &cur); - if (err == ESP_ERR_NVS_NOT_FOUND) { - continue; - } else if (err != ESP_OK) { - ESP_LOGE(TAG, "NVS read operation failed !!"); - return -1; + if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + + err = get_nvs_db_value(obj_type, key_string, &cur); + if (err == ESP_ERR_NVS_NOT_FOUND) { + continue; + } else if (err != ESP_OK) { + ESP_LOGE(TAG, "NVS read operation failed !!"); + return -1; + } + } else { + err = get_nvs_peer_record(key_string, &p_dev_rec); + if (err == ESP_ERR_NVS_NOT_FOUND) { + continue; + } else if (err != ESP_OK) { + ESP_LOGE(TAG, "NVS read operation failed !!"); + return -1; + } } + /* NVS index has data, fill up the ram db with it */ - if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { - ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i); - memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd)); - db_item += sizeof(struct ble_store_value_cccd); + if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) { + ESP_LOGD(TAG, "Peer dev records filled from NVS index = %d", i); + memcpy(db_item, &p_dev_rec, sizeof(struct ble_hs_dev_records)); + db_item += sizeof(struct ble_hs_dev_records); (*db_num)++; } else { - ESP_LOGD(TAG, "KEY in RAM is filled up from NVS index = %d", i); - memcpy(db_item, &cur.sec, sizeof(struct ble_store_value_sec)); - db_item += sizeof(struct ble_store_value_sec); - (*db_num)++; + if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) { + ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i); + memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd)); + db_item += sizeof(struct ble_store_value_cccd); + (*db_num)++; + } else { + ESP_LOGD(TAG, "KEY in RAM is filled up from NVS index = %d", i); + memcpy(db_item, &cur.sec, sizeof(struct ble_store_value_sec)); + db_item += sizeof(struct ble_store_value_sec); + (*db_num)++; + } } } return 0; @@ -364,6 +464,28 @@ ble_nvs_restore_sec_keys(void) return 0; } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +static int +ble_nvs_restore_peer_records(void) +{ + esp_err_t err; + int ble_store_num_peer_dev_rec = 0; + struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records(); + + err = populate_db_from_nvs(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, peer_dev_rec, + &ble_store_num_peer_dev_rec); + if (err != ESP_OK) { + ESP_LOGE(TAG, "NVS operation failed fetching 'Peer Dev Records'"); + return err; + } + + ble_rpa_set_num_peer_dev_records(ble_store_num_peer_dev_rec); + ESP_LOGD(TAG, "peer_dev_rec restored %d records", ble_store_num_peer_dev_rec); + + return 0; +} +#endif + int ble_store_config_persist_cccds(void) { int nvs_count, nvs_idx; @@ -442,14 +564,50 @@ int ble_store_config_persist_our_secs(void) return 0; } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) +int ble_store_persist_peer_records(void) +{ + int nvs_count, nvs_idx; + struct ble_hs_dev_records peer_rec; + int ble_store_num_peer_dev_rec = ble_rpa_get_num_peer_dev_records(); + struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records(); + + nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0); + if (nvs_count < ble_store_num_peer_dev_rec) { + /* NVS db count less than RAM count, write operation */ + ESP_LOGD(TAG, "Persisting peer dev record to NVS..."); + peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1]; + return ble_store_nvs_peer_records(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, &peer_rec); + } else if (nvs_count > ble_store_num_peer_dev_rec) { + /* NVS db count more than RAM count, delete operation */ + nvs_idx = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, + peer_dev_rec, + ble_store_num_peer_dev_rec); + if (nvs_idx == -1) { + ESP_LOGE(TAG, "NVS delete operation failed for peer records"); + return BLE_HS_ESTORE_FAIL; + } + ESP_LOGD(TAG, "Deleting peer record, nvs idx = %d", nvs_idx); + return ble_nvs_delete_value(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, nvs_idx); + } + return 0; +} +#endif + void ble_store_config_conf_init(void) { - esp_err_t err; + int err; err = ble_nvs_restore_sec_keys(); - if (err != ESP_OK) { + if (err != 0) { ESP_LOGE(TAG, "NVS operation failed, can't retrieve the bonding info"); } +#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY) + err = ble_nvs_restore_peer_records(); + if (err != 0) { + ESP_LOGE(TAG, "NVS operation failed, can't retrieve the peer records"); + } +#endif } /***************************************************************************************/ diff --git a/src/nimconfig.h b/src/nimconfig.h index f45842a0..5e586def 100644 --- a/src/nimconfig.h +++ b/src/nimconfig.h @@ -23,5 +23,11 @@ #define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70 #define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30 #define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8 +#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12 +#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1 +#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000 +#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2 +#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT 1 +#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900 #define CONFIG_BT_ENABLED 1 #define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY 1 From 728d686fe0fb087bd0071b6e31f2c24414d95591 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 24 Mar 2020 20:50:05 -0600 Subject: [PATCH 51/62] Fixed watchdog timeout when CCCD storage is full. NimBLE stack was saving CCCD's for non-bonded paired peers. Changed example to reflect this fix. --- examples/NimBLE_Server/NimBLE_Server.ino | 5 ----- src/nimble/host/src/ble_gap.c | 8 ++++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino index eac3bcfe..c8a0bb0f 100644 --- a/examples/NimBLE_Server/NimBLE_Server.ino +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -140,11 +140,6 @@ void setup() { NimBLEDevice::init("NimBLE-Arduino"); // sets device name - /** Uncomment this if you encounter watchdog timer resets after pairing with the device. - * Known bug in the NimBLE host that still needs to be resolved. - */ - //ble_store_clear(); - /** Set the IO capabilities of the device, each option will trigger a different pairing method. * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing diff --git a/src/nimble/host/src/ble_gap.c b/src/nimble/host/src/ble_gap.c index 0df2d19c..68c91f75 100644 --- a/src/nimble/host/src/ble_gap.c +++ b/src/nimble/host/src/ble_gap.c @@ -5300,8 +5300,12 @@ ble_gap_enc_event(uint16_t conn_handle, int status, int security_restored) ble_gap_event_listener_call(&event); ble_gap_call_conn_event_cb(&event, conn_handle); - - if (status == 0) { +/* H2zero mod + If bonding is not enabled don't store cccd data + if (status == 0) { +*/ + if (status == 0 && ble_hs_cfg.sm_bonding) { +/* End mod */ if (security_restored) { ble_gatts_bonding_restored(conn_handle); } else { From bd58d788d3ec70b16cb462f05298ab3003a80960 Mon Sep 17 00:00:00 2001 From: h2zero Date: Tue, 24 Mar 2020 23:49:21 -0600 Subject: [PATCH 52/62] Added getClientByID methode to NimBLEDevice. Initial work on NimBLE_client example. --- examples/NimBLE_Client/NimBLE_Client.ino | 227 +++++++++++++++++++++++ src/NimBLEDevice.cpp | 15 ++ src/NimBLEDevice.h | 2 +- 3 files changed, 243 insertions(+), 1 deletion(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index e69de29b..e7ffafce 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -0,0 +1,227 @@ + +/** NimBLE_Server Demo: + * + * Demonstrates many of the available features of the NimBLE client library. + * + * Created: on March 24 2020 + * Author: H2zero + * +*/ + +#include + +static NimBLERemoteService* pSvc = nullptr; +static NimBLERemoteCharacteristic* pChr = nullptr; +static NimBLERemoteDescriptor* pDsc = nullptr; + +static NimBLEAdvertisedDevice* advDevice; + +static NimBLEClient* pClient_a[3] = {nullptr}; +static uint8_t cCount = 0; +static bool doConnect = false; + + +class ClientCallbacks : public NimBLEClientCallbacks { + void onConnect(NimBLEClient* pClient) { + Serial.println("Connected"); + //pClient->updateConnParams(24,48,0,800); + }; + + void onDisconnect(NimBLEClient* pClient) { + Serial.println("Disconnected"); + }; + + uint32_t onPassKeyRequest(){ + Serial.println("Client Passkey Request"); + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: "); + Serial.println(pass_key); + return true; + }; + + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + if(!desc->sec_state.encrypted) { + NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); + Serial.println("Encrypt connection failed - disconnecting"); + return; + } + }; +}; + + +class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + Serial.print("Advertised Device found: "); + Serial.println(advertisedDevice->toString().c_str()); + if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) + { + Serial.println("Found Our Service"); + NimBLEDevice::getScan()->stop(); + + advDevice = advertisedDevice; + doConnect = true; + } + } +}; + + +void scanEndedCB(NimBLEScanResults results){ + Serial.println("Scan Ended"); +} + + +void notifyCB(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ + Serial.print((isNotify == true) ? "Notify" : "Indication"); + Serial.print(" from "); + Serial.println(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print("Value: "); + Serial.println(std::string((char*)pData, length).c_str()); +} + + +void setup (){ + Serial.begin(115200); + Serial.println("Starting NimBLE Client"); + NimBLEDevice::init(""); + +// BLEDevice::addIgnored(NimBLEAddress ("cc:b8:6c:d6:a8:82")); + +// BLEDevice::setPower(ESP_PWR_LVL_P9); + + NimBLEScan* pScan = NimBLEDevice::getScan(); //create new scan + pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); + pScan->setInterval(1349); + pScan->setWindow(449); + pScan->setActiveScan(true); + pScan->start(0, scanEndedCB); +} + + +void loop (){ + while(!doConnect){ + delay(10); + } + doConnect = false; + + pClient_a[cCount] = NimBLEDevice::createClient(); + NimBLEClient* pClient = pClient_a[cCount]; + cCount++; + pClient->setClientCallbacks(new ClientCallbacks()); + pClient->setConnectionParams(6,6,0,400); + pClient->setConnectTimeout(10); + if (pClient->connect(advDevice)) { + Serial.print("Connected to: "); + Serial.println(pClient->getPeerAddress().toString().c_str()); + Serial.print("RSSI: "); + Serial.println(pClient->getRssi()); + + pSvc = pClient->getService("DEAD"); + if(pSvc) { + pChr = pSvc->getCharacteristic("BEEF"); + } + + if(pChr != nullptr) { + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + pClient->disconnect(); + cCount--; + NimBLEDevice::deleteClient(pClient); + return; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + pChr->registerForNotify(notifyCB); + } + else if(pChr->canIndicate()) { + pChr->registerForNotify(notifyCB, false); + } + } + + else{ + Serial.println("DEAD service not found."); + } + + pSvc = pClient->getService("BAAD"); + if(pSvc) { + pChr = pSvc->getCharacteristic("F00D"); + } + + if(pChr != nullptr) { + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc != nullptr) { + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Take it back")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + pClient->disconnect(); + cCount--; + NimBLEDevice::deleteClient(pClient); + return; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + pChr->registerForNotify(notifyCB); + } + else if(pChr->canIndicate()) { + pChr->registerForNotify(notifyCB, false); + } + } + + else{ + Serial.println("BAAD service not found."); + } + + if(cCount>2){ + cCount--; + NimBLEDevice::deleteClient(pClient); + } + } + else{ + cCount--; + NimBLEDevice::deleteClient(pClient); + } + + NimBLEDevice::getScan()->start(0,scanEndedCB); +} diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index fbadea50..2c5a1211 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -167,6 +167,21 @@ void NimBLEDevice::stopAdvertising() { } // getClientList +/** + * @brief Get a reference to a client by connection ID. + * @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 Set the transmission power. * The power level can be one of: diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index a9c15da3..f16ae2fa 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -98,7 +98,7 @@ class NimBLEDevice { static NimBLEAdvertising* getAdvertising(); static void startAdvertising(); static void stopAdvertising(); - + static NimBLEClient* getClientByID(uint16_t conn_id); static std::list* getClientList(); private: From 437dbc7462cb93fb3110426e4e8fbe37e9a23c6e Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 25 Mar 2020 15:49:43 -0600 Subject: [PATCH 53/62] Client connect handler semaphore release moved. Continued work on client example. --- examples/NimBLE_Client/NimBLE_Client.ino | 281 +++++++++++++---------- src/NimBLEClient.cpp | 15 +- src/modlog/modlog.h | 10 + 3 files changed, 178 insertions(+), 128 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index e7ffafce..9fed41af 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -10,6 +10,8 @@ #include +void scanEndedCB(NimBLEScanResults results); + static NimBLERemoteService* pSvc = nullptr; static NimBLERemoteCharacteristic* pChr = nullptr; static NimBLERemoteDescriptor* pDsc = nullptr; @@ -17,7 +19,7 @@ static NimBLERemoteDescriptor* pDsc = nullptr; static NimBLEAdvertisedDevice* advDevice; static NimBLEClient* pClient_a[3] = {nullptr}; -static uint8_t cCount = 0; +static uint8_t clientCount = 0; static bool doConnect = false; @@ -28,7 +30,8 @@ class ClientCallbacks : public NimBLEClientCallbacks { }; void onDisconnect(NimBLEClient* pClient) { - Serial.println("Disconnected"); + Serial.println("Disconnected - Starting scanning"); + NimBLEDevice::getScan()->start(0,scanEndedCB); }; uint32_t onPassKeyRequest(){ @@ -68,20 +71,25 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { }; -void scanEndedCB(NimBLEScanResults results){ - Serial.println("Scan Ended"); -} - - -void notifyCB(NimBLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ +void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ Serial.print((isNotify == true) ? "Notify" : "Indication"); Serial.print(" from "); - Serial.println(pBLERemoteCharacteristic->getUUID().toString().c_str()); - Serial.print("Value: "); + Serial.print(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str()); + Serial.print(": Service = "); + Serial.print(pRemoteCharacteristic->getRemoteService()->getUUID().toString().c_str()); + Serial.print(", Characteristic = "); + Serial.print(pRemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(", Value = "); Serial.println(std::string((char*)pData, length).c_str()); } +void scanEndedCB(NimBLEScanResults results){ + Serial.println("Scan Ended"); +} + +static ClientCallbacks clientCB; + void setup (){ Serial.begin(115200); Serial.println("Starting NimBLE Client"); @@ -102,125 +110,160 @@ void setup (){ void loop (){ while(!doConnect){ - delay(10); + delay(1); } + doConnect = false; - - pClient_a[cCount] = NimBLEDevice::createClient(); - NimBLEClient* pClient = pClient_a[cCount]; - cCount++; - pClient->setClientCallbacks(new ClientCallbacks()); - pClient->setConnectionParams(6,6,0,400); - pClient->setConnectTimeout(10); - if (pClient->connect(advDevice)) { - Serial.print("Connected to: "); - Serial.println(pClient->getPeerAddress().toString().c_str()); - Serial.print("RSSI: "); - Serial.println(pClient->getRssi()); - - pSvc = pClient->getService("DEAD"); - if(pSvc) { - pChr = pSvc->getCharacteristic("BEEF"); - } - - if(pChr != nullptr) { - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("Tasty")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - pClient->disconnect(); - cCount--; - NimBLEDevice::deleteClient(pClient); - return; - } - - if(pChr->canRead()) { - Serial.print("The value of: "); - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); - Serial.println(pChr->readValue().c_str()); - } - } - - if(pChr->canNotify()) { - pChr->registerForNotify(notifyCB); - } - else if(pChr->canIndicate()) { - pChr->registerForNotify(notifyCB, false); - } - } - - else{ - Serial.println("DEAD service not found."); + NimBLEClient* pClient = nullptr; + + /** Find any disconnected clients to use first. **/ + for(uint8_t i = 0; i < clientCount; ++i) { + if(!pClient_a[i]->isConnected()) { + pClient = pClient_a[i]; } - - pSvc = pClient->getService("BAAD"); - if(pSvc) { - pChr = pSvc->getCharacteristic("F00D"); - } - - if(pChr != nullptr) { - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); - if(pDsc != nullptr) { - Serial.print(pDsc->getUUID().toString().c_str()); - Serial.print(" value: "); - Serial.println(pDsc->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("Take it back")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - pClient->disconnect(); - cCount--; - NimBLEDevice::deleteClient(pClient); - return; - } - - if(pChr->canRead()) { - Serial.print("The value of: "); - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); - Serial.println(pChr->readValue().c_str()); - } - } - - if(pChr->canNotify()) { - pChr->registerForNotify(notifyCB); - } - else if(pChr->canIndicate()) { - pChr->registerForNotify(notifyCB, false); - } - } + /** if there is more than one disconnected and this one was connected to this device before + * we should prefer to use it so we don't have to build the services database again. + */ + if(pClient && pClient->getPeerAddress().equals(advDevice->getAddress())) { + pClient = pClient_a[i]; + /** Send false as the second argument in connect() to prevent refreshing the service database. + * This saves considerable time and power use. + */ + if(!pClient->connect(advDevice, false)) { + Serial.println("Reconnect failed - Starting scanning"); + NimBLEDevice::getScan()->start(0,scanEndedCB); + return; + } + + break; + } + } - else{ - Serial.println("BAAD service not found."); + if(!pClient) { + if(clientCount >= 3) { + Serial.println("Max clients reached - no connections available"); + return; } - if(cCount>2){ - cCount--; + pClient_a[clientCount] = NimBLEDevice::createClient(); + pClient = pClient_a[clientCount]; + clientCount++; + + pClient->setClientCallbacks(&clientCB); + pClient->setConnectionParams(6,6,0,400); + pClient->setConnectTimeout(10); + + if (!pClient->connect(advDevice)) { NimBLEDevice::deleteClient(pClient); + pClient_a[--clientCount] = nullptr; + Serial.println("Failed to connect - Starting scan"); + NimBLEDevice::getScan()->start(0,scanEndedCB); + return; + } + } + + if(!pClient->isConnected()) { + if (!pClient->connect(advDevice)) { + Serial.println("Failed to connect - Starting scan"); + NimBLEDevice::getScan()->start(0,scanEndedCB); + return; } } + + Serial.print("Connected to: "); + Serial.println(pClient->getPeerAddress().toString().c_str()); + Serial.print("RSSI: "); + Serial.println(pClient->getRssi()); + + pSvc = pClient->getService("DEAD"); + if(pSvc) { + pChr = pSvc->getCharacteristic("BEEF"); + } + + if(pChr != nullptr) { + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + pClient->disconnect(); + return; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + pChr->registerForNotify(notifyCB); + } + else if(pChr->canIndicate()) { + pChr->registerForNotify(notifyCB, false); + } + } + + else{ + Serial.println("DEAD service not found."); + } + + pSvc = pClient->getService("BAAD"); + if(pSvc) { + pChr = pSvc->getCharacteristic("F00D"); + } + + if(pChr != nullptr) { + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc != nullptr) { + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Take it back")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + pClient->disconnect(); + return; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + pChr->registerForNotify(notifyCB); + } + else if(pChr->canIndicate()) { + pChr->registerForNotify(notifyCB, false); + } + } + else{ - cCount--; - NimBLEDevice::deleteClient(pClient); + Serial.println("BAAD service not found."); } NimBLEDevice::getScan()->start(0,scanEndedCB); diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 901894ba..8e4dc573 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -603,9 +603,7 @@ uint16_t NimBLEClient::getMTU() { client->m_semaphoreSearchCmplEvt.give(1); client->m_semeaphoreSecEvt.give(1); - //if (client->m_pClientCallbacks != nullptr) { client->m_pClientCallbacks->onDisconnect(client); - //} //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; @@ -630,7 +628,7 @@ uint16_t NimBLEClient::getMTU() { if (event->connect.status == 0) { // Connection successfully established. - NIMBLE_LOGI(LOG_TAG, "\nConnection established"); + NIMBLE_LOGI(LOG_TAG, "Connection established"); client->m_conn_id = event->connect.conn_handle; @@ -640,20 +638,19 @@ uint16_t NimBLEClient::getMTU() { // MODLOG_DFLT(INFO, "\n"); client->m_isConnected = true; - - //if (client->m_pClientCallbacks != nullptr) { + client->m_pClientCallbacks->onConnect(client); - //} // Incase of a multiconnecting device we ignore this device when scanning since we are already connected to it NimBLEDevice::addIgnored(client->m_peerAddress); - client->m_semaphoreOpenEvt.give(0); - + rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); if(rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, NimBLEUtils::returnCodeToString(rc)); } + + client->m_semaphoreOpenEvt.give(0); } else { // Connection attempt failed @@ -662,7 +659,7 @@ uint16_t NimBLEClient::getMTU() { client->m_semaphoreOpenEvt.give(event->connect.status); } - return 0; + return event->connect.status; } // BLE_GAP_EVENT_CONNECT case BLE_GAP_EVENT_NOTIFY_RX: { diff --git a/src/modlog/modlog.h b/src/modlog/modlog.h index 55b6b324..26777fa8 100644 --- a/src/modlog/modlog.h +++ b/src/modlog/modlog.h @@ -41,6 +41,16 @@ modlog_dummy(const char *msg, ...) #endif #ifdef ESP_PLATFORM +/// Uncomment these and comment out the 3 defines below to see NimBLE messages in Arduino. +/*#define MODLOG_DEBUG(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_INFO(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) + +#define MODLOG_WARN(ml_mod_, ml_msg_, ...) \ + esp_log_write(ESP_LOG_ERROR, "NimBLE",ml_msg_, ##__VA_ARGS__) +*/ #define MODLOG_DEBUG(ml_mod_, ml_msg_, ...) \ esp_log_write(ESP_LOG_DEBUG, "NimBLE",ml_msg_, ##__VA_ARGS__) From ad96e2207e667f06c449406d062169665794ba39 Mon Sep 17 00:00:00 2001 From: h2zero Date: Wed, 25 Mar 2020 22:52:54 -0600 Subject: [PATCH 54/62] Cleanup logging / comments. Reorder semaphore release sequences in client code. --- examples/NimBLE_Client/NimBLE_Client.ino | 2 +- src/NimBLEClient.cpp | 42 +++++++++++------------- src/NimBLERemoteCharacteristic.cpp | 3 -- src/NimBLERemoteDescriptor.cpp | 4 +-- src/NimBLERemoteService.cpp | 18 +++------- src/NimBLEScan.cpp | 2 +- 6 files changed, 28 insertions(+), 43 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index 9fed41af..eef05a41 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -141,7 +141,7 @@ void loop (){ if(!pClient) { if(clientCount >= 3) { - Serial.println("Max clients reached - no connections available"); + Serial.println("Max clients reached - no more connections available"); return; } diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 8e4dc573..b21cf43c 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -143,7 +143,7 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ } if(refreshServices) { - NIMBLE_LOGE(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); + NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); clearServices(); } @@ -154,8 +154,9 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ m_semaphoreOpenEvt.take("connect"); - /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for - * timeout. Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. + /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for + * timeout (default value of m_connectTimeout). + * Loop on BLE_HS_EBUSY if the scan hasn't stopped yet. */ do{ rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, m_pConnParams, @@ -399,7 +400,7 @@ std::map* NimBLEClient::getServices() { * @return true on success otherwise false if an error occurred */ bool NimBLEClient::retrieveServices() { -/* +/** * Design * ------ * We invoke ble_gattc_disc_all_svcs. This will request a list of the services exposed by the @@ -407,7 +408,6 @@ bool NimBLEClient::retrieveServices() { */ NIMBLE_LOGD(LOG_TAG, ">> retrieveServices"); - //clearServices(); // Clear any services that may exist. if(!m_isConnected){ NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting"); @@ -430,10 +430,8 @@ bool NimBLEClient::retrieveServices() { m_haveServices = (m_semaphoreSearchCmplEvt.wait("retrieveServices") == 0); if(m_haveServices){ for (auto &myPair : m_servicesMap) { - // if we were disconnected try to recover gracefully and release all resources if(!m_isConnected || !myPair.second->retrieveCharacteristics()) { NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve characteristics -aborting"); - //clearServices(); return false; } } @@ -442,9 +440,7 @@ bool NimBLEClient::retrieveServices() { return true; } else { - // if there was an error make sure we release any resources NIMBLE_LOGE(LOG_TAG, "Could not retrieve services"); - //clearServices(); return false; } } // getServices @@ -570,7 +566,7 @@ uint16_t NimBLEClient::getMTU() { //struct ble_hs_adv_fields fields; int rc; - NIMBLE_LOGI(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); + NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type)); // Execute handler code based on the type of event received. switch(event->type) { @@ -598,20 +594,21 @@ uint16_t NimBLEClient::getMTU() { break; } */ - + client->m_isConnected = false; + client->m_waitingToConnect=false; + + //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; + + + + // Indicate a non-success return value to any semaphores waiting client->m_semaphoreOpenEvt.give(1); client->m_semaphoreSearchCmplEvt.give(1); client->m_semeaphoreSecEvt.give(1); - client->m_pClientCallbacks->onDisconnect(client); - - //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; - - // Remove the device from ignore list so we can scan it again + // Remove the device from ignore list so we will scan it again NimBLEDevice::removeIgnored(client->m_peerAddress); - - client->m_isConnected = false; - client->m_waitingToConnect=false; + client->m_pClientCallbacks->onDisconnect(client); return 0; } // BLE_GAP_EVENT_DISCONNECT @@ -654,12 +651,13 @@ uint16_t NimBLEClient::getMTU() { } else { // Connection attempt failed - NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d", - event->connect.status); + NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s", + event->connect.status, + NimBLEUtils::returnCodeToString(event->connect.status)); client->m_semaphoreOpenEvt.give(event->connect.status); } - return event->connect.status; + return 0; } // BLE_GAP_EVENT_CONNECT case BLE_GAP_EVENT_NOTIFY_RX: { diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index d9f1f237..7c7abe1a 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -160,8 +160,6 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, } case BLE_HS_EDONE:{ /* All descriptors in this characteristic discovered; */ - - NIMBLE_LOGD(LOG_TAG,"Giving search Descriptor semaphore - completed"); characteristic->m_semaphoreGetDescEvt.give(0); rc = 0; break; @@ -173,7 +171,6 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle, if (rc != 0) { /* Error; abort discovery. */ // pass non-zero to semaphore on error to indicate an error finding descriptors - NIMBLE_LOGD(LOG_TAG,"Giving search Descriptor semaphore - failed"); characteristic->m_semaphoreGetDescEvt.give(1); } NIMBLE_LOGD(LOG_TAG,"<< Descriptor Discovered. status: %d", rc); diff --git a/src/NimBLERemoteDescriptor.cpp b/src/NimBLERemoteDescriptor.cpp index 014c6ca6..7b61cc6f 100644 --- a/src/NimBLERemoteDescriptor.cpp +++ b/src/NimBLERemoteDescriptor.cpp @@ -89,7 +89,7 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle, return 0; } - NIMBLE_LOGI(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); + NIMBLE_LOGD(LOG_TAG, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); if (error->status == 0) { desc->m_value = std::string((char*) attr->om->om_data, attr->om->om_len); @@ -209,7 +209,7 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle, return 0; } - NIMBLE_LOGI(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); + NIMBLE_LOGD(LOG_TAG, "Write complete; status=%d conn_handle=%d", error->status, conn_handle); if (error->status == 0) { descriptor->m_semaphoreDescWrite.give(0); diff --git a/src/NimBLERemoteService.cpp b/src/NimBLERemoteService.cpp index 501f3f52..28c2cc33 100644 --- a/src/NimBLERemoteService.cpp +++ b/src/NimBLERemoteService.cpp @@ -116,11 +116,9 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle, break; } case BLE_HS_EDONE:{ - /* All characteristics in this service discovered; start discovering - * characteristics in the next service. - */ - - NIMBLE_LOGD(LOG_TAG,"Giving search Characteristic semaphore - completed"); + /** All characteristics in this service discovered; start discovering + * characteristics in the next service. + */ service->m_semaphoreGetCharEvt.give(0); rc = 0; break; @@ -170,7 +168,7 @@ bool NimBLERemoteService::retrieveCharacteristics() { m_haveCharacteristics = (m_semaphoreGetCharEvt.wait("retrieveCharacteristics") == 0); if(m_haveCharacteristics){ uint16_t endHdl = 0xFFFF; - NIMBLE_LOGI(LOG_TAG, "Found %d Characteristics", m_characteristicMapByHandle.size()); + NIMBLE_LOGD(LOG_TAG, "Found %d Characteristics", m_characteristicMapByHandle.size()); for (auto it = m_characteristicMapByHandle.cbegin(); it != m_characteristicMapByHandle.cend(); ++it) { NIMBLE_LOGD(LOG_TAG, "Found UUID: %s Handle: %d Def Handle: %d", (*it).second->getUUID().toString().c_str(), (*it).second->getHandle(), (*it).second->getDefHandle()); // The descriptor handle is between this characteristic val_handle and the next ones def_handle @@ -212,8 +210,6 @@ bool NimBLERemoteService::retrieveCharacteristics() { * @return A map of all the characteristics of this service. */ std::map* NimBLERemoteService::getCharacteristics() { - NIMBLE_LOGD(LOG_TAG, "getCharacteristics() for service: %s", getUUID().toString().c_str()); - return &m_characteristicMap; } // getCharacteristics @@ -223,8 +219,6 @@ std::map* NimBLERemoteService::getChar * @return A map of all the characteristics of this service. */ std::map* NimBLERemoteService::getCharacteristicsByHandle() { - NIMBLE_LOGD(LOG_TAG, " getCharacteristicsByHandle() for service: %s", getUUID().toString().c_str()); - return &m_characteristicMapByHandle; } // getCharacteristicsByHandle @@ -311,10 +305,6 @@ bool NimBLERemoteService::setValue(NimBLEUUID characteristicUuid, std::string va * @return N/A. */ void NimBLERemoteService::removeCharacteristics() { -/* for (auto &myPair : m_characteristicMap) { - delete myPair.second; - } -*/ m_characteristicMap.clear(); // Clear the map for (auto &myPair : m_characteristicMapByHandle) { diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 2222d5d3..1f3ee916 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -153,11 +153,11 @@ NimBLEScan::NimBLEScan() { pScan->m_stopped = true; - pScan->m_semaphoreScanEnd.give(); if (pScan->m_scanCompleteCB != nullptr) { pScan->m_scanCompleteCB(pScan->m_scanResults); } + pScan->m_semaphoreScanEnd.give(); return 0; } From 55f84cc63c7efdece85f0d4aa5f626188a897209 Mon Sep 17 00:00:00 2001 From: h2zero Date: Thu, 26 Mar 2020 23:00:57 -0600 Subject: [PATCH 55/62] Working out multi connected client issues - mostly resolved. Change NimBLE scan result to return a copy to the callback as the pointer was overwritten too quickly. Update new example to reflect this change. Implement a task switch in client connect to clear up any pending operations before aquiring services data. Change callback calling in scan gap handler to pass a copy of advertised device. Set advertised device advertisment type when creating a new device only. --- examples/NimBLE_Client/NimBLE_Client.ino | 20 ++++++----- src/NimBLEAdvertisedDevice.h | 4 +-- src/NimBLEClient.cpp | 46 ++++++++++++++---------- src/NimBLEScan.cpp | 39 ++++++++++---------- 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index eef05a41..d927fe52 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -47,8 +47,8 @@ class ClientCallbacks : public NimBLEClientCallbacks { void onAuthenticationComplete(ble_gap_conn_desc* desc){ if(!desc->sec_state.encrypted) { - NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); Serial.println("Encrypt connection failed - disconnecting"); + NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); return; } }; @@ -56,21 +56,22 @@ class ClientCallbacks : public NimBLEClientCallbacks { class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + void onResult(NimBLEAdvertisedDevice advertisedDevice) { Serial.print("Advertised Device found: "); - Serial.println(advertisedDevice->toString().c_str()); - if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) + Serial.println(advertisedDevice.toString().c_str()); + if(advertisedDevice.isAdvertisingService(NimBLEUUID("DEAD"))) { Serial.println("Found Our Service"); NimBLEDevice::getScan()->stop(); - advDevice = advertisedDevice; + advDevice = new NimBLEAdvertisedDevice(advertisedDevice); doConnect = true; } } }; + void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ Serial.print((isNotify == true) ? "Notify" : "Indication"); Serial.print(" from "); @@ -149,13 +150,14 @@ void loop (){ pClient = pClient_a[clientCount]; clientCount++; - pClient->setClientCallbacks(&clientCB); - pClient->setConnectionParams(6,6,0,400); - pClient->setConnectTimeout(10); + pClient->setClientCallbacks(&clientCB, false); + pClient->setConnectionParams(24,24,0,100); + pClient->setConnectTimeout(20); if (!pClient->connect(advDevice)) { NimBLEDevice::deleteClient(pClient); - pClient_a[--clientCount] = nullptr; + pClient_a[clientCount] = nullptr; + clientCount--; Serial.println("Failed to connect - Starting scan"); NimBLEDevice::getScan()->start(0,scanEndedCB); return; diff --git a/src/NimBLEAdvertisedDevice.h b/src/NimBLEAdvertisedDevice.h index da02a93b..75d52bb3 100644 --- a/src/NimBLEAdvertisedDevice.h +++ b/src/NimBLEAdvertisedDevice.h @@ -125,8 +125,8 @@ class NimBLEAdvertisedDeviceCallbacks { * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the * device that was found. During any individual scan, a device will only be detected one time. */ - //virtual void onResult(NimBLEAdvertisedDevice advertisedDevice); - virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; + virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0; + //virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; }; #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index b21cf43c..5cdc9591 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -121,9 +121,11 @@ void NimBLEClient::onHostReset() { * Add overloaded function to ease connect to peer device with not public address */ bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) { - NimBLEAddress address = device->getAddress(); + NimBLEAddress address(device->getAddress()); uint8_t type = device->getAddressType(); - return connect(address, type, refreshServices); + bool ret = connect(address, type, refreshServices); + delete device; + return ret; //connect(address, type, refreshServices); } @@ -135,19 +137,14 @@ bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServices) { NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); - int rc = 0; - if(!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); return false; } - - if(refreshServices) { - NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); - clearServices(); - } + int rc = 0; m_peerAddress = address; + ble_addr_t peerAddrt; memcpy(&peerAddrt.val, address.getNative(),6); peerAddrt.type = type; @@ -165,8 +162,11 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; addr_type=%d " - "addr=%s", - type, address.toString().c_str()); + "addr=%s, rc=%d; %s", + type, + m_peerAddress.toString().c_str(), + rc, NimBLEUtils::returnCodeToString(rc)); + m_semaphoreOpenEvt.give(); m_waitingToConnect = false; return false; @@ -179,7 +179,14 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ if(rc != 0){ return false; } - + + if(refreshServices) { + NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str()); + clearServices(); + } + + taskYIELD(); // Let any waiting tasks finish before we get services / return + if (!m_haveServices) { if (!retrieveServices()) { // error getting services, make sure we disconnect and release any resources before returning @@ -229,6 +236,7 @@ int NimBLEClient::disconnect(uint8_t reason) { NIMBLE_LOGD(LOG_TAG, ">> disconnect()"); int rc = 0; if(m_isConnected){ + m_isConnected = false; // flag the disconnect now so no calls are performed after rc = ble_gap_terminate(m_conn_id, reason); if(rc != 0){ NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc)); @@ -583,24 +591,23 @@ uint16_t NimBLEClient::getMTU() { //print_conn_desc(&event->disconnect.conn); //MODLOG_DFLT(INFO, "\n"); - /* + switch(event->disconnect.reason) { case BLE_HS_ETIMEOUT_HCI: case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: + NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); break; default: break; } - */ + client->m_isConnected = false; client->m_waitingToConnect=false; //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; - - - + // Indicate a non-success return value to any semaphores waiting client->m_semaphoreOpenEvt.give(1); client->m_semaphoreSearchCmplEvt.give(1); @@ -645,9 +652,10 @@ uint16_t NimBLEClient::getMTU() { if(rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, NimBLEUtils::returnCodeToString(rc)); + client->m_semaphoreOpenEvt.give(rc); + } else { + client->m_semaphoreOpenEvt.give(0); } - - client->m_semaphoreOpenEvt.give(0); } else { // Connection attempt failed diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 1f3ee916..54a6c3cd 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -121,6 +121,8 @@ NimBLEScan::NimBLEScan() { advertisedDevice = new NimBLEAdvertisedDevice(); advertisedDevice->setAddressType(event->disc.addr.type); advertisedDevice->setAddress(advertisedAddress); + //NIMBLE_LOGE(LOG_TAG, "advertisement type: %d, %s",event->disc.event_type, NimBLEUtils::advTypeToString(event->disc.event_type)); + advertisedDevice->setAdvType(event->disc.event_type); pScan->m_scanResults.m_advertisedDevicesMap.insert(std::pair(advertisedAddress.toString(), advertisedDevice)); NIMBLE_LOGI(LOG_TAG, "NEW DEVICE FOUND: %s", advertisedAddress.toString().c_str()); } @@ -128,8 +130,6 @@ NimBLEScan::NimBLEScan() { NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str()); } advertisedDevice->setRSSI(event->disc.rssi); - // NIMBLE_LOGI(LOG_TAG, "advertisement type: %d, %s",advType, NimBLEUtils::advTypeToString(event->disc.event_type)); - advertisedDevice->setAdvType(event->disc.event_type); advertisedDevice->parseAdvertisement(&fields); advertisedDevice->setScan(pScan); advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data); @@ -137,10 +137,10 @@ NimBLEScan::NimBLEScan() { if (pScan->m_pAdvertisedDeviceCallbacks) { // If not active scanning report the result to the listener. if(pScan->m_scan_params.passive) { - pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); // Otherwise wait for the scan response so we can report all of the data at once. } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { - pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); + pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } //m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } @@ -151,12 +151,11 @@ NimBLEScan::NimBLEScan() { NIMBLE_LOGD(LOG_TAG, "discovery complete; reason=%d", event->disc_complete.reason); - pScan->m_stopped = true; - if (pScan->m_scanCompleteCB != nullptr) { pScan->m_scanCompleteCB(pScan->m_scanResults); } + pScan->m_stopped = true; pScan->m_semaphoreScanEnd.give(); return 0; } @@ -221,25 +220,20 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) { bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration); - // If we are already scanning don't start again or we will get stuck on the semaphore. - if(!m_stopped) { - NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); - return false; - } - - // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals - // then we should not clear map or we will connect the same device few times - if(!is_continue) { - clearResults(); - } - // If Host is not synced we cannot start scanning. if(!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); return false; } + // If we are already scanning don't start again or we will get stuck on the semaphore. + if(!m_stopped) { + NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); + return false; + } + m_semaphoreScanEnd.take("start"); + // Save the callback to be invoked when the scan completes. m_scanCompleteCB = scanCompleteCB; @@ -251,9 +245,16 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul duration = duration*1000; // convert duration to milliseconds } + // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals + // then we should not clear map or we will connect the same device few times + if(!is_continue) { + clearResults(); + } + int rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, NimBLEScan::handleGapEvent, this); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc); + NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); m_semaphoreScanEnd.give(); return false; } From e374c5011e8a320881e7619598042a3144d113b0 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 27 Mar 2020 15:12:23 -0600 Subject: [PATCH 56/62] Tweak scan start to prevent multiple scans - occasionally would start twice if called from separate tasks at nearly the same time. Move client onConnect callback to the connect method to prevent any actions performed there from interfering with getting services. --- src/NimBLEClient.cpp | 8 ++++---- src/NimBLEScan.cpp | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 5cdc9591..b2b3af70 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -135,7 +135,7 @@ bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) * @return True on success. */ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServices) { - NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + NIMBLE_LOGE(LOG_TAG, ">> connect(%s)", address.toString().c_str()); if(!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); @@ -185,8 +185,6 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ clearServices(); } - taskYIELD(); // Let any waiting tasks finish before we get services / return - if (!m_haveServices) { if (!retrieveServices()) { // error getting services, make sure we disconnect and release any resources before returning @@ -199,6 +197,8 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ } } + m_pClientCallbacks->onConnect(this); + NIMBLE_LOGD(LOG_TAG, "<< connect()"); return true; } // connect @@ -643,7 +643,7 @@ uint16_t NimBLEClient::getMTU() { client->m_isConnected = true; - client->m_pClientCallbacks->onConnect(client); + //client->m_pClientCallbacks->onConnect(client); // Incase of a multiconnecting device we ignore this device when scanning since we are already connected to it NimBLEDevice::addIgnored(client->m_peerAddress); diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index 54a6c3cd..b0c62acf 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -84,7 +84,10 @@ NimBLEScan::NimBLEScan() { switch(event->type) { case BLE_GAP_EVENT_DISC: { - NimBLEAdvertisedDevice* advertisedDevice = nullptr; + if(pScan->m_stopped) { + NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results."); + return 0; + } rc = ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data); @@ -109,6 +112,8 @@ NimBLEScan::NimBLEScan() { return 0; } + NimBLEAdvertisedDevice* advertisedDevice = nullptr; + // If we've seen this device before get a pointer to it from the map auto it = pScan->m_scanResults.m_advertisedDevicesMap.find(advertisedAddress.toString()); if(it != pScan->m_scanResults.m_advertisedDevicesMap.cend()) { @@ -218,7 +223,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) { * @return True if scan started or false if there was an error. */ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { - NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration); + NIMBLE_LOGE(LOG_TAG, ">> start(duration=%d)", duration); // If Host is not synced we cannot start scanning. if(!NimBLEDevice::m_synced) { @@ -227,11 +232,13 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul } // If we are already scanning don't start again or we will get stuck on the semaphore. - if(!m_stopped) { - NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); + if(!m_stopped || ble_gap_disc_active()) { + NIMBLE_LOGW(LOG_TAG, "Scan already in progress"); return false; } + m_stopped = false; + m_semaphoreScanEnd.take("start"); // Save the callback to be invoked when the scan completes. @@ -256,10 +263,11 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); m_semaphoreScanEnd.give(); + m_stopped = true; return false; } - m_stopped = false; + // m_stopped = false; NIMBLE_LOGD(LOG_TAG, "<< start()"); return true; @@ -285,10 +293,10 @@ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) { */ void NimBLEScan::stop() { NIMBLE_LOGD(LOG_TAG, ">> stop()"); - + int rc = ble_gap_disc_cancel(); - if (rc != 0) { - NIMBLE_LOGD(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc); + if (rc != 0 && rc != BLE_HS_EALREADY) { + NIMBLE_LOGE(LOG_TAG, "Failed to cancel scan; rc=%d\n", rc); return; } From 22be41c1baebdf46c8d59ca7bf1249467939b559 Mon Sep 17 00:00:00 2001 From: h2zero Date: Fri, 27 Mar 2020 22:42:52 -0600 Subject: [PATCH 57/62] Revert changes to advertised device callback to use pointers instead of copy. Implement host reset handling in client. --- examples/NimBLE_Client/NimBLE_Client.ino | 14 ++++----- src/NimBLEAdvertisedDevice.h | 4 +-- src/NimBLEClient.cpp | 36 +++++++++++++----------- src/NimBLEDevice.cpp | 23 +++++++++++---- src/NimBLEDevice.h | 2 +- src/NimBLERemoteCharacteristic.cpp | 2 -- src/NimBLEScan.cpp | 12 ++++---- src/NimBLEScan.h | 1 + 8 files changed, 56 insertions(+), 38 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index d927fe52..4b76c0bd 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -26,7 +26,7 @@ static bool doConnect = false; class ClientCallbacks : public NimBLEClientCallbacks { void onConnect(NimBLEClient* pClient) { Serial.println("Connected"); - //pClient->updateConnParams(24,48,0,800); + pClient->updateConnParams(120,120,0,45); }; void onDisconnect(NimBLEClient* pClient) { @@ -56,15 +56,15 @@ class ClientCallbacks : public NimBLEClientCallbacks { class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { - void onResult(NimBLEAdvertisedDevice advertisedDevice) { + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { Serial.print("Advertised Device found: "); - Serial.println(advertisedDevice.toString().c_str()); - if(advertisedDevice.isAdvertisingService(NimBLEUUID("DEAD"))) + Serial.println(advertisedDevice->toString().c_str()); + if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) { Serial.println("Found Our Service"); NimBLEDevice::getScan()->stop(); - advDevice = new NimBLEAdvertisedDevice(advertisedDevice); + advDevice = advertisedDevice; doConnect = true; } } @@ -151,8 +151,8 @@ void loop (){ clientCount++; pClient->setClientCallbacks(&clientCB, false); - pClient->setConnectionParams(24,24,0,100); - pClient->setConnectTimeout(20); + pClient->setConnectionParams(12,12,0,12); + pClient->setConnectTimeout(5); if (!pClient->connect(advDevice)) { NimBLEDevice::deleteClient(pClient); diff --git a/src/NimBLEAdvertisedDevice.h b/src/NimBLEAdvertisedDevice.h index 75d52bb3..b6b2f706 100644 --- a/src/NimBLEAdvertisedDevice.h +++ b/src/NimBLEAdvertisedDevice.h @@ -125,8 +125,8 @@ class NimBLEAdvertisedDeviceCallbacks { * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the * device that was found. During any individual scan, a device will only be detected one time. */ - virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0; - //virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; + //virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0; + virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0; }; #endif /* CONFIG_BT_ENABLED */ diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index b2b3af70..0b91798b 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -123,9 +123,7 @@ void NimBLEClient::onHostReset() { bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) { NimBLEAddress address(device->getAddress()); uint8_t type = device->getAddressType(); - bool ret = connect(address, type, refreshServices); - delete device; - return ret; //connect(address, type, refreshServices); + return connect(address, type, refreshServices); } @@ -306,7 +304,8 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval, int rc = ble_gap_update_params(m_conn_id, ¶ms); if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); } } @@ -351,7 +350,8 @@ int NimBLEClient::getRssi() { int8_t rssiValue = 0; int rc = ble_gap_conn_rssi(m_conn_id, &rssiValue); if(rc != 0) { - NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d", rc); + NIMBLE_LOGE(LOG_TAG, "Failed to read RSSI error code: %d, %s", + rc, NimBLEUtils::returnCodeToString(rc)); return 0; } @@ -586,25 +586,28 @@ uint16_t NimBLEClient::getMTU() { if(client->m_conn_id != event->disconnect.conn.conn_handle) return 0; + client->m_isConnected = false; + client->m_waitingToConnect=false; + NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason, NimBLEUtils::returnCodeToString(event->disconnect.reason)); //print_conn_desc(&event->disconnect.conn); //MODLOG_DFLT(INFO, "\n"); + // if Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. switch(event->disconnect.reason) { case BLE_HS_ETIMEOUT_HCI: case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NimBLEDevice::onReset(event->disconnect.reason); break; default: break; } - - client->m_isConnected = false; - client->m_waitingToConnect=false; //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE; @@ -631,8 +634,9 @@ uint16_t NimBLEClient::getMTU() { client->m_waitingToConnect=false; if (event->connect.status == 0) { - // Connection successfully established. - NIMBLE_LOGI(LOG_TAG, "Connection established"); + client->m_isConnected = true; + + NIMBLE_LOGD(LOG_TAG, "Connection established"); client->m_conn_id = event->connect.conn_handle; @@ -641,21 +645,21 @@ uint16_t NimBLEClient::getMTU() { // print_conn_desc(&desc); // MODLOG_DFLT(INFO, "\n"); - client->m_isConnected = true; - //client->m_pClientCallbacks->onConnect(client); - // Incase of a multiconnecting device we ignore this device when scanning since we are already connected to it + // In the case of a multiconnecting device we ignore this device when + // scanning since we are already connected to it NimBLEDevice::addIgnored(client->m_peerAddress); rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL); if(rc != 0) { NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc, NimBLEUtils::returnCodeToString(rc)); + // if error getting mtu indicate a connection error. client->m_semaphoreOpenEvt.give(rc); - } else { + } /*else { client->m_semaphoreOpenEvt.give(0); - } + }*/ } else { // Connection attempt failed @@ -760,7 +764,7 @@ uint16_t NimBLEClient::getMTU() { NIMBLE_LOGI(LOG_TAG, "mtu update event; conn_handle=%d mtu=%d", event->mtu.conn_handle, event->mtu.value); - + client->m_semaphoreOpenEvt.give(0); //client->m_mtu = event->mtu.value; return 0; } // BLE_GAP_EVENT_MTU diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 2c5a1211..3f40d1c9 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -266,7 +266,12 @@ void NimBLEDevice::stopAdvertising() { */ /* STATIC */ void NimBLEDevice::onReset(int reason) { + if(!m_synced) { + return; + } + m_synced = false; + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { (*it)->onHostReset(); } @@ -293,15 +298,23 @@ void NimBLEDevice::stopAdvertising() { */ /* STATIC */ void NimBLEDevice::onSync(void) { - int rc; - + NIMBLE_LOGE(LOG_TAG, "NimBle host synced."); + /* Make sure we have proper identity address set (public preferred) */ - rc = ble_hs_util_ensure_addr(0); + int rc = ble_hs_util_ensure_addr(0); assert(rc == 0); - NIMBLE_LOGI(LOG_TAG, "NimBle host synced."); - m_synced = true; + + 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); + } + + if(m_bleAdvertising != nullptr) { + // Restart advertisng, parameters should already be set. + m_bleAdvertising->start(); + } } // onSync diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index f16ae2fa..192ce899 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -123,7 +123,7 @@ class NimBLEDevice { static std::list m_cList; static std::list m_ignoreList; static NimBLESecurityCallbacks* m_securityCallbacks; - + public: static gap_event_handler m_customGapHandler; }; diff --git a/src/NimBLERemoteCharacteristic.cpp b/src/NimBLERemoteCharacteristic.cpp index 7c7abe1a..f054c005 100644 --- a/src/NimBLERemoteCharacteristic.cpp +++ b/src/NimBLERemoteCharacteristic.cpp @@ -432,8 +432,6 @@ bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallbac return desc->writeValue(val, 2, response); } // registerForNotify -//END_H2ZERO_MOD - /** diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index b0c62acf..f3d36b9a 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -142,10 +142,10 @@ NimBLEScan::NimBLEScan() { if (pScan->m_pAdvertisedDeviceCallbacks) { // If not active scanning report the result to the listener. if(pScan->m_scan_params.passive) { - pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); // Otherwise wait for the scan response so we can report all of the data at once. } else if (event->disc.event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP) { - pScan->m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); + pScan->m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice); } //m_pAdvertisedDeviceCallbacks->onResult(*advertisedDevice); } @@ -224,7 +224,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) { */ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { NIMBLE_LOGE(LOG_TAG, ">> start(duration=%d)", duration); - + // If Host is not synced we cannot start scanning. if(!NimBLEDevice::m_synced) { NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); @@ -232,17 +232,19 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul } // If we are already scanning don't start again or we will get stuck on the semaphore. - if(!m_stopped || ble_gap_disc_active()) { + if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset. NIMBLE_LOGW(LOG_TAG, "Scan already in progress"); return false; } - + m_stopped = false; m_semaphoreScanEnd.take("start"); // Save the callback to be invoked when the scan completes. m_scanCompleteCB = scanCompleteCB; + // Save the duration in the case that the host is reset so we can reuse it. + m_duration = duration; // If 0 duration specified then we assume a continuous scan is desired. if(duration == 0){ diff --git a/src/NimBLEScan.h b/src/NimBLEScan.h index 9761a435..a19a3da0 100644 --- a/src/NimBLEScan.h +++ b/src/NimBLEScan.h @@ -79,6 +79,7 @@ class NimBLEScan { bool m_wantDuplicates; NimBLEScanResults m_scanResults; FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd"); + uint32_t m_duration; }; From 69bc2512a02fe90569e0b7b56882a5ded427095a Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 28 Mar 2020 20:14:52 -0600 Subject: [PATCH 58/62] Advertising now handles host reset and max connections correctly, advertising starts again after host re-syncs. Client now returns false if attempting to connect to more than 1 peripheral at the same time. Client now handles host resets and begins scanning after re-syncing. Client should only invoke authentication complete callback if encryption change event status is success. Add getClientListSize(), getClientByAddress() and getDisconnectedClient() methods to NimBLEDevice. Add protection to multiple calls of onHostSync to NimBLEDevice to prevent simultaneous calls to scan start and advertising start. Scan start now loops repeatedly if host is in busy (connecting) state to allow scanning to start immediately, if called. --- examples/NimBLE_Client/NimBLE_Client.ino | 527 +++++++++++++---------- examples/NimBLE_Server/NimBLE_Server.ino | 368 ++++++++-------- src/NimBLEAdvertising.cpp | 19 +- src/NimBLEAdvertising.h | 2 +- src/NimBLEClient.cpp | 83 ++-- src/NimBLEClient.h | 2 +- src/NimBLEDevice.cpp | 65 ++- src/NimBLEDevice.h | 6 +- src/NimBLEScan.cpp | 21 +- src/NimBLEServer.cpp | 53 +-- src/NimBLEServer.h | 2 +- 11 files changed, 650 insertions(+), 498 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index 4b76c0bd..b889a0cd 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -12,261 +12,356 @@ void scanEndedCB(NimBLEScanResults results); -static NimBLERemoteService* pSvc = nullptr; -static NimBLERemoteCharacteristic* pChr = nullptr; -static NimBLERemoteDescriptor* pDsc = nullptr; - static NimBLEAdvertisedDevice* advDevice; -static NimBLEClient* pClient_a[3] = {nullptr}; -static uint8_t clientCount = 0; static bool doConnect = false; +static uint32_t scanTime = 0; /** 0 = scan forever */ +/** None of these are required as they will be handled by the library with defaults. ** + ** Remove as you see fit for your needs */ class ClientCallbacks : public NimBLEClientCallbacks { - void onConnect(NimBLEClient* pClient) { - Serial.println("Connected"); + void onConnect(NimBLEClient* pClient) { + Serial.println("Connected"); + /** After connection we should change the parameters if we don't need fast response times. + * These settings are 150ms interval, 0 latency, 450ms timout. + * Timeout should be a multiple of the interval, minimum is 100ms. + * I find a multiple of 3-5 * the interval works best for quick response/reconnect. + * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout + */ pClient->updateConnParams(120,120,0,45); - }; + }; - void onDisconnect(NimBLEClient* pClient) { - Serial.println("Disconnected - Starting scanning"); - NimBLEDevice::getScan()->start(0,scanEndedCB); - }; - - uint32_t onPassKeyRequest(){ - Serial.println("Client Passkey Request"); - return 123456; - }; + void onDisconnect(NimBLEClient* pClient) { + Serial.print(pClient->getPeerAddress().toString().c_str()); + Serial.println(" Disconnected - Starting scan"); + NimBLEDevice::getScan()->start(scanTime, scanEndedCB); + }; + + /********************* Security handled here ********************** + ****** Note: these are the same return values as defaults ********/ + uint32_t onPassKeyRequest(){ + Serial.println("Client Passkey Request"); + /** return the passkey to send to the server */ + return 123456; + }; - bool onConfirmPIN(uint32_t pass_key){ - Serial.print("The passkey YES/NO number: "); + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: "); Serial.println(pass_key); - return true; - }; + /** Return false if passkeys don't match. */ + return true; + }; - void onAuthenticationComplete(ble_gap_conn_desc* desc){ - if(!desc->sec_state.encrypted) { + /** Pairing process complete, we can check the results in ble_gap_conn_desc */ + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + if(!desc->sec_state.encrypted) { Serial.println("Encrypt connection failed - disconnecting"); + /** Find the client with the connection handle provided in desc */ NimBLEDevice::getClientByID(desc->conn_handle)->disconnect(); return; } - }; + }; }; +/** Define a class to handle the callbacks when advertisments are received */ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { - void onResult(NimBLEAdvertisedDevice* advertisedDevice) { - Serial.print("Advertised Device found: "); + + void onResult(NimBLEAdvertisedDevice* advertisedDevice) { + Serial.print("Advertised Device found: "); Serial.println(advertisedDevice->toString().c_str()); - if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) - { - Serial.println("Found Our Service"); - NimBLEDevice::getScan()->stop(); - - advDevice = advertisedDevice; - doConnect = true; - } - } + if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD"))) + { + Serial.println("Found Our Service"); + /** stop scan before connecting */ + NimBLEDevice::getScan()->stop(); + /** Save the device reference in a global for the client to use*/ + advDevice = advertisedDevice; + /** Ready to connect now */ + doConnect = true; + } + }; }; - +/** Notification / Indication receiving handler callback */ void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ Serial.print((isNotify == true) ? "Notify" : "Indication"); Serial.print(" from "); - Serial.print(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str()); - Serial.print(": Service = "); - Serial.print(pRemoteCharacteristic->getRemoteService()->getUUID().toString().c_str()); - Serial.print(", Characteristic = "); - Serial.print(pRemoteCharacteristic->getUUID().toString().c_str()); - Serial.print(", Value = "); + Serial.print(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str()); + Serial.print(": Service = "); + Serial.print(pRemoteCharacteristic->getRemoteService()->getUUID().toString().c_str()); + Serial.print(", Characteristic = "); + Serial.print(pRemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(", Value = "); Serial.println(std::string((char*)pData, length).c_str()); } - +/** Callback to process the results of the last scan or restart it */ void scanEndedCB(NimBLEScanResults results){ - Serial.println("Scan Ended"); + Serial.println("Scan Ended"); } + +/** Create a single global instance of the callback class to be used by all clients */ static ClientCallbacks clientCB; + +/** Handles the provisioning of clients and connects / interfaces with the server */ +bool connectToServer() { + NimBLEClient* pClient = nullptr; + + /** Check if we have a client we should reuse first **/ + if(NimBLEDevice::getClientListSize()) { + /** Special case when we already know this device, we send false as the + * second argument in connect() to prevent refreshing the service database. + * This saves considerable time and power. + */ + pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress()); + if(pClient){ + if(!pClient->connect(advDevice, false)) { + Serial.println("Reconnect failed"); + return false; + } + Serial.println("Reconnected client"); + } + /** We don't already have a client that knows this device, + * we will check for a client that is disconnected that we can use. + */ + else { + pClient = NimBLEDevice::getDisconnectedClient(); + } + } + + /** No client to reuse? Create a new one. */ + if(!pClient) { + if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { + Serial.println("Max clients reached - no more connections available"); + return false; + } + + pClient = NimBLEDevice::createClient(); + + Serial.println("New client created"); + + pClient->setClientCallbacks(&clientCB, false); + /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout. + * These settings are safe for 3 clients to connect reliably, can go faster if you have less + * connections. Timeout should be a multiple of the interval, minimum is 100ms. + * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout + */ + pClient->setConnectionParams(12,12,0,12); + /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ + pClient->setConnectTimeout(5); + + + if (!pClient->connect(advDevice)) { + /** Created a client but failed to connect, don't need to keep it as it has no data */ + NimBLEDevice::deleteClient(pClient); + Serial.println("Failed to connect, deleted client"); + return false; + } + } + + if(!pClient->isConnected()) { + if (!pClient->connect(advDevice)) { + Serial.println("Failed to connect"); + return false; + } + } + + Serial.print("Connected to: "); + Serial.println(pClient->getPeerAddress().toString().c_str()); + Serial.print("RSSI: "); + Serial.println(pClient->getRssi()); + + /** Now we can read/write/subscribe the charateristics of the services we are interested in */ + NimBLERemoteService* pSvc = nullptr; + NimBLERemoteCharacteristic* pChr = nullptr; + NimBLERemoteDescriptor* pDsc = nullptr; + + pSvc = pClient->getService("DEAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("BEEF"); + } + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("Tasty")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + /** Must send a callback to subscribe, if nullptr it will unsubscribe */ + if(!pChr->registerForNotify(notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as second argument to subscribe to indications instead of notifications */ + if(!pChr->registerForNotify(notifyCB, false)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + else{ + Serial.println("DEAD service not found."); + } + + pSvc = pClient->getService("BAAD"); + if(pSvc) { /** make sure it's not null */ + pChr = pSvc->getCharacteristic("F00D"); + } + + if(pChr) { /** make sure it's not null */ + if(pChr->canRead()) { + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pChr->readValue().c_str()); + } + + pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); + if(pDsc) { /** make sure it's not null */ + Serial.print("Descriptor: "); + Serial.print(pDsc->getUUID().toString().c_str()); + Serial.print(" Value: "); + Serial.println(pDsc->readValue().c_str()); + } + + if(pChr->canWrite()) { + if(pChr->writeValue("No tip!")) { + Serial.print("Wrote new value to: "); + Serial.println(pChr->getUUID().toString().c_str()); + } + else { + /** Disconnect if write failed */ + pClient->disconnect(); + return false; + } + + if(pChr->canRead()) { + Serial.print("The value of: "); + Serial.print(pChr->getUUID().toString().c_str()); + Serial.print(" is now: "); + Serial.println(pChr->readValue().c_str()); + } + } + + if(pChr->canNotify()) { + /** Must send a callback to subscribe, if nullptr it will unsubscribe */ + if(!pChr->registerForNotify(notifyCB)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + else if(pChr->canIndicate()) { + /** Send false as second argument to subscribe to indications instead of notifications */ + if(!pChr->registerForNotify(notifyCB, false)) { + /** Disconnect if subscribe failed */ + pClient->disconnect(); + return false; + } + } + } + + else{ + Serial.println("BAAD service not found."); + } + + Serial.println("Done with this device!"); + return true; +} + void setup (){ - Serial.begin(115200); - Serial.println("Starting NimBLE Client"); - NimBLEDevice::init(""); + Serial.begin(115200); + Serial.println("Starting NimBLE Client"); + /** Initialize NimBLE, no device name spcified as we are not advertising */ + NimBLEDevice::init(""); + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison -// BLEDevice::addIgnored(NimBLEAddress ("cc:b8:6c:d6:a8:82")); - -// BLEDevice::setPower(ESP_PWR_LVL_P9); - - NimBLEScan* pScan = NimBLEDevice::getScan(); //create new scan - pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); - pScan->setInterval(1349); - pScan->setWindow(449); - pScan->setActiveScan(true); - pScan->start(0, scanEndedCB); + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecuityAuth(false, false, true); + NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + /** Optional: set the transmit power, default is -3db */ + NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** 12db */ + + /** Optional: set any devices you don't want to get advertisments from */ + // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff")); + + /** create new scan */ + NimBLEScan* pScan = NimBLEDevice::getScan(); + + /** create a callback that gets called when advertisers are found */ + pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); + + /** Set scan interval (how often) and window (how long) in milliseconds */ + pScan->setInterval(400); + pScan->setWindow(100); + + /** Active scan will gather scan response data from advertisers + * but will use more energy from both devices + */ + pScan->setActiveScan(true); + /** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever + * Optional callback for when scanning stops. + */ + pScan->start(scanTime, scanEndedCB); } void loop (){ - while(!doConnect){ - delay(1); - } - - doConnect = false; - NimBLEClient* pClient = nullptr; - - /** Find any disconnected clients to use first. **/ - for(uint8_t i = 0; i < clientCount; ++i) { - if(!pClient_a[i]->isConnected()) { - pClient = pClient_a[i]; - } - /** if there is more than one disconnected and this one was connected to this device before - * we should prefer to use it so we don't have to build the services database again. - */ - if(pClient && pClient->getPeerAddress().equals(advDevice->getAddress())) { - pClient = pClient_a[i]; - /** Send false as the second argument in connect() to prevent refreshing the service database. - * This saves considerable time and power use. - */ - if(!pClient->connect(advDevice, false)) { - Serial.println("Reconnect failed - Starting scanning"); - NimBLEDevice::getScan()->start(0,scanEndedCB); - return; - } - - break; - } - } - - if(!pClient) { - if(clientCount >= 3) { - Serial.println("Max clients reached - no more connections available"); - return; - } - - pClient_a[clientCount] = NimBLEDevice::createClient(); - pClient = pClient_a[clientCount]; - clientCount++; - - pClient->setClientCallbacks(&clientCB, false); - pClient->setConnectionParams(12,12,0,12); - pClient->setConnectTimeout(5); - - if (!pClient->connect(advDevice)) { - NimBLEDevice::deleteClient(pClient); - pClient_a[clientCount] = nullptr; - clientCount--; - Serial.println("Failed to connect - Starting scan"); - NimBLEDevice::getScan()->start(0,scanEndedCB); - return; - } - } - - if(!pClient->isConnected()) { - if (!pClient->connect(advDevice)) { - Serial.println("Failed to connect - Starting scan"); - NimBLEDevice::getScan()->start(0,scanEndedCB); - return; - } - } - - Serial.print("Connected to: "); - Serial.println(pClient->getPeerAddress().toString().c_str()); - Serial.print("RSSI: "); - Serial.println(pClient->getRssi()); - - pSvc = pClient->getService("DEAD"); - if(pSvc) { - pChr = pSvc->getCharacteristic("BEEF"); - } - - if(pChr != nullptr) { - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("Tasty")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - pClient->disconnect(); - return; - } - - if(pChr->canRead()) { - Serial.print("The value of: "); - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); - Serial.println(pChr->readValue().c_str()); - } - } - - if(pChr->canNotify()) { - pChr->registerForNotify(notifyCB); - } - else if(pChr->canIndicate()) { - pChr->registerForNotify(notifyCB, false); - } - } - - else{ - Serial.println("DEAD service not found."); - } - - pSvc = pClient->getService("BAAD"); - if(pSvc) { - pChr = pSvc->getCharacteristic("F00D"); - } - - if(pChr != nullptr) { - if(pChr->canRead()) { - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" Value: "); - Serial.println(pChr->readValue().c_str()); - } - - pDsc = pChr->getDescriptor(NimBLEUUID("C01D")); - if(pDsc != nullptr) { - Serial.print(pDsc->getUUID().toString().c_str()); - Serial.print(" value: "); - Serial.println(pDsc->readValue().c_str()); - } - - if(pChr->canWrite()) { - if(pChr->writeValue("Take it back")) { - Serial.print("Wrote new value to: "); - Serial.println(pChr->getUUID().toString().c_str()); - } - else { - pClient->disconnect(); - return; - } - - if(pChr->canRead()) { - Serial.print("The value of: "); - Serial.print(pChr->getUUID().toString().c_str()); - Serial.print(" is now: "); - Serial.println(pChr->readValue().c_str()); - } - } - - if(pChr->canNotify()) { - pChr->registerForNotify(notifyCB); - } - else if(pChr->canIndicate()) { - pChr->registerForNotify(notifyCB, false); - } - } - - else{ - Serial.println("BAAD service not found."); - } + /** Loop here until we find a device we want to connect to */ + while(!doConnect){ + delay(1); + } + + doConnect = false; + + /** Found a device we want to connect to, do it now */ + if(connectToServer()) { + Serial.println("Success! we should now be getting notifications, scanning for more!"); + } else { + Serial.println("Failed to connect, starting scan"); + } - NimBLEDevice::getScan()->start(0,scanEndedCB); + NimBLEDevice::getScan()->start(scanTime,scanEndedCB); } diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino index c8a0bb0f..a30ce8b1 100644 --- a/examples/NimBLE_Server/NimBLE_Server.ino +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -12,234 +12,236 @@ #include #include +static NimBLEServer* pServer; /** None of these are required as they will be handled by the library with defaults. ** ** Remove as you see fit for your needs */ class ServerCallbacks: public NimBLEServerCallbacks { - void onConnect(NimBLEServer* pServer) { - Serial.println("Client connected"); - Serial.println("Multi-connect support: start advertising"); - NimBLEDevice::startAdvertising(); - }; - /** Alternative onConnect() method to extract details of the connection. - * See: src/ble_gap.h for the details of the ble_gap_conn_desc struct. - */ - void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { - Serial.print("Client address: "); - Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); - /** We can use the connection handle here to ask for different connection parameters. - * Args: connection handle, min connection interval, max connection interval - * latency, supervision timeout. - */ - pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 800); - }; - void onDisconnect(NimBLEServer* pServer) { - Serial.println("Client disconnected - starting advertising"); - NimBLEDevice::startAdvertising(); - }; + void onConnect(NimBLEServer* pServer) { + Serial.println("Client connected"); + Serial.println("Multi-connect support: start advertising"); + NimBLEDevice::startAdvertising(); + }; + /** Alternative onConnect() method to extract details of the connection. + * See: src/ble_gap.h for the details of the ble_gap_conn_desc struct. + */ + void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) { + Serial.print("Client address: "); + Serial.println(NimBLEAddress(desc->peer_ota_addr).toString().c_str()); + /** We can use the connection handle here to ask for different connection parameters. + * Args: connection handle, min connection interval, max connection interval + * latency, supervision timeout. + * Units; Min/Max Intervals: 1.25 millisecond increments. + * Latency: number of intervals allowed to skip. + * Timeout: 10 millisecond increments, try for 3x interval time for best results. + */ + pServer->updateConnParams(desc->conn_handle, 24, 48, 0, 18); + }; + void onDisconnect(NimBLEServer* pServer) { + Serial.println("Client disconnected - start advertising"); + NimBLEDevice::startAdvertising(); + }; /********************* Security handled here ********************** ****** Note: these are the same return values as defaults ********/ - uint32_t onPassKeyRequest(){ - Serial.println("Server Passkey Request"); - /** This should return a random 6 digit number for security - * or make your own static passkey as done here. - */ - return 123456; - }; - - bool onConfirmPIN(uint32_t pass_key){ - Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); - /** Return false if passkeys don't match. */ - return true; - }; - - void onAuthenticationComplete(ble_gap_conn_desc* desc){ - /** Check that encryption was successful, if not we disconnect the client */ - if(!desc->sec_state.encrypted) { - // createServer returns the current server reference unless one is not already created - NimBLEDevice::createServer()->disconnect(desc->conn_handle); - Serial.println("Encrypt connection failed - disconnecting client"); - return; - } - Serial.println("Starting BLE work!"); - } + uint32_t onPassKeyRequest(){ + Serial.println("Server Passkey Request"); + /** This should return a random 6 digit number for security + * or make your own static passkey as done here. + */ + return 123456; + }; + + bool onConfirmPIN(uint32_t pass_key){ + Serial.print("The passkey YES/NO number: ");Serial.println(pass_key); + /** Return false if passkeys don't match. */ + return true; + }; + + void onAuthenticationComplete(ble_gap_conn_desc* desc){ + /** Check that encryption was successful, if not we disconnect the client */ + if(!desc->sec_state.encrypted) { + /** NOTE: createServer returns the current server reference unless one is not already created */ + NimBLEDevice::createServer()->disconnect(desc->conn_handle); + Serial.println("Encrypt connection failed - disconnecting client"); + return; + } + Serial.println("Starting BLE work!"); + }; }; - +/** Handler class for characteristic actions */ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { - void onRead(NimBLECharacteristic* pCharacteristic){ - Serial.print(pCharacteristic->getUUID().toString().c_str()); - Serial.print(": onRead(), value: "); - Serial.println(pCharacteristic->getValue().c_str()); - }; - - void onWrite(NimBLECharacteristic* pCharacteristic){ - Serial.print(pCharacteristic->getUUID().toString().c_str()); - Serial.print(": onWrite(), value: "); - Serial.println(pCharacteristic->getValue().c_str()); - }; - - void onNotify(NimBLECharacteristic* pCharacteristic){ - Serial.println("Sending notification to client"); - }; - - - /** The status returned in s is defined in NimBLECharacteristic.h. - * The value returned in code is the NimBLE host return code. - */ - void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code){ - Serial.print("Notification/Indication status code: "); - Serial.print(s); - Serial.print(", return code: "); - Serial.println(NimBLEUtils::returnCodeToString(code)); - }; + void onRead(NimBLECharacteristic* pCharacteristic){ + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onRead(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + + void onWrite(NimBLECharacteristic* pCharacteristic) { + Serial.print(pCharacteristic->getUUID().toString().c_str()); + Serial.print(": onWrite(), value: "); + Serial.println(pCharacteristic->getValue().c_str()); + }; + /** Called before notification or indication is sent, + * the value can be changed here before sending if desired. + */ + void onNotify(NimBLECharacteristic* pCharacteristic) { + Serial.println("Sending notification to clients"); + }; + + + /** The status returned in s is defined in NimBLECharacteristic.h. + * The value returned in code is the NimBLE host return code. + */ + void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { + Serial.print("Notification/Indication status code: "); + Serial.print(s); + Serial.print(", return code: "); + Serial.print(code); + Serial.print(", "); + Serial.println(NimBLEUtils::returnCodeToString(code)); + }; }; - -class DescriptorCallbacks : public NimBLEDescriptorCallbacks -{ - void onWrite(NimBLEDescriptor* pDescriptor) - { - if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))){ - /** Cast to NimBLE2902 to use the class specific functions. **/ - NimBLE2902* p2902 = (NimBLE2902*)pDescriptor; - if(p2902->getNotifications()) - { - Serial.println("Client Subscribed to notfications"); - } - else - { - Serial.println("Client Unubscribed to notfications"); - } - } - else { - std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength()); - Serial.print("Descriptor witten value:"); - Serial.println(dscVal.c_str()); - } - }; - - void onRead(NimBLEDescriptor* pDescriptor) - { - Serial.print(pDescriptor->getUUID().toString().c_str()); - Serial.println(" Descriptor read"); - }; +/** Handler class for descriptor actions */ +class DescriptorCallbacks : public NimBLEDescriptorCallbacks { + void onWrite(NimBLEDescriptor* pDescriptor) { + if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))) { + /** Cast to NimBLE2902 to use the class specific functions. **/ + NimBLE2902* p2902 = (NimBLE2902*)pDescriptor; + if(p2902->getNotifications()) { + Serial.println("Client Subscribed to notfications"); + } else { + Serial.println("Client Unubscribed to notfications"); + } + } else { + std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength()); + Serial.print("Descriptor witten value:"); + Serial.println(dscVal.c_str()); + } + }; + + void onRead(NimBLEDescriptor* pDescriptor) { + Serial.print(pDescriptor->getUUID().toString().c_str()); + Serial.println(" Descriptor read"); + }; }; -/** Define callback instances globally to use for multiple Charateristics \ Descriptors **/ +/** Define callback instances globally to use for multiple Charateristics \ Descriptors */ static DescriptorCallbacks dscCallbacks; static CharacteristicCallbacks chrCallbacks; -static NimBLEServer* pServer; void setup() { - Serial.begin(115200); - Serial.println("Starting NimBLE Server"); - - NimBLEDevice::init("NimBLE-Arduino"); // sets device name - - /** Set the IO capabilities of the device, each option will trigger a different pairing method. - * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing - * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing - * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing - */ - //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey - //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison - - /** 2 different ways to set security - both calls achieve the same result. - * no bonding, no man in the middle protection, secure connections. - * These are the default values, only shown here for demonstration. - */ - //NimBLEDevice::setSecuityAuth(false, false, true); - NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); - - pServer = NimBLEDevice::createServer(); - pServer->setCallbacks(new ServerCallbacks()); - - NimBLEService* pDeadService = pServer->createService("DEAD"); - NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( + Serial.begin(115200); + Serial.println("Starting NimBLE Server"); + + /** sets device name */ + NimBLEDevice::init("NimBLE-Arduino"); + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey + //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ + //NimBLEDevice::setSecuityAuth(false, false, true); + NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + pServer = NimBLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + NimBLEService* pDeadService = pServer->createService("DEAD"); + NimBLECharacteristic* pBeefCharacteristic = pDeadService->createCharacteristic( "BEEF", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | - /** Require a secure connection for read and write access **/ + /** Require a secure connection for read and write access */ NIMBLE_PROPERTY::READ_ENC | // only allow reading if paired / encrypted NIMBLE_PROPERTY::WRITE_ENC // only allow writing if paired / encrypted ); - pBeefCharacteristic->setValue("Burger"); - pBeefCharacteristic->setCallbacks(&chrCallbacks); - - /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with - * either of those uuid's it will create the associated class with the correct properties - * and sizes. However we must cast the returned reference to the correct type as the method - * only returns a pointer to the base NimBLEDescriptor class. - */ - NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); - pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); - pBeef2904->setCallbacks(&dscCallbacks); + pBeefCharacteristic->setValue("Burger"); + pBeefCharacteristic->setCallbacks(&chrCallbacks); + + /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with + * either of those uuid's it will create the associated class with the correct properties + * and sizes. However we must cast the returned reference to the correct type as the method + * only returns a pointer to the base NimBLEDescriptor class. + */ + NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); + pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); + pBeef2904->setCallbacks(&dscCallbacks); - NimBLEService* pBaadService = pServer->createService("BAAD"); - NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( + NimBLEService* pBaadService = pServer->createService("BAAD"); + NimBLECharacteristic* pFoodCharacteristic = pBaadService->createCharacteristic( "F00D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY ); - pFoodCharacteristic->setValue("Fries"); - pFoodCharacteristic->setCallbacks(&chrCallbacks); - - /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value **/ - NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( + pFoodCharacteristic->setValue("Fries"); + pFoodCharacteristic->setCallbacks(&chrCallbacks); + + /** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */ + NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor( "C01D", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE| NIMBLE_PROPERTY::WRITE_ENC, // only allow writing if paired / encrypted 20 ); - pC01Ddsc->setValue("No tip!"); - pC01Ddsc->setCallbacks(&dscCallbacks); - - /** Note a 2902 descriptor does NOT need to be created as any chactateristic with - * notification or indication properties will have one created autmatically. - * Manually creating it is only useful if you wish to handle callback functions - * as shown here. Otherwise this can be removed without loss of functionality. - */ - NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); - pFood2902->setCallbacks(&dscCallbacks); - - /** Start the services when finished creating all Characteristics and Descriptors **/ - pDeadService->start(); - pBaadService->start(); - - NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); - /** Add the services to the advertisment data **/ - pAdvertising->addServiceUUID(pDeadService->getUUID()); - pAdvertising->addServiceUUID(pBaadService->getUUID()); - /** If your device is battery powered you may consider setting scan response - * to false as it will extend battery life at the expense of less data sent. - */ - pAdvertising->setScanResponse(true); - pAdvertising->start(); - - Serial.println("Advertising Started"); + pC01Ddsc->setValue("Send it back!"); + pC01Ddsc->setCallbacks(&dscCallbacks); + + /** Note a 2902 descriptor does NOT need to be created as any chactateristic with + * notification or indication properties will have one created autmatically. + * Manually creating it is only useful if you wish to handle callback functions + * as shown here. Otherwise this can be removed without loss of functionality. + */ + NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); + pFood2902->setCallbacks(&dscCallbacks); + + /** Start the services when finished creating all Characteristics and Descriptors */ + pDeadService->start(); + pBaadService->start(); + + NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising(); + /** Add the services to the advertisment data **/ + pAdvertising->addServiceUUID(pDeadService->getUUID()); + pAdvertising->addServiceUUID(pBaadService->getUUID()); + /** If your device is battery powered you may consider setting scan response + * to false as it will extend battery life at the expense of less data sent. + */ + pAdvertising->setScanResponse(true); + pAdvertising->start(); + + Serial.println("Advertising Started"); } void loop() { /** Do your thing here, this just spams notifications to all connected clients */ - if(pServer->getConnectedCount()) { - NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); - if(pSvc) { - NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); - if(pChr) { - pChr->notify(true); - } + if(pServer->getConnectedCount()) { + NimBLEService* pSvc = pServer->getServiceByUUID("BAAD"); + if(pSvc) { + NimBLECharacteristic* pChr = pSvc->getCharacteristic("F00D"); + if(pChr) { + pChr->notify(true); + } + } } - } - + delay(2000); -} \ No newline at end of file +} diff --git a/src/NimBLEAdvertising.cpp b/src/NimBLEAdvertising.cpp index b9d801bc..fe44be15 100644 --- a/src/NimBLEAdvertising.cpp +++ b/src/NimBLEAdvertising.cpp @@ -182,6 +182,17 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme void NimBLEAdvertising::start() { NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData); + // If Host is not synced we cannot start advertising. + if(!NimBLEDevice::m_synced) { + NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + return; + } + + if(NimBLEDevice::createServer()->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising"); + return; + } + int numServices = m_serviceUUIDs.size(); int rc = 0; uint8_t addressType; @@ -352,14 +363,14 @@ void NimBLEAdvertising::start() { rc = ble_hs_id_infer_auto(0, &addressType); if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGC(LOG_TAG, "Error determining address type; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); abort(); } rc = ble_gap_adv_start(addressType, NULL, BLE_HS_FOREVER, &m_advParams, NimBLEServer::handleGapEvent, NimBLEDevice::createServer()); //get a reference to the server (does not create a new one) if (rc != 0) { - NIMBLE_LOGE(LOG_TAG, "error enabling advertisement; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); abort(); } @@ -383,11 +394,11 @@ void NimBLEAdvertising::stop() { NIMBLE_LOGD(LOG_TAG, "<< stop"); } // stop - +/* void NimBLEAdvertising::onHostReset() { // m_advSvcsSet = false; } - + */ /** * @brief Add data to the payload to be advertised. diff --git a/src/NimBLEAdvertising.h b/src/NimBLEAdvertising.h index 9a895fce..cfb8adc2 100644 --- a/src/NimBLEAdvertising.h +++ b/src/NimBLEAdvertising.h @@ -86,7 +86,7 @@ class NimBLEAdvertising { private: friend class NimBLEDevice; - void onHostReset(); + // void onHostReset(); ble_hs_adv_fields m_advData; ble_hs_adv_fields m_scanData; ble_gap_adv_params m_advParams; diff --git a/src/NimBLEClient.cpp b/src/NimBLEClient.cpp index 0b91798b..9422ef7a 100644 --- a/src/NimBLEClient.cpp +++ b/src/NimBLEClient.cpp @@ -92,30 +92,13 @@ void NimBLEClient::clearServices() { /** - * @brief If the host was reset try to gracefully recover and ensure all semaphores are unblocked + * NOT NEEDED */ + /* void NimBLEClient::onHostReset() { - - // Dont think this is necessary - /* - m_isConnected = false; // make sure we change connected status before releasing semaphores - m_waitingToConnect = false; - - m_semaphoreOpenEvt.give(1); - m_semaphoreSearchCmplEvt.give(1); - m_semeaphoreSecEvt.give(1); - for (auto &sPair : m_servicesMap) { - sPair.second->releaseSemaphores(); - } - //m_conn_id = BLE_HS_CONN_HANDLE_NONE; // old handle will be invalid, clear it just incase - - // tell the user we disconnected - if (m_pClientCallbacks != nullptr) { - m_pClientCallbacks->onDisconnect(this); - } - */ + } - + */ /** * Add overloaded function to ease connect to peer device with not public address @@ -133,10 +116,15 @@ bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) * @return True on success. */ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServices) { - NIMBLE_LOGE(LOG_TAG, ">> connect(%s)", address.toString().c_str()); + NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str()); if(!NimBLEDevice::m_synced) { - NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); + return false; + } + + if(ble_gap_conn_active()) { + NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait."); return false; } @@ -163,7 +151,7 @@ bool NimBLEClient::connect(NimBLEAddress address, uint8_t type, bool refreshServ "addr=%s, rc=%d; %s", type, m_peerAddress.toString().c_str(), - rc, NimBLEUtils::returnCodeToString(rc)); + rc, NimBLEUtils::returnCodeToString(BLE_HS_ATT_ERR(rc))); m_semaphoreOpenEvt.give(); m_waitingToConnect = false; @@ -595,14 +583,14 @@ uint16_t NimBLEClient::getMTU() { //MODLOG_DFLT(INFO, "\n"); - // if Host reset tell the device now before returning to prevent + // If Host reset tell the device now before returning to prevent // any errors caused by calling host functions before resyncing. switch(event->disconnect.reason) { case BLE_HS_ETIMEOUT_HCI: case BLE_HS_EOS: case BLE_HS_ECONTROLLER: case BLE_HS_ENOTSYNCED: - NIMBLE_LOGE(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); + NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason); NimBLEDevice::onReset(event->disconnect.reason); break; default: @@ -734,7 +722,14 @@ uint16_t NimBLEClient::getMTU() { } // BLE_GAP_EVENT_CONN_UPDATE_REQ, BLE_GAP_EVENT_L2CAP_UPDATE_REQ case BLE_GAP_EVENT_CONN_UPDATE: { - NIMBLE_LOGD(LOG_TAG, "Connection parameters updated."); + if(client->m_conn_id != event->conn_update.conn_handle){ + return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE + } + if(event->conn_update.status == 0) { + NIMBLE_LOGI(LOG_TAG, "Connection parameters updated."); + } else { + NIMBLE_LOGE(LOG_TAG, "Update connection parameters failed."); + } return 0; } // BLE_GAP_EVENT_CONN_UPDATE @@ -743,14 +738,16 @@ uint16_t NimBLEClient::getMTU() { return 0; //BLE_HS_ENOTCONN BLE_ATT_ERR_INVALID_HANDLE } - struct ble_gap_conn_desc desc; - rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); - assert(rc == 0); - - if(NimBLEDevice::m_securityCallbacks != nullptr) { - NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); - } else { - client->m_pClientCallbacks->onAuthenticationComplete(&desc); + if(event->enc_change.status == 0) { + struct ble_gap_conn_desc desc; + rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); + assert(rc == 0); + + if(NimBLEDevice::m_securityCallbacks != nullptr) { + NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); + } else { + client->m_pClientCallbacks->onAuthenticationComplete(&desc); + } } client->m_semeaphoreSecEvt.give(event->enc_change.status); @@ -791,12 +788,7 @@ uint16_t NimBLEClient::getMTU() { } else { pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); } - /* }else if(client->m_pClientCallbacks != nullptr) { - pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); - }else{ - pkey.numcmp_accept = false; - } - */ + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); @@ -821,14 +813,7 @@ uint16_t NimBLEClient::getMTU() { } else { client->m_pClientCallbacks->onPassKeyRequest(); } - /* }else if(client->m_pClientCallbacks != nullptr) { - pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); - NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); - }else{ - pkey.passkey = 0; - NIMBLE_LOGE(LOG_TAG, "No Callback! Sending 0 as the passkey"); - } - */ + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); NIMBLE_LOGD(LOG_TAG, "ble_sm_inject_io result: %d", rc); diff --git a/src/NimBLEClient.h b/src/NimBLEClient.h index 6b41607a..b8219bcb 100644 --- a/src/NimBLEClient.h +++ b/src/NimBLEClient.h @@ -70,7 +70,7 @@ class NimBLEClient { static int serviceDiscoveredCB(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg); void clearServices(); // Clear any existing services. bool retrieveServices(); //Retrieve services from the server - void onHostReset(); +// void onHostReset(); NimBLEAddress m_peerAddress = NimBLEAddress("\0\0\0\0\0\0"); // The BD address of the remote server. uint16_t m_conn_id; diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index 3f40d1c9..ccb5ec70 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -116,6 +116,11 @@ void NimBLEDevice::stopAdvertising() { * @return A reference to the new client object. */ /* STATIC */ NimBLEClient* NimBLEDevice::createClient() { + if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) { + NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)", + NIMBLE_MAX_CONNECTIONS); + } + NimBLEClient* pClient = new NimBLEClient(); m_cList.push_back(pClient); @@ -167,8 +172,18 @@ void NimBLEDevice::stopAdvertising() { } // 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) { @@ -182,6 +197,35 @@ void NimBLEDevice::stopAdvertising() { } // 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. + */ +/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(NimBLEAddress peer_addr) { + 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 + + /** * @brief Set the transmission power. * The power level can be one of: @@ -272,23 +316,23 @@ void NimBLEDevice::stopAdvertising() { m_synced = false; - for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { - (*it)->onHostReset(); - } - if(m_pScan != nullptr) { m_pScan->onHostReset(); } - +/* Not needed if(m_pServer != nullptr) { m_pServer->onHostReset(); } + for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) { + (*it)->onHostReset(); + } + if(m_bleAdvertising != nullptr) { m_bleAdvertising->onHostReset(); } - - NIMBLE_LOGE(LOG_TAG, "Resetting state; reason=%d, %s", reason, +*/ + NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason, NimBLEUtils::returnCodeToString(reason)); } // onReset @@ -298,7 +342,12 @@ void NimBLEDevice::stopAdvertising() { */ /* STATIC */ void NimBLEDevice::onSync(void) { - NIMBLE_LOGE(LOG_TAG, "NimBle host synced."); + 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; + } /* Make sure we have proper identity address set (public preferred) */ int rc = ble_hs_util_ensure_addr(0); diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index 192ce899..a9b0e8bd 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -59,6 +59,7 @@ #define BLEEddystoneTLM NimBLEEddystoneTLM #define BLEEddystoneURL NimBLEEddystoneURL +#define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS /** * @brief BLE functions. @@ -99,6 +100,9 @@ class NimBLEDevice { static void startAdvertising(); static void stopAdvertising(); static NimBLEClient* getClientByID(uint16_t conn_id); + static NimBLEClient* getClientByPeerAddress(NimBLEAddress peer_addr); + static NimBLEClient* getDisconnectedClient(); + static size_t getClientListSize(); static std::list* getClientList(); private: @@ -106,8 +110,6 @@ class NimBLEDevice { friend class NimBLEClient; friend class NimBLEScan; friend class NimBLEAdvertising; -// friend class NimBLERemoteService; -// friend class NimBLERemoteCharacteristic; static void onReset(int reason); static void onSync(void); diff --git a/src/NimBLEScan.cpp b/src/NimBLEScan.cpp index f3d36b9a..634a953a 100644 --- a/src/NimBLEScan.cpp +++ b/src/NimBLEScan.cpp @@ -223,17 +223,17 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) { * @return True if scan started or false if there was an error. */ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) { - NIMBLE_LOGE(LOG_TAG, ">> start(duration=%d)", duration); + NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration); // If Host is not synced we cannot start scanning. if(!NimBLEDevice::m_synced) { - NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync."); + NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync."); return false; } // If we are already scanning don't start again or we will get stuck on the semaphore. if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset. - NIMBLE_LOGW(LOG_TAG, "Scan already in progress"); + NIMBLE_LOGE(LOG_TAG, "Scan already in progress"); return false; } @@ -260,12 +260,18 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul clearResults(); } - int rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, NimBLEScan::handleGapEvent, this); + int rc = 0; + + do{ + rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params, + NimBLEScan::handleGapEvent, this); + }while(rc == BLE_HS_EBUSY); + if (rc != 0) { NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc)); + m_stopped = true; m_semaphoreScanEnd.give(); - m_stopped = true; return false; } @@ -303,12 +309,13 @@ void NimBLEScan::stop() { } m_stopped = true; - m_semaphoreScanEnd.give(); if (m_scanCompleteCB != nullptr) { m_scanCompleteCB(m_scanResults); } - + + m_semaphoreScanEnd.give(); + NIMBLE_LOGD(LOG_TAG, "<< stop()"); } // stop diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index 66df591e..fba83230 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -223,7 +223,7 @@ uint32_t NimBLEServer::getConnectedCount() { */ /*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { NimBLEServer* server = (NimBLEServer*)arg; - NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", + NIMBLE_LOGE(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); int rc = 0; @@ -232,19 +232,20 @@ uint32_t NimBLEServer::getConnectedCount() { case BLE_GAP_EVENT_CONNECT: { if (event->connect.status != 0) { /* Connection failed; resume advertising */ + NIMBLE_LOGC(LOG_TAG, "Connection failed"); NimBLEDevice::startAdvertising(); server->m_connId = BLE_HS_CONN_HANDLE_NONE; } else { server->m_connId = event->connect.conn_handle; server->addPeerDevice((void*)server, false, server->m_connId); - //if (server->m_pServerCallbacks != nullptr) { + ble_gap_conn_desc desc; rc = ble_gap_conn_find(event->connect.conn_handle, &desc); assert(rc == 0); + server->m_pServerCallbacks->onConnect(server); server->m_pServerCallbacks->onConnect(server, &desc); - //} } return 0; @@ -252,13 +253,24 @@ uint32_t NimBLEServer::getConnectedCount() { case BLE_GAP_EVENT_DISCONNECT: { - server->m_connId = BLE_HS_CONN_HANDLE_NONE; - //if (server->m_pServerCallbacks != nullptr) { - server->m_pServerCallbacks->onDisconnect(server); - //} - /* Connection terminated; resume advertising */ - //NimBLEDevice::startAdvertising(); + // If Host reset tell the device now before returning to prevent + // any errors caused by calling host functions before resyncing. + 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->removePeerDevice(event->disconnect.conn.conn_handle, false); + server->m_connId = BLE_HS_CONN_HANDLE_NONE; + server->m_pServerCallbacks->onDisconnect(server); + return 0; } // BLE_GAP_EVENT_DISCONNECT @@ -341,12 +353,7 @@ uint32_t NimBLEServer::getConnectedCount() { } else { pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); } - /* }else if(server->m_pServerCallbacks != nullptr) { - pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); - }else{ - pkey.numcmp_accept = false; - } - */ + 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); @@ -371,14 +378,7 @@ uint32_t NimBLEServer::getConnectedCount() { } else { pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); } - /* }else if(server->m_pServerCallbacks != nullptr) { - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); - NIMBLE_LOGD(LOG_TAG, "Sending passkey: %d", pkey.passkey); - }else{ - pkey.passkey = 0; - NIMBLE_LOGE(LOG_TAG, "No Callback! Sending 0 as the passkey"); - } - */ + 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); @@ -579,12 +579,13 @@ void NimBLEServer::updateConnParams(uint16_t conn_handle, } } +/* Don't think this is needed void NimBLEServer::onHostReset() { - /* for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) { + for(auto it = m_notifyChrMap.cbegin(); it != m_notifyChrMap.cend(); ++it) { (*it).second->m_semaphoreConfEvt.give(0); } - */ + } - +*/ #endif // CONFIG_BT_ENABLED \ No newline at end of file diff --git a/src/NimBLEServer.h b/src/NimBLEServer.h index 1bbf73a0..436f2036 100644 --- a/src/NimBLEServer.h +++ b/src/NimBLEServer.h @@ -102,7 +102,7 @@ class NimBLEServer { friend class NimBLECharacteristic; friend class NimBLEDevice; friend class NimBLEAdvertising; - void onHostReset(); + // void onHostReset(); // BLEAdvertising m_bleAdvertising; uint16_t m_connId; uint16_t m_svcChgChrHdl; From 2e8bb7de3e26762fdcb410d5dc4ea3f5ace031a9 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sat, 28 Mar 2020 21:43:26 -0600 Subject: [PATCH 59/62] Cleanup examples / logging. --- examples/NimBLE_Client/NimBLE_Client.ino | 16 +++---- examples/NimBLE_Server/NimBLE_Server.ino | 61 ++++++++++++------------ src/NimBLEServer.cpp | 2 +- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index b889a0cd..ef11752c 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -87,15 +87,13 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks { /** Notification / Indication receiving handler callback */ void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ - Serial.print((isNotify == true) ? "Notify" : "Indication"); - Serial.print(" from "); - Serial.print(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString().c_str()); - Serial.print(": Service = "); - Serial.print(pRemoteCharacteristic->getRemoteService()->getUUID().toString().c_str()); - Serial.print(", Characteristic = "); - Serial.print(pRemoteCharacteristic->getUUID().toString().c_str()); - Serial.print(", Value = "); - Serial.println(std::string((char*)pData, length).c_str()); + std::string str = (isNotify == true) ? "Notification" : "Indication"; + str += " from "; + str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString(); + str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString(); + str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString(); + str += ", Value = " + std::string((char*)pData, length); + Serial.println(str.c_str()); } /** Callback to process the results of the last scan or restart it */ diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino index a30ce8b1..ec78da26 100644 --- a/examples/NimBLE_Server/NimBLE_Server.ino +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -84,23 +84,24 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks { Serial.println(pCharacteristic->getValue().c_str()); }; /** Called before notification or indication is sent, - * the value can be changed here before sending if desired. - */ + * the value can be changed here before sending if desired. + */ void onNotify(NimBLECharacteristic* pCharacteristic) { Serial.println("Sending notification to clients"); }; - /** The status returned in s is defined in NimBLECharacteristic.h. - * The value returned in code is the NimBLE host return code. - */ - void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) { - Serial.print("Notification/Indication status code: "); - Serial.print(s); - Serial.print(", return code: "); - Serial.print(code); - Serial.print(", "); - Serial.println(NimBLEUtils::returnCodeToString(code)); + /** The status returned in status is defined in NimBLECharacteristic.h. + * The value returned in code is the NimBLE host return code. + */ + void onStatus(NimBLECharacteristic* pCharacteristic, Status status, int code) { + String str = ("Notification/Indication status code: "); + str += status; + str += ", return code: "; + str += code; + str += ", "; + str += NimBLEUtils::returnCodeToString(code); + Serial.println(str); }; }; @@ -142,18 +143,18 @@ void setup() { NimBLEDevice::init("NimBLE-Arduino"); /** Set the IO capabilities of the device, each option will trigger a different pairing method. - * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing - * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing - * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing - */ + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey //NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison /** 2 different ways to set security - both calls achieve the same result. - * no bonding, no man in the middle protection, secure connections. - * - * These are the default values, only shown here for demonstration. - */ + * no bonding, no man in the middle protection, secure connections. + * + * These are the default values, only shown here for demonstration. + */ //NimBLEDevice::setSecuityAuth(false, false, true); NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); @@ -174,10 +175,10 @@ void setup() { pBeefCharacteristic->setCallbacks(&chrCallbacks); /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with - * either of those uuid's it will create the associated class with the correct properties - * and sizes. However we must cast the returned reference to the correct type as the method - * only returns a pointer to the base NimBLEDescriptor class. - */ + * either of those uuid's it will create the associated class with the correct properties + * and sizes. However we must cast the returned reference to the correct type as the method + * only returns a pointer to the base NimBLEDescriptor class. + */ NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904"); pBeef2904->setFormat(NimBLE2904::FORMAT_UTF8); pBeef2904->setCallbacks(&dscCallbacks); @@ -206,10 +207,10 @@ void setup() { pC01Ddsc->setCallbacks(&dscCallbacks); /** Note a 2902 descriptor does NOT need to be created as any chactateristic with - * notification or indication properties will have one created autmatically. - * Manually creating it is only useful if you wish to handle callback functions - * as shown here. Otherwise this can be removed without loss of functionality. - */ + * notification or indication properties will have one created autmatically. + * Manually creating it is only useful if you wish to handle callback functions + * as shown here. Otherwise this can be removed without loss of functionality. + */ NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902"); pFood2902->setCallbacks(&dscCallbacks); @@ -222,8 +223,8 @@ void setup() { pAdvertising->addServiceUUID(pDeadService->getUUID()); pAdvertising->addServiceUUID(pBaadService->getUUID()); /** If your device is battery powered you may consider setting scan response - * to false as it will extend battery life at the expense of less data sent. - */ + * to false as it will extend battery life at the expense of less data sent. + */ pAdvertising->setScanResponse(true); pAdvertising->start(); diff --git a/src/NimBLEServer.cpp b/src/NimBLEServer.cpp index fba83230..3978daec 100644 --- a/src/NimBLEServer.cpp +++ b/src/NimBLEServer.cpp @@ -223,7 +223,7 @@ uint32_t NimBLEServer::getConnectedCount() { */ /*STATIC*/int NimBLEServer::handleGapEvent(struct ble_gap_event *event, void *arg) { NimBLEServer* server = (NimBLEServer*)arg; - NIMBLE_LOGE(LOG_TAG, ">> handleGapEvent: %s", + NIMBLE_LOGD(LOG_TAG, ">> handleGapEvent: %s", NimBLEUtils::gapEventToString(event->type)); int rc = 0; From 640c571ce3abbee30563b5c58d857e25f59af883 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 29 Mar 2020 10:27:21 -0600 Subject: [PATCH 60/62] Update readme, update/include licensing files. --- LICENSE | 24 +++- README.md | 72 ++++++++--- src/CODING_STANDARDS.md | 267 ++++++++++++++++++++++++++++++++++++++++ src/NOTICE | 8 ++ src/README.md | 173 ++++++++++++++++++++++++++ src/RELEASE_NOTES.md | 27 ++++ 6 files changed, 552 insertions(+), 19 deletions(-) create mode 100644 src/CODING_STANDARDS.md create mode 100644 src/NOTICE create mode 100644 src/README.md create mode 100644 src/RELEASE_NOTES.md diff --git a/LICENSE b/LICENSE index 261eeb9e..0ca98522 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -178,7 +178,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -199,3 +199,21 @@ 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. + +This product bundles queue.h 8.5, which is available under the "3-clause BSD" +license. For details, see porting/nimble/include/os/queue.h + +This product partly derives from FreeBSD, which is available under the +"3-clause BSD" license. For details, see: + * porting/nimble/src/os_mbuf.c + +This product bundles Gary S. Brown's CRC32 implementation, which is available +under the following license: + COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + +This product bundles tinycrypt, which is available under the "3-clause BSD" +license. For details, and bundled files see: + * ext/tinycrypt/LICENSE + +This product partly derives from esp32-snippets; Copyright 2017 Neil Kolban. \ No newline at end of file diff --git a/README.md b/README.md index 29229116..f3caa1ee 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,80 @@ # *** UPDATE *** -Server code nearing readiness, checkout the ServerDev branch if you would like to assist with testing. - -Client code working with all functionality. +This library is now ready with (mostly)all original BLE library compatiblity. + +3 simultaneous connections tested stable so far on both client and server. # **************** # NimBLE-Arduino -A fork of the NimBLE library structured for compilation with Ardruino, designed for use with ESP32. +A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32. + +Why? Because the Bluedroid library is too bulky. + +Initial client code testing has resulted in code size reduction of ~115k and reduced ram consumption of ~37k. + +Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): -Why? Because the Bluedroid library is too bulky, In testing I have found an initial code size reduction of ~115k and reduced ram usage by ~37k. +### Memory usage (compilation output) +#### Arduino BLE library +```log +RAM: [== ] 17.7% (used 58156 bytes from 327680 bytes) +Flash: [======== ] 76.0% (used 1345630 bytes from 1769472 bytes) +``` +#### NimBLE-Arduino library +```log +RAM: [= ] 14.5% (used 47476 bytes from 327680 bytes) +Flash: [======= ] 69.5% (used 911378 bytes from 1310720 bytes) +``` +### Memory usage after **`setup()`** function +#### Arduino BLE library +**`Internal Total heap 259104, internal Free Heap 91660`** +#### NimBLE-Arduino library +**`Internal Total heap 290288, internal Free Heap 182344`** + # Installation: -Download .zip -Extract to Arduino/libraries folder or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. +Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. + +`#include "NimBLEDevice.h"` at the beginning of your sketch. + +Tested and working with esp32-arduino v1.0.2 and 1.0.4 in Arduino IDE v1.8.12 and platform IO. + + +# Usage: + +This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes. + +Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library. + +More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client. + +Beacon examples provided by @beegee-tokyo are in examples/ BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon. + +Change the settings in the `nimconfig.h` file to customize NimBLE to your project, such as increasing max connections, default is 3. -# Use: +# Continuing development: -This library is intended to be compatible with the current BLE library classes, functions and types with minor changes. +This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@c4af628.](https://github.com/espressif/esp-nimble) -At this time only the client code has been (nearly) fully implemented and work has started on the server code. +Also tracking the NimBLE related changes in esp-idf, master branch, currently [@48bd2d7.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble) -# Features: +# Acknowledgments: -Multiple clients are supported, up to 3 presently. +* @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from. +* @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples. # Todo: -1. Complete server implementation. -2. Code cleanup. -3. Create documentation. -4. Examples. +1. Code cleanup. +2. Create documentation. +3. Examples. +4. Add BLE Mesh code. +5. Expose more NimBLE features. diff --git a/src/CODING_STANDARDS.md b/src/CODING_STANDARDS.md new file mode 100644 index 00000000..d14b9fdb --- /dev/null +++ b/src/CODING_STANDARDS.md @@ -0,0 +1,267 @@ +# Coding Style for Apache NimBLE + +Apache NimBLE project is part of Apache Mynewt projct and follows its coding +style. + +# Coding Style for Apache Mynewt Core + +This document is meant to define the coding style for Apache Mynewt, and +all subprojects of Apache Mynewt. This covers C and Assembly coding +conventions, *only*. Other languages (such as Go), have their own +coding conventions. + +## Headers + +* All files that are newly written, should have the Apache License clause +at the top of them. + +* For files that are copied from another source, but contain an Apache +compatible license, the original license header shall be maintained. + +* For more information on applying the Apache license, the definitive +source is here: http://www.apache.org/dev/apply-license.html + +* The Apache License clause for the top of files is as follows: + +```no-highlight +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +``` + +## Whitespace and Braces + +* Code must be indented to 4 spaces, tabs should not be used. + +* Do not add whitespace at the end of a line. + +* Put space after keywords (for, if, return, switch, while). + +* for, else, if, while statements must have braces around their +code blocks, i.e., do: + +``` + if (x) { + assert(0); + } else { + assert(0); + } +``` + +Not: + +``` + if (x) + assert(0); + else + assert(0); +``` + +* Braces for statements must be on the same line as the statement. Good: + +``` + for (i = 0; i < 10; i++) { + if (i == 5) { + break; + } else { + continue; + } + } +``` + +Not: + +``` + for (i = 0; i < 10; i++) + { <-- brace must be on same line as for + if (i == 5) { + break; + } <-- no new line between else + else { + continue; + } + } +``` + +* After a function declaration, the braces should be on a newline, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +not: + +``` + static void * + function(int var1, int var2) { +``` + +## Line Length and Wrap + +* Line length should never exceed 79 columns. + +* When you have to wrap a long statement, put the operator at the end of the + line. i.e.: + +``` + if (x && + y == 10 && + b) +``` + +Not: + +``` + if (x + && y == 10 + && b) +``` + +## Comments + +* No C++ style comments allowed. + +* When using a single line comment, put it above the line of code that you +intend to comment, i.e., do: + +``` + /* check variable */ + if (a) { +``` + +Not: + +``` + if (a) { /* check variable */ +``` + + +* All public APIs should be commented with Doxygen style comments describing +purpose, parameters and return values. Private APIs need not be documented. + + +## Header files + +* Header files must contain the following structure: + * Apache License (see above) + * ```#ifdef``` aliasing, to prevent multiple includes + * ```#include``` directives for other required header files + * ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs + * Contents of the header file + +* ```#ifdef``` aliasing, shall be in the following format, where +the package name is "os" and the file name is "callout.h": + +```no-highlight +#ifndef _OS_CALLOUT_H +#define _OS_CALLOUT_H +``` + +* ```#include``` directives must happen prior to the cplusplus +wrapper. + +* The cplusplus wrapper must have the following format, and precedes +any contents of the header file: + +```no-highlight +#ifdef __cplusplus +#extern "C" { +##endif +``` + +## Naming + +* Names of functions, structures and variables must be in all lowercase. + +* Names should be as short as possible, but no shorter. + +* Globally visible names must be prefixed with the name of the module, +followed by the '_' character, i.e.: + +``` + os_callout_init(&c) +``` + +Not: + +``` + callout_init(c) +``` + +## Functions + +* No spaces after function names when calling a function, i.e, do: + +``` + rc = function(a) +``` + +Not: + +``` + rc = function (a) +``` + + +* Arguments to function calls should have spaces between the comma, i.e. do: + +``` + rc = function(a, b) +``` + +Not: + +``` + rc = function(a,b) +``` + +* The function type must be on a line by itself preceding the function, i.e. do: + +``` + static void * + function(int var1, int var2) + { +``` + +Not: + +``` + static void *function(int var1, int var2) + { +``` + +* In general, for functions that return values that denote success or error, 0 +shall be success, and non-zero shall be the failure code. + +## Variables and Macros + +* Do not use typedefs for structures. This makes it impossible for +applications to use pointers to those structures opaquely. + +* typedef may be used for non-structure types, where it is beneficial to +hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate +typedefs by applying the ```_t``` marker to them. + +* Place all function-local variable definitions at the top of the function body, before any statements. + +## Compiler Directives + +* Code must compile cleanly with -Wall enabled. + diff --git a/src/NOTICE b/src/NOTICE new file mode 100644 index 00000000..fc24c6ab --- /dev/null +++ b/src/NOTICE @@ -0,0 +1,8 @@ +Apache Mynewt NimBLE +Copyright 2015-2018 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at +Runtime Inc, copyright 2015. diff --git a/src/README.md b/src/README.md new file mode 100644 index 00000000..bbd17fc8 --- /dev/null +++ b/src/README.md @@ -0,0 +1,173 @@ + + +Apache Mynewt + +## Overview + +Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) +that completely replaces the proprietary SoftDevice on Nordic chipsets. It is +part of [Apache Mynewt project](https://github.com/apache/mynewt-core). + +Features highlight: + - Support for 251 byte packet size + - Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central + - Support for up to 32 simultaneous connections. + - Legacy and SC (secure connections) SMP support (pairing and bonding). + - Advertising Extensions. + - Periodic Advertising. + - Coded (aka Long Range) and 2M PHYs. + - Bluetooth Mesh. + +## Supported hardware + +Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board +and architecture [supported](https://github.com/apache/mynewt-core#overview) +by Apache Mynewt OS. + + +## Browsing + +If you are browsing around the source tree, and want to see some of the +major functional chunks, here are a few pointers: + +- nimble/controller: Contains code for controller including Link Layer and HCI implementation +([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller)) + +- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52) +([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers)) + +- nimble/host: Contains code for host subsystem. This includes protocols like +L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP), +Generic Attribute Profile (GATT) and Security Manager (SM). +([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host)) + +- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem. +([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh)) + +- nimble/transport: Contains code for supported transport protocols between host +and controller. This includes UART, emSPI and RAM (used in combined build when +host and controller run on same CPU) +([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport)) + +- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported +operating systems +([porting](https://github.com/apache/mynewt-nimble/tree/master/porting)) + +- ext: Contains external libraries used by NimBLE. Those are used if not +provided by OS +([ext](https://github.com/apache/mynewt-nimble/tree/master/ext)) + +- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os)) + +## Sample Applications + +There are also some sample applications that show how to Apache Mynewt NimBLE +stack. These sample applications are located in the `apps/` directory of +Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples: + +* [blecent](https://github.com/apache/mynewt-core/tree/master/apps/blecent): +A basic central device with no user interface. This application scans for +a peripheral that supports the alert notification service (ANS). Upon +discovering such a peripheral, blecent connects and performs a characteristic +read, characteristic write, and notification subscription. +* [blehci](https://github.com/apache/mynewt-core/tree/master/apps/blehci): +Implements a BLE controller-only application. A separate host-only +implementation, such as Linux's BlueZ, can interface with this application via +HCI over UART. +* [bleprph](https://github.com/apache/mynewt-core/tree/master/apps/bleprph): An + implementation of a minimal BLE peripheral. +* [btshell](https://github.com/apache/mynewt-core/tree/master/apps/btshell): A + shell-like application allowing to configure and use most of NimBLE + functionality from command line. +* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart): +Implements a simple BLE peripheral that supports the Nordic +UART / Serial Port Emulation service +(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html). +* [test](https://github.com/apache/mynewt-core/tree/master/apps/test): Test + project which can be compiled either with the simulator, or on a per-architecture basis. + Test will run all the package's unit tests. + +# Getting Help + +If you are having trouble using or contributing to Apache Mynewt NimBLE, or just +want to talk to a human about what you're working on, you can contact us via the +[developers mailing list](mailto:dev@mynewt.apache.org). + +Although not a formal channel, you can also find a number of core developers +on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://join.slack.com/mynewt/shared_invite/MTkwMTg1ODM1NTg5LTE0OTYxNzQ4NzQtZTU1YmNhYjhkMg) + +Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers) +for some help troubleshooting first. + +# Contributing + +Anybody who works with Apache Mynewt can be a contributing member of the +community that develops and deploys it. The process of releasing an operating +system for microcontrollers is never done: and we welcome your contributions +to that effort. + +More information can be found at the Community section of the Apache Mynewt +website, located [here](https://mynewt.apache.org/community). + +## Pull Requests + +Apache Mynewt welcomes pull request via Github. Discussions are done on Github, +but depending on the topic, can also be relayed to the official Apache Mynewt +developer mailing list dev@mynewt.apache.org. + +If you are suggesting a new feature, please email the developer list directly, +with a description of the feature you are planning to work on. + +## Filing Bugs + +Bugs can be filed on the +[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Bug". + +Where possible, please include a self-contained reproduction case! + +## Feature Requests + +Feature requests should also be filed on the +[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues). +Please label the issue as a "Feature" or "Enhancement" depending on the scope. + +## Writing Tests + +We love getting newt tests! Apache Mynewt is a huge undertaking, and improving +code coverage is a win for every Apache Mynewt user. + + + +# License + +The code in this repository is all under either the Apache 2 license, or a +license compatible with the Apache 2 license. See the LICENSE file for more +information. diff --git a/src/RELEASE_NOTES.md b/src/RELEASE_NOTES.md new file mode 100644 index 00000000..cda8fe2e --- /dev/null +++ b/src/RELEASE_NOTES.md @@ -0,0 +1,27 @@ +# RELEASE NOTES + +16 July 2019 - Apache NimBLE v1.2.0 + +For full release notes, please visit the +[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes). + +Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) that completely +replaces the proprietary SoftDevice on Nordic chipsets. + +New features in this version of NimBLE include: + +* Perdiodic Advertising support with up to 1650 bytes of data (scanner and advertiser) +* Support for scan request notification in GAP API +* Updated host qualification ID +* Qualification related bugfixes +* GAP API doxygen documentation update +* BLE Mesh improvements - fixes and resync with latest Zephyr code +* RIOT OS port fixes and improvements +* btshell sample application improvements +* improvements for bttester application +* Controller duplicates filtering improvements +* Memory and CPU usage optimizations in controller + +If working on next-generation RTOS and Bluetooth protocol stack +sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt +Developer's list, dev@mynewt.apache.org. From ab1686f2e1d53461d057952241e93c6b0258a391 Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 29 Mar 2020 14:20:28 -0600 Subject: [PATCH 61/62] Add missing license documents. Fix typos. Add API_DIFFERENCES reference document. --- API_DIFFERENCES.md | 207 ++++++++++ README.md | 12 +- examples/NimBLE_Client/NimBLE_Client.ino | 4 +- examples/NimBLE_Server/NimBLE_Server.ino | 4 +- .../BLE_client/BLE_client.ino | 2 +- src/NimBLEDevice.cpp | 14 +- src/NimBLEDevice.h | 4 +- src/NimBLESecurity.cpp | 2 +- src/NimBLESecurity.h | 5 +- src/tinycrypt/AUTHORS | 15 + src/tinycrypt/LICENSE | 61 +++ src/tinycrypt/README | 71 ++++ src/tinycrypt/VERSION | 1 + src/tinycrypt/documentation/tinycrypt.rst | 352 ++++++++++++++++++ 14 files changed, 730 insertions(+), 24 deletions(-) create mode 100644 API_DIFFERENCES.md create mode 100644 src/tinycrypt/AUTHORS create mode 100644 src/tinycrypt/LICENSE create mode 100644 src/tinycrypt/README create mode 100644 src/tinycrypt/VERSION create mode 100644 src/tinycrypt/documentation/tinycrypt.rst diff --git a/API_DIFFERENCES.md b/API_DIFFERENCES.md new file mode 100644 index 00000000..f47f8d0f --- /dev/null +++ b/API_DIFFERENCES.md @@ -0,0 +1,207 @@ +# Server API differnces: + +### Characteristics: +When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`. + +#### Previous: +``` +BLECharacteristic::PROPERTY_READ | +BLECharacteristic::PROPERTY_WRITE +``` + +#### Changed to: +``` +NIMBLE_PROPERTY::READ | +NIMBLE_PROPERTY::WRITE +``` + +#### The full list of properties: +``` +NIMBLE_PROPERTY::READ +NIMBLE_PROPERTY::READ_ENC +NIMBLE_PROPERTY::READ_AUTHEN +NIMBLE_PROPERTY::READ_AUTHOR +NIMBLE_PROPERTY::WRITE +NIMBLE_PROPERTY::WRITE_NR +NIMBLE_PROPERTY::WRITE_ENC +NIMBLE_PROPERTY::WRITE_AUTHEN +NIMBLE_PROPERTY::WRITE_AUTHOR +NIMBLE_PROPERTY::BROADCAST +NIMBLE_PROPERTY::NOTIFY +NIMBLE_PROPERTY::INDICATE +``` + +### Descriptors: +Descriptors are now created using the NimBLEcharacteristic method `createDescriptor()`. + +The previous method `addDescriptor()` is now a private function in the library. + +This was done because the NimBLE host automatically creates a 0x2902 descriptor if a characteristic has notify or indicate properties applied. +Due to this fact, this library also creates one automatically for your application. +The only reason to manually create this descriptor now is to assign callback functions. +If you do not require this functionality you can safely exclude the manual creation of that descriptor. + + +For any other descriptor, (except 0x2904, see below) it should now be created just as characteristics are +by invoking the `NimBLECharacteristic::createDescriptor` methods. +Which are defined as: +``` +NimBLEDescriptor* createDescriptor(const char* uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); + +NimBLEDescriptor* createDescriptor(NimBLEUUID uuid, + uint32_t properties = NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE, + uint16_t max_len = 100); +``` +##### Example: +``` +pDescriptor = pCharacteristic->createDescriptor("ABCD", + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::WRITE_ENC, + 25);` +``` +Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes. + +For the 0x2904 descriptor, there is a special class that is created when you call `createDescriptor("2904")`. + +The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to `NimBLE2904*` to access the specific class methods. + +##### Example: +``` +p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904"); +``` + +#### Server Security: +Security is set on the characteristic or descriptor properties by applying one of the following: +``` +NIMBLE_PROPERTY::READ_ENC +NIMBLE_PROPERTY::READ_AUTHEN +NIMBLE_PROPERTY::READ_AUTHOR +NIMBLE_PROPERTY::WRITE_ENC +NIMBLE_PROPERTY::WRITE_AUTHEN +NIMBLE_PROPERTY::WRITE_AUTHOR +``` +When a peer wants to read or write a characteristic or descriptor with any of these properties applied +it will trigger the pairing process. By default the "just-works" pairing will be performed automatically. +This can be changed to use passkey authentication or numeric confirmation. See below for details. + + +# Client API Differences: +The `BLEAdvertisedDeviceCallbacks` class `onResult()` method now receives a pointer to the +`NimBLEAdvertisedDevice` object instead of a copy. + +`NimBLEClient::connect()` now takes an extra parameter to indicate if the client should download the services + database from the peripheral, default value is true. + +Defined as: +``` +bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true); +bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_TYPE_PUBLIC, bool refreshServices = true); +``` +If set to false the client will use the services database it retrieved from the peripheral last time it connected. +This allows for faster connections and power saving if the devices just dropped connection and want to reconnect. + +``` +NimBLERemoteCharacteristic::writeValue(); +NimBLERemoteCharacteristic::registerForNotify(); +``` +Now return true or false to indicate success or failure so you can choose to disconnect or try again. + +#### Client Security: +The client will automatically initiate security when the peripheral responds that it's required. +The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below. + + +# Security: +Security callback functions are now incorporated in the client/server Callbacks class. +However backward compatibility with the `BLESecurity` class is retained to minimize app code changes. + +The relevent server callbacks are defined as: +``` +bool onConfirmPIN(uint32_t pin); // accept or reject the passkey +void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc +bool onPassKeyNotify(uint32_t pass_key); // receive the passkey sent by the client, accept or reject +``` +The relevent client callbacks are defined as: +``` +bool onConfirmPIN(uint32_t pin); // accept or reject the passkey +void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc +uint32_t onPassKeyRequest(); // return the passkey to send to the server +``` + +Security settings and IO capabilities are now set by the corresponding method of `NimBLEDevice::`. +``` +static void setSecurityAuth(bool bonding, bool mitm, bool sc); +static void setSecurityAuth(uint8_t auth_req); +static void setSecurityIOCap(uint8_t iocap); +static void setSecurityInitKey(uint8_t init_key); +static void setSecurityRespKey(uint8_t init_key); + + +/** + * @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. + */ +void NimBLEDevice::setSecuityAuth(bool bonding, bool mitm, bool sc) + + + +/** + * @brief Set the authorization mode for this device. + * @param A bitmap indicating what modes are supported. + * The bits are defined as follows: + ** 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. + ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only. + */ +void NimBLEDevice::setSecuityAuth(uint8_t auth_req) + + + +/** + * @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 + ** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability + ** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability + ** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability + */ +void NimBLEDevice::setSecurityIOCap(uint8_t iocap) + + + +/** + * @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. + * The bits are defined as follows: + ** 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 + */ +void NimBLEDevice::setSecurityInitKey(uint8_t init_key) + + +/** + * @brief Set the keys we are willing to accept during pairing. + * @param A bitmap indicating which keys to accept during pairing. + * The bits are defined as follows: + ** 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 + */ +void NimBLEDevice::setSecurityRespKey(uint8_t init_key) +``` + + I'm sure there are more things I have forgotten but this is all the majors. + I will update this document as necessary. \ No newline at end of file diff --git a/README.md b/README.md index f3caa1ee..8a6671f1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # *** UPDATE *** -This library is now ready with (mostly)all original BLE library compatiblity. +This library is now ready with (mostly)all original Arduino BLE library compatiblity. +Check the examples and API_DIFFERENCES document for details of using this library. 3 simultaneous connections tested stable so far on both client and server. -# **************** - # NimBLE-Arduino A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32. @@ -16,6 +15,7 @@ Initial client code testing has resulted in code size reduction of ~115k and red Server code testing results from @beegee-toyo [from the project here](https://github.com/beegee-tokyo/ESP32WiFiBLE-NimBLE): + ### Memory usage (compilation output) #### Arduino BLE library ```log @@ -33,8 +33,7 @@ Flash: [======= ] 69.5% (used 911378 bytes from 1310720 bytes) #### NimBLE-Arduino library **`Internal Total heap 290288, internal Free Heap 182344`** - - + # Installation: Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library. @@ -74,7 +73,6 @@ Also tracking the NimBLE related changes in esp-idf, master branch, currently [@ 1. Code cleanup. 2. Create documentation. -3. Examples. +3. Expose more NimBLE features. 4. Add BLE Mesh code. -5. Expose more NimBLE features. diff --git a/examples/NimBLE_Client/NimBLE_Client.ino b/examples/NimBLE_Client/NimBLE_Client.ino index ef11752c..af39a094 100644 --- a/examples/NimBLE_Client/NimBLE_Client.ino +++ b/examples/NimBLE_Client/NimBLE_Client.ino @@ -316,8 +316,8 @@ void setup (){ * * These are the default values, only shown here for demonstration. */ - //NimBLEDevice::setSecuityAuth(false, false, true); - NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); /** Optional: set the transmit power, default is -3db */ NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** 12db */ diff --git a/examples/NimBLE_Server/NimBLE_Server.ino b/examples/NimBLE_Server/NimBLE_Server.ino index ec78da26..be0abd8c 100644 --- a/examples/NimBLE_Server/NimBLE_Server.ino +++ b/examples/NimBLE_Server/NimBLE_Server.ino @@ -155,8 +155,8 @@ void setup() { * * These are the default values, only shown here for demonstration. */ - //NimBLEDevice::setSecuityAuth(false, false, true); - NimBLEDevice::setSecuityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + //NimBLEDevice::setSecurityAuth(false, false, true); + NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); pServer = NimBLEDevice::createServer(); pServer->setCallbacks(new ServerCallbacks()); diff --git a/examples/Refactored_original_examples/BLE_client/BLE_client.ino b/examples/Refactored_original_examples/BLE_client/BLE_client.ino index 6810c0a1..0d4ab94b 100644 --- a/examples/Refactored_original_examples/BLE_client/BLE_client.ino +++ b/examples/Refactored_original_examples/BLE_client/BLE_client.ino @@ -136,7 +136,7 @@ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { /******************************************************************* myDevice = new BLEAdvertisedDevice(advertisedDevice); *******************************************************************/ - myDevice = new BLEAdvertisedDevice(*advertisedDevice); /** dereference to copy data now **/ + myDevice = advertisedDevice; /** Just save the reference now, no need to copy the object */ doConnect = true; doScan = true; diff --git a/src/NimBLEDevice.cpp b/src/NimBLEDevice.cpp index ccb5ec70..84dce515 100644 --- a/src/NimBLEDevice.cpp +++ b/src/NimBLEDevice.cpp @@ -471,12 +471,12 @@ bool NimBLEDevice::getInitialized() { * @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::setSecuityAuth(bool bonding, bool mitm, bool sc) { +/*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; -} // setSecuityAuth +} // setSecurityAuth /** @@ -489,11 +489,11 @@ bool NimBLEDevice::getInitialized() { ** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported. ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only. */ -/*STATIC*/void NimBLEDevice::setSecuityAuth(uint8_t auth_req) { - NimBLEDevice::setSecuityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, +/*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); -} // setSecuityAuth +} // setSecurityAuth /** @@ -521,7 +521,7 @@ bool NimBLEDevice::getInitialized() { */ /*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) { ble_hs_cfg.sm_our_key_dist = init_key; -} // setsScurityInitKey +} // setsSecurityInitKey /** @@ -535,7 +535,7 @@ bool NimBLEDevice::getInitialized() { */ /*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) { ble_hs_cfg.sm_their_key_dist = init_key; -} // setsScurityRespKey +} // setsSecurityRespKey /** diff --git a/src/NimBLEDevice.h b/src/NimBLEDevice.h index a9b0e8bd..954e996e 100644 --- a/src/NimBLEDevice.h +++ b/src/NimBLEDevice.h @@ -83,8 +83,8 @@ class NimBLEDevice { static bool deleteClient(NimBLEClient* pClient); static void setPower(esp_power_level_t powerLevel); static void setCustomGapHandler(gap_event_handler handler); - static void setSecuityAuth(bool bonding, bool mitm, bool sc); - static void setSecuityAuth(uint8_t auth_req); + static void setSecurityAuth(bool bonding, bool mitm, bool sc); + static void setSecurityAuth(uint8_t auth_req); static void setSecurityIOCap(uint8_t iocap); static void setSecurityInitKey(uint8_t init_key); static void setSecurityRespKey(uint8_t init_key); diff --git a/src/NimBLESecurity.cpp b/src/NimBLESecurity.cpp index 8dc72966..0651858d 100644 --- a/src/NimBLESecurity.cpp +++ b/src/NimBLESecurity.cpp @@ -35,7 +35,7 @@ NimBLESecurity::~NimBLESecurity() { * @brief Set requested authentication mode */ void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) { - NimBLEDevice::setSecuityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0, + 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); } diff --git a/src/NimBLESecurity.h b/src/NimBLESecurity.h index 8a950b34..60e4f443 100644 --- a/src/NimBLESecurity.h +++ b/src/NimBLESecurity.h @@ -12,8 +12,9 @@ * Author: chegewara */ -// This class exists for backward compatibility - Should not be used in new code // -// See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient // +/** This class exists for backward compatibility - Should not be used in new code + * See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient + */ #ifndef COMPONENTS_NIMBLESECURITY_H_ #define COMPONENTS_NIMBLESECURITY_H_ diff --git a/src/tinycrypt/AUTHORS b/src/tinycrypt/AUTHORS new file mode 100644 index 00000000..0a8e9f80 --- /dev/null +++ b/src/tinycrypt/AUTHORS @@ -0,0 +1,15 @@ +Architect: +Rafael Misoczki + +Open Source Maintainer: +Constanza Heath +Rafael Misoczki + +Contributors: +Constanza Heath +Rafael Misoczki +Flavio Santes +Jarkko Sakkinen +Chris Morrison +Marti Bolivar +Colin Ian King diff --git a/src/tinycrypt/LICENSE b/src/tinycrypt/LICENSE new file mode 100644 index 00000000..2e1db516 --- /dev/null +++ b/src/tinycrypt/LICENSE @@ -0,0 +1,61 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +https://github.com/kmackay/micro-ecc + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ diff --git a/src/tinycrypt/README b/src/tinycrypt/README new file mode 100644 index 00000000..fb52c196 --- /dev/null +++ b/src/tinycrypt/README @@ -0,0 +1,71 @@ + +================================================================================ + + TinyCrypt Cryptographic Library + +================================================================================ + + Copyright (c) 2017, Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================================ + +Overview: + +The TinyCrypt Library provides an implementation for constrained devices of a +minimal set of standard cryptography primitives. + +Please, ***SEE THE DOCUMENTATION*** folder for more information on the supported +cryptographic primitives and the limitations of TinyCrypt library. For usage, +security and technicalities, please see the corresponding header file of each +cryptographic primitive. + +================================================================================ + +Organization: + +/lib: C source code of the cryptographic primitives. +/lib/include/tinycrypt: C header files of the cryptographic primitives. +/tests: Test vectors of the cryptographic primitives. +/doc: Documentation of TinyCrypt. + +================================================================================ + +Building: + +1) In Makefile.conf set: + - CFLAGS for compiler flags. + - CC for compiler. + - ENABLE_TESTS for enabling (true) or disabling (false) tests compilation. +2) In lib/Makefile select the primitives required by your project. +3) In tests/Makefile select the corresponding tests of the selected primitives. +4) make +5) run tests in tests/ + +================================================================================ + diff --git a/src/tinycrypt/VERSION b/src/tinycrypt/VERSION new file mode 100644 index 00000000..a45be462 --- /dev/null +++ b/src/tinycrypt/VERSION @@ -0,0 +1 @@ +0.2.8 diff --git a/src/tinycrypt/documentation/tinycrypt.rst b/src/tinycrypt/documentation/tinycrypt.rst new file mode 100644 index 00000000..356c099a --- /dev/null +++ b/src/tinycrypt/documentation/tinycrypt.rst @@ -0,0 +1,352 @@ + +TinyCrypt Cryptographic Library +############################### +Copyright (C) 2017 by Intel Corporation, All Rights Reserved. + +Overview +******** +The TinyCrypt Library provides an implementation for targeting constrained devices +with a minimal set of standard cryptography primitives, as listed below. To better +serve applications targeting constrained devices, TinyCrypt implementations differ +from the standard specifications (see the Important Remarks section for some +important differences). Certain cryptographic primitives depend on other +primitives, as mentioned in the list below. + +Aside from the Important Remarks section below, valuable information on the usage, +security and technicalities of each cryptographic primitive are found in the +corresponding header file. + +* SHA-256: + + * Type of primitive: Hash function. + * Standard Specification: NIST FIPS PUB 180-4. + * Requires: -- + +* HMAC-SHA256: + + * Type of primitive: Message authentication code. + * Standard Specification: RFC 2104. + * Requires: SHA-256 + +* HMAC-PRNG: + + * Type of primitive: Pseudo-random number generator (256-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: SHA-256 and HMAC-SHA256. + +* AES-128: + + * Type of primitive: Block cipher. + * Standard Specification: NIST FIPS PUB 197. + * Requires: -- + +* AES-CBC mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CTR mode: + + * Type of primitive: Encryption mode of operation. + * Standard Specification: NIST SP 800-38A. + * Requires: AES-128. + +* AES-CMAC mode: + + * Type of primitive: Message authentication code. + * Standard Specification: NIST SP 800-38B. + * Requires: AES-128. + +* AES-CCM mode: + + * Type of primitive: Authenticated encryption. + * Standard Specification: NIST SP 800-38C. + * Requires: AES-128. + +* CTR-PRNG: + + * Type of primitive: Pseudo-random number generator (128-bit strength). + * Standard Specification: NIST SP 800-90A. + * Requires: AES-128. + +* ECC-DH: + + * Type of primitive: Key exchange based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +* ECC-DSA: + + * Type of primitive: Digital signature based on curve NIST p-256. + * Standard Specification: RFC 6090. + * Requires: ECC auxiliary functions (ecc.h/c). + +Design Goals +************ + +* Minimize the code size of each cryptographic primitive. This means minimize + the size of a platform-independent implementation, as presented in TinyCrypt. + Note that various applications may require further features, optimizations with + respect to other metrics and countermeasures for particular threats. These + peculiarities would increase the code size and thus are not considered here. + +* Minimize the dependencies among the cryptographic primitives. This means + that it is unnecessary to build and allocate object code for more primitives + than the ones strictly required by the intended application. In other words, + one can select and compile only the primitives required by the application. + + +Important Remarks +***************** + +The cryptographic implementations in TinyCrypt library have some limitations. +Some of these limitations are inherent to the cryptographic primitives +themselves, while others are specific to TinyCrypt. These limitations were accepted +in order to meet its design goals (in special, minimal code size) and to better +serve applications targeting constrained devices in general. Some of these +limitations are discussed in-depth below. + +General Remarks +*************** + +* TinyCrypt does **not** intend to be fully side-channel resistant. Due to the + variety of side-channel attacks, many of them only relevant to certain + platforms. In this sense, instead of penalizing all library users with + side-channel countermeasures such as increasing the overall code size, + TinyCrypt only implements certain generic timing-attack countermeasures. + +Specific Remarks +**************** + +* SHA-256: + + * The number of bits_hashed in the state is not checked for overflow. Note + however that this will only be a problem if you intend to hash more than + 2^64 bits, which is an extremely large window. + +* HMAC: + + * The HMAC verification process is assumed to be performed by the application. + This compares the computed tag with some given tag. + Note that conventional memory-comparison methods (such as memcmp function) + might be vulnerable to timing attacks; thus be sure to use a constant-time + memory comparison function (such as compare_constant_time + function provided in lib/utils.c). + + * The tc_hmac_final function, responsible for computing the message tag, + cleans the state context before exiting. Thus, applications do not need to + clean the TCHmacState_t ctx after calling tc_hmac_final. This should not + be changed in future versions of the library as there are applications + currently relying on this good-practice/feature of TinyCrypt. + +* HMAC-PRNG: + + * Before using HMAC-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + + * NIST SP 800-90A requires three items as seed material in the initialization + step: entropy seed, personalization and a nonce (which is not implemented). + TinyCrypt requires the personalization byte array and automatically creates + the entropy seed using a mandatory call to the re-seed function. + +* AES-128: + + * The current implementation does not support other key-lengths (such as 256 + bits). Note that if you need AES-256, it doesn't sound as though your + application is running in a constrained environment. AES-256 requires keys + twice the size as for AES-128, and the key schedule is 40% larger. + +* CTR mode: + + * The AES-CTR mode limits the size of a data message they encrypt to 2^32 + blocks. If you need to encrypt larger data sets, your application would + need to replace the key after 2^32 block encryptions. + +* CTR-PRNG: + + * Before using CTR-PRNG, you *must* find an entropy source to produce a seed. + PRNGs only stretch the seed into a seemingly random output of arbitrary + length. The security of the output is exactly equal to the + unpredictability of the seed. + +* CBC mode: + + * TinyCrypt CBC decryption assumes that the iv and the ciphertext are + contiguous (as produced by TinyCrypt CBC encryption). This allows for a + very efficient decryption algorithm that would not otherwise be possible. + +* CMAC mode: + + * AES128-CMAC mode of operation offers 64 bits of security against collision + attacks. Note however that an external attacker cannot generate the tags + him/herself without knowing the MAC key. In this sense, to attack the + collision property of AES128-CMAC, an external attacker would need the + cooperation of the legal user to produce an exponentially high number of + tags (e.g. 2^64) to finally be able to look for collisions and benefit + from them. As an extra precaution, the current implementation allows to at + most 2^48 calls to tc_cmac_update function before re-calling tc_cmac_setup + (allowing a new key to be set), as suggested in Appendix B of SP 800-38B. + +* CCM mode: + + * There are a few tradeoffs for the selection of the parameters of CCM mode. + In special, there is a tradeoff between the maximum number of invocations + of CCM under a given key and the maximum payload length for those + invocations. Both things are related to the parameter 'q' of CCM mode. The + maximum number of invocations of CCM under a given key is determined by + the nonce size, which is: 15-q bytes. The maximum payload length for those + invocations is defined as 2^(8q) bytes. + + To achieve minimal code size, TinyCrypt CCM implementation fixes q = 2, + which is a quite reasonable choice for constrained applications. The + implications of this choice are: + + The nonce size is: 13 bytes. + + The maximum payload length is: 2^16 bytes = 65 KB. + + The mac size parameter is an important parameter to estimate the security + against collision attacks (that aim at finding different messages that + produce the same authentication tag). TinyCrypt CCM implementation + accepts any even integer between 4 and 16, as suggested in SP 800-38C. + + * TinyCrypt CCM implementation accepts associated data of any length between + 0 and (2^16 - 2^8) = 65280 bytes. + + * TinyCrypt CCM implementation accepts: + + * Both non-empty payload and associated data (it encrypts and + authenticates the payload and only authenticates the associated data); + + * Non-empty payload and empty associated data (it encrypts and + authenticates the payload); + + * Non-empty associated data and empty payload (it degenerates to an + authentication-only mode on the associated data). + + * RFC-3610, which also specifies CCM, presents a few relevant security + suggestions, such as: it is recommended for most applications to use a + mac size greater than 8. Besides, it is emphasized that the usage of the + same nonce for two different messages which are encrypted with the same + key obviously destroys the security properties of CCM mode. + +* ECC-DH and ECC-DSA: + + * TinyCrypt ECC implementation is based on micro-ecc (see + https://github.com/kmackay/micro-ecc). In the original micro-ecc + documentation, there is an important remark about the way integers are + represented: + + "Integer representation: To reduce code size, all large integers are + represented using little-endian words - so the least significant word is + first. You can use the 'ecc_bytes2native()' and 'ecc_native2bytes()' + functions to convert between the native integer representation and the + standardized octet representation." + + Note that the assumed bit layout is: {31, 30, ..., 0}, {63, 62, ..., 32}, + {95, 94, ..., 64}, {127, 126, ..., 96} for a very-long-integer (vli) + consisting of 4 unsigned integers (as an example). + + * A cryptographically-secure PRNG function must be set (using uECC_set_rng()) + before calling uECC_make_key() or uECC_sign(). + +Examples of Applications +************************ +It is possible to do useful cryptography with only the given small set of +primitives. With this list of primitives it becomes feasible to support a range +of cryptography usages: + + * Measurement of code, data structures, and other digital artifacts (SHA256); + + * Generate commitments (SHA256); + + * Construct keys (HMAC-SHA256); + + * Extract entropy from strings containing some randomness (HMAC-SHA256); + + * Construct random mappings (HMAC-SHA256); + + * Construct nonces and challenges (HMAC-PRNG, CTR-PRNG); + + * Authenticate using a shared secret (HMAC-SHA256); + + * Create an authenticated, replay-protected session (HMAC-SHA256 + HMAC-PRNG); + + * Authenticated encryption (AES-128 + AES-CCM); + + * Key-exchange (EC-DH); + + * Digital signature (EC-DSA); + +Test Vectors +************ + +The library provides a test program for each cryptographic primitive (see 'test' +folder). Besides illustrating how to use the primitives, these tests evaluate +the correctness of the implementations by checking the results against +well-known publicly validated test vectors. + +For the case of the HMAC-PRNG, due to the necessity of performing an extensive +battery test to produce meaningful conclusions, we suggest the user to evaluate +the unpredictability of the implementation by using the NIST Statistical Test +Suite (see References). + +For the case of the EC-DH and EC-DSA implementations, most of the test vectors +were obtained from the site of the NIST Cryptographic Algorithm Validation +Program (CAVP), see References. + +References +********** + +* `NIST FIPS PUB 180-4 (SHA-256)`_ + +.. _NIST FIPS PUB 180-4 (SHA-256): + http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf + +* `NIST FIPS PUB 197 (AES-128)`_ + +.. _NIST FIPS PUB 197 (AES-128): + http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +* `NIST SP800-90A (HMAC-PRNG)`_ + +.. _NIST SP800-90A (HMAC-PRNG): + http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + +* `NIST SP 800-38A (AES-CBC and AES-CTR)`_ + +.. _NIST SP 800-38A (AES-CBC and AES-CTR): + http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + +* `NIST SP 800-38B (AES-CMAC)`_ + +.. _NIST SP 800-38B (AES-CMAC): + http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf + +* `NIST SP 800-38C (AES-CCM)`_ + +.. _NIST SP 800-38C (AES-CCM): + http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + +* `NIST Statistical Test Suite (useful for testing HMAC-PRNG)`_ + +.. _NIST Statistical Test Suite (useful for testing HMAC-PRNG): + http://csrc.nist.gov/groups/ST/toolkit/rng/documentation_software.html + +* `NIST Cryptographic Algorithm Validation Program (CAVP) site`_ + +.. _NIST Cryptographic Algorithm Validation Program (CAVP) site: + http://csrc.nist.gov/groups/STM/cavp/ + +* `RFC 2104 (HMAC-SHA256)`_ + +.. _RFC 2104 (HMAC-SHA256): + https://www.ietf.org/rfc/rfc2104.txt + +* `RFC 6090 (ECC-DH and ECC-DSA)`_ + +.. _RFC 6090 (ECC-DH and ECC-DSA): + https://www.ietf.org/rfc/rfc6090.txt From bae2eb26d0128d649eb854bc23a4846d7d7581fc Mon Sep 17 00:00:00 2001 From: h2zero Date: Sun, 29 Mar 2020 14:32:07 -0600 Subject: [PATCH 62/62] More licensing updates. --- LICENSE | 2 +- src/esp_nimble_cfg.h | 2 ++ src/nimble/host/src/ble_gap.c | 2 ++ src/nimble/nimble_npl.h | 2 ++ src/nimble/nimble_port.h | 1 + src/port/src/esp_nimble_mem.c | 2 ++ 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0ca98522..4abe6969 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright {2020} {Ryan Powell} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/esp_nimble_cfg.h b/src/esp_nimble_cfg.h index 8e7ab7ac..16d4253b 100644 --- a/src/esp_nimble_cfg.h +++ b/src/esp_nimble_cfg.h @@ -1,4 +1,6 @@ +/* Modifications copyright (C) 2020 Ryan Powell */ + #ifndef __ESP_NIMBLE_CFG__ #define __ESP_NIMBLE_CFG__ #include "nimconfig.h" diff --git a/src/nimble/host/src/ble_gap.c b/src/nimble/host/src/ble_gap.c index 68c91f75..ebac74c7 100644 --- a/src/nimble/host/src/ble_gap.c +++ b/src/nimble/host/src/ble_gap.c @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ + + /* Modifications copyright (C) 2020 Ryan Powell */ #include #include diff --git a/src/nimble/nimble_npl.h b/src/nimble/nimble_npl.h index 02d27c9f..689d363d 100644 --- a/src/nimble/nimble_npl.h +++ b/src/nimble/nimble_npl.h @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ + +/* Modifications copyright (C) 2020 Ryan Powell*/ #ifndef _NIMBLE_NPL_H_ #define _NIMBLE_NPL_H_ diff --git a/src/nimble/nimble_port.h b/src/nimble/nimble_port.h index e8996a66..aa5e6390 100644 --- a/src/nimble/nimble_port.h +++ b/src/nimble/nimble_port.h @@ -17,6 +17,7 @@ * under the License. */ + /* Modifications copyright (C) 2020 Ryan Powell */ #ifndef _NIMBLE_PORT_H #define _NIMBLE_PORT_H diff --git a/src/port/src/esp_nimble_mem.c b/src/port/src/esp_nimble_mem.c index 5ea52eb7..ae2a69c2 100644 --- a/src/port/src/esp_nimble_mem.c +++ b/src/port/src/esp_nimble_mem.c @@ -19,6 +19,8 @@ * under the License. */ + /* Modifications copyright (C) 2020 Ryan Powell */ + #include "esp_attr.h" #include "esp_heap_caps.h" #include "nimconfig.h"