summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--library.properties2
-rw-r--r--src/BLE2904.cpp74
-rw-r--r--src/BLE2904.h74
-rw-r--r--src/BLEAdvertisedDevice.cpp113
-rw-r--r--src/BLEAdvertisedDevice.h12
-rw-r--r--src/BLEAdvertising.cpp319
-rw-r--r--src/BLEAdvertising.h32
-rw-r--r--src/BLEBeacon.cpp84
-rw-r--r--src/BLEBeacon.h43
-rw-r--r--src/BLECharacteristic.cpp95
-rw-r--r--src/BLECharacteristic.h5
-rw-r--r--src/BLEClient.cpp63
-rw-r--r--src/BLEClient.h37
-rw-r--r--src/BLEDescriptor.cpp17
-rw-r--r--src/BLEDescriptor.h34
-rw-r--r--src/BLEDevice.cpp269
-rw-r--r--src/BLEDevice.h29
-rw-r--r--src/BLEExceptions.cpp9
-rw-r--r--src/BLEExceptions.h31
-rw-r--r--src/BLEHIDDevice.cpp234
-rw-r--r--src/BLEHIDDevice.h75
-rw-r--r--src/BLERemoteCharacteristic.cpp13
-rw-r--r--src/BLERemoteDescriptor.cpp25
-rw-r--r--src/BLERemoteDescriptor.h2
-rw-r--r--src/BLERemoteService.cpp116
-rw-r--r--src/BLERemoteService.h37
-rw-r--r--src/BLEScan.cpp11
-rw-r--r--src/BLEScan.h5
-rw-r--r--src/BLESecurity.cpp105
-rw-r--r--src/BLESecurity.h71
-rw-r--r--src/BLEServer.cpp151
-rw-r--r--src/BLEServer.h3
-rw-r--r--src/BLEService.h8
-rw-r--r--src/BLEUUID.cpp74
-rw-r--r--src/BLEUUID.h3
-rw-r--r--src/BLEUtils.cpp70
-rw-r--r--src/FreeRTOS.cpp46
-rw-r--r--src/FreeRTOS.h4
-rw-r--r--src/GeneralUtils.cpp67
-rw-r--r--src/GeneralUtils.h8
40 files changed, 2114 insertions, 356 deletions
diff --git a/library.properties b/library.properties
index ff5be78..37e6744 100644
--- a/library.properties
+++ b/library.properties
@@ -1,5 +1,5 @@
name=ESP32 BLE Arduino
-version=0.4.7
+version=0.4.8
author=Neil Kolban <kolban1@kolban.com>
maintainer=Neil Kolban <kolban1@kolban.com>
sentence=BLE functions for ESP32
diff --git a/src/BLE2904.cpp b/src/BLE2904.cpp
new file mode 100644
index 0000000..4423ed8
--- /dev/null
+++ b/src/BLE2904.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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 "BLE2904.h"
+
+
+BLE2904::BLE2904() : BLEDescriptor(BLEUUID((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 BLE2904::setDescription(uint16_t description) {
+ m_data.m_description = description;
+ setValue((uint8_t*)&m_data, sizeof(m_data));
+}
+
+
+/**
+ * @brief Set the exponent.
+ */
+void BLE2904::setExponent(int8_t exponent) {
+ m_data.m_exponent = exponent;
+ setValue((uint8_t*)&m_data, sizeof(m_data));
+} // setExponent
+
+
+/**
+ * @brief Set the format.
+ */
+void BLE2904::setFormat(uint8_t format) {
+ m_data.m_format = format;
+ setValue((uint8_t*)&m_data, sizeof(m_data));
+} // setFormat
+
+
+/**
+ * @brief Set the namespace.
+ */
+void BLE2904::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] uint The type of units of this characteristic as defined by assigned numbers.
+ */
+void BLE2904::setUnit(uint16_t unit) {
+ m_data.m_unit = unit;
+ setValue((uint8_t*)&m_data, sizeof(m_data));
+} // setUnit
+
+#endif
diff --git a/src/BLE2904.h b/src/BLE2904.h
new file mode 100644
index 0000000..cb337e2
--- /dev/null
+++ b/src/BLE2904.h
@@ -0,0 +1,74 @@
+/*
+ * BLE2904.h
+ *
+ * Created on: Dec 23, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLE2904_H_
+#define COMPONENTS_CPP_UTILS_BLE2904_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLEDescriptor.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 BLE2904: public BLEDescriptor {
+public:
+ BLE2904();
+ 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 /* COMPONENTS_CPP_UTILS_BLE2904_H_ */
diff --git a/src/BLEAdvertisedDevice.cpp b/src/BLEAdvertisedDevice.cpp
index 4347526..351c5e1 100644
--- a/src/BLEAdvertisedDevice.cpp
+++ b/src/BLEAdvertisedDevice.cpp
@@ -29,6 +29,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
+ m_serviceData = "";
m_txPower = 0;
m_pScan = nullptr;
@@ -36,6 +37,7 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() {
m_haveManufacturerData = false;
m_haveName = false;
m_haveRSSI = false;
+ m_haveServiceData = false;
m_haveServiceUUID = false;
m_haveTXPower = false;
@@ -65,7 +67,7 @@ BLEAddress BLEAdvertisedDevice::getAddress() {
*/
uint16_t BLEAdvertisedDevice::getAppearance() {
return m_appearance;
-}
+} // getAppearance
/**
@@ -74,7 +76,7 @@ uint16_t BLEAdvertisedDevice::getAppearance() {
*/
std::string BLEAdvertisedDevice::getManufacturerData() {
return m_manufacturerData;
-}
+} // getManufacturerData
/**
@@ -105,6 +107,24 @@ BLEScan* BLEAdvertisedDevice::getScan() {
/**
+ * @brief Get the service data.
+ * @return The ServiceData of the advertised device.
+ */
+std::string BLEAdvertisedDevice::getServiceData() {
+ return m_serviceData;
+} //getServiceData
+
+
+/**
+ * @brief Get the service data UUID.
+ * @return The service data UUID.
+ */
+BLEUUID BLEAdvertisedDevice::getServiceDataUUID() {
+ return m_serviceDataUUID;
+} // getServiceDataUUID
+
+
+/**
* @brief Get the Service UUID.
* @return The Service UUID of the advertised device.
*/
@@ -132,6 +152,8 @@ int8_t BLEAdvertisedDevice::getTXPower() {
return m_txPower;
} // getTXPower
+
+
/**
* @brief Does this advertisement have an appearance value?
* @return True if there is an appearance value present.
@@ -169,6 +191,15 @@ bool BLEAdvertisedDevice::haveRSSI() {
/**
+ * @brief Does this advertisement have a service data value?
+ * @return True if there is a service data value present.
+ */
+bool BLEAdvertisedDevice::haveServiceData() {
+ return m_haveServiceData;
+} // haveServiceData
+
+
+/**
* @brief Does this advertisement have a service UUID value?
* @return True if there is a service UUID value present.
*/
@@ -205,8 +236,8 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
bool finished = false;
while(!finished) {
- length = *payload; // Retrieve the length of the record.
- payload++; // Skip to type
+ length = *payload; // Retrieve the length of the record.
+ payload++; // Skip to type
sizeConsumed += 1 + length; // increase the size consumed.
if (length != 0) { // A length of 0 indicates that we have reached the end.
@@ -219,15 +250,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
free(pHex);
-
-
switch(ad_type) {
- case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
+ case ESP_BLE_AD_TYPE_NAME_CMPL: { // Adv Data Type: 0x09
setName(std::string(reinterpret_cast<char*>(payload), length));
break;
} // ESP_BLE_AD_TYPE_NAME_CMPL
- case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
+ case ESP_BLE_AD_TYPE_TX_PWR: { // Adv Data Type: 0x0A
setTXPower(*payload);
break;
} // ESP_BLE_AD_TYPE_TX_PWR
@@ -237,13 +266,13 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
break;
} // ESP_BLE_AD_TYPE_APPEARANCE
- case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
+ case ESP_BLE_AD_TYPE_FLAG: { // Adv Data Type: 0x01
setAdFlag(*payload);
break;
} // ESP_BLE_AD_TYPE_FLAG
case ESP_BLE_AD_TYPE_16SRV_CMPL:
- case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
+ case ESP_BLE_AD_TYPE_16SRV_PART: { // Adv Data Type: 0x02
for (int var = 0; var < length/2; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t*>(payload+var*2)));
}
@@ -251,7 +280,7 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
} // ESP_BLE_AD_TYPE_16SRV_PART
case ESP_BLE_AD_TYPE_32SRV_CMPL:
- case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
+ case ESP_BLE_AD_TYPE_32SRV_PART: { // Adv Data Type: 0x04
for (int var = 0; var < length/4; ++var) {
setServiceUUID(BLEUUID(*reinterpret_cast<uint32_t*>(payload+var*4)));
}
@@ -274,6 +303,45 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
break;
} // ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE
+ case ESP_BLE_AD_TYPE_SERVICE_DATA: { // Adv Data Type: 0x16 (Service Data) - 2 byte UUID
+ if (length < 2) {
+ ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
+ break;
+ }
+ uint16_t uuid = *(uint16_t *)payload;
+ setServiceDataUUID(BLEUUID(uuid));
+ if (length > 2) {
+ setServiceData(std::string(reinterpret_cast<char*>(payload+2), length-2));
+ }
+ break;
+ } //ESP_BLE_AD_TYPE_SERVICE_DATA
+
+ case ESP_BLE_AD_TYPE_32SERVICE_DATA: { // Adv Data Type: 0x20 (Service Data) - 4 byte UUID
+ if (length < 4) {
+ ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
+ break;
+ }
+ uint32_t uuid = *(uint32_t *)payload;
+ setServiceDataUUID(BLEUUID(uuid));
+ if (length > 4) {
+ setServiceData(std::string(reinterpret_cast<char*>(payload+4), length-4));
+ }
+ break;
+ } //ESP_BLE_AD_TYPE_32SERVICE_DATA
+
+ case ESP_BLE_AD_TYPE_128SERVICE_DATA: { // Adv Data Type: 0x21 (Service Data) - 16 byte UUID
+ if (length < 16) {
+ ESP_LOGE(LOG_TAG, "Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
+ break;
+ }
+
+ setServiceDataUUID(BLEUUID(payload, (size_t)16, false));
+ if (length > 16) {
+ setServiceData(std::string(reinterpret_cast<char*>(payload+16), length-16));
+ }
+ break;
+ } //ESP_BLE_AD_TYPE_32SERVICE_DATA
+
default: {
ESP_LOGD(LOG_TAG, "Unhandled type: adType: %d - 0x%.2x", ad_type, ad_type);
break;
@@ -384,6 +452,26 @@ void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
/**
+ * @brief Set the ServiceData value.
+ * @param [in] data ServiceData value.
+ */
+void BLEAdvertisedDevice::setServiceData(std::string serviceData) {
+ m_haveServiceData = true; // Set the flag that indicates we have service data.
+ m_serviceData = serviceData; // Save the service data that we received.
+} //setServiceData
+
+
+/**
+ * @brief Set the ServiceDataUUID value.
+ * @param [in] data ServiceDataUUID value.
+ */
+void BLEAdvertisedDevice::setServiceDataUUID(BLEUUID uuid) {
+ m_haveServiceData = true; // Set the flag that indicates we have service data.
+ m_serviceDataUUID = uuid;
+} // setServiceDataUUID
+
+
+/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
*/
@@ -418,5 +506,8 @@ std::string BLEAdvertisedDevice::toString() {
return ss.str();
} // toString
+
+
+
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLEAdvertisedDevice.h b/src/BLEAdvertisedDevice.h
index aea6da0..41bc4c6 100644
--- a/src/BLEAdvertisedDevice.h
+++ b/src/BLEAdvertisedDevice.h
@@ -35,14 +35,18 @@ public:
std::string getName();
int getRSSI();
BLEScan* getScan();
+ std::string getServiceData();
+ BLEUUID getServiceDataUUID();
BLEUUID getServiceUUID();
int8_t getTXPower();
- bool isAdvertisingService(BLEUUID uuid);
+
+ bool isAdvertisingService(BLEUUID uuid);
bool haveAppearance();
bool haveManufacturerData();
bool haveName();
bool haveRSSI();
+ bool haveServiceData();
bool haveServiceUUID();
bool haveTXPower();
@@ -60,14 +64,18 @@ private:
void setName(std::string name);
void setRSSI(int rssi);
void setScan(BLEScan* pScan);
+ void setServiceData(std::string data);
+ void setServiceDataUUID(BLEUUID uuid);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(BLEUUID serviceUUID);
void setTXPower(int8_t txPower);
+
bool m_haveAppearance;
bool m_haveManufacturerData;
bool m_haveName;
bool m_haveRSSI;
+ bool m_haveServiceData;
bool m_haveServiceUUID;
bool m_haveTXPower;
@@ -82,6 +90,8 @@ private:
int m_rssi;
std::vector<BLEUUID> m_serviceUUIDs;
int8_t m_txPower;
+ std::string m_serviceData;
+ BLEUUID m_serviceDataUUID;
};
/**
diff --git a/src/BLEAdvertising.cpp b/src/BLEAdvertising.cpp
index e5179a4..9e01ab7 100644
--- a/src/BLEAdvertising.cpp
+++ b/src/BLEAdvertising.cpp
@@ -4,6 +4,17 @@
* This class encapsulates advertising a BLE Server.
* Created on: Jun 21, 2017
* Author: kolban
+ *
+ * The ESP-IDF provides a framework for BLE advertising. It has determined that there are a common set
+ * of properties that are advertised and has built a data structure that can be populated by the programmer.
+ * This means that the programmer doesn't have to "mess with" the low level construction of a low level
+ * BLE advertising frame. Many of the fields are determined for us while others we can set before starting
+ * to advertise.
+ *
+ * Should we wish to construct our own payload, we can use the BLEAdvertisementData class and call the setters
+ * upon it. Once it is populated, we can then associate it with the advertising and what ever the programmer
+ * set in the data will be advertised.
+ *
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
@@ -12,6 +23,7 @@
#include <esp_err.h>
#include "BLEUtils.h"
#include "GeneralUtils.h"
+
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
@@ -44,6 +56,9 @@ BLEAdvertising::BLEAdvertising() {
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_customAdvData = false; // No custom advertising data
+ m_customScanResponseData = false; // No custom scan response data
} // BLEAdvertising
@@ -78,12 +93,76 @@ void BLEAdvertising::setAppearance(uint16_t appearance) {
/**
+ * @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");
+ 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)
@@ -92,7 +171,7 @@ void BLEAdvertising::start() {
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];
+ 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());
@@ -105,20 +184,25 @@ void BLEAdvertising::start() {
ESP_LOGD(LOG_TAG, "- no services advertised");
}
+ esp_err_t errRc;
+ if (m_customAdvData == false) {
// Set the configuration for advertising.
- m_advData.set_scan_rsp = false;
- esp_err_t 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;
+ m_advData.set_scan_rsp = false;
+ 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;
+ }
}
- m_advData.set_scan_rsp = true;
- 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 (m_customScanResponseData == false) {
+ m_advData.set_scan_rsp = true;
+ 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.
@@ -153,5 +237,216 @@ void BLEAdvertising::stop() {
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
+
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLEAdvertising.h b/src/BLEAdvertising.h
index 6f315b8..003ad1a 100644
--- a/src/BLEAdvertising.h
+++ b/src/BLEAdvertising.h
@@ -14,6 +14,32 @@
#include <vector>
/**
+ * @brief Advertisement data set by the programmer to be published by the %BLE server.
+ */
+class BLEAdvertisementData {
+ // 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(BLEUUID uuid);
+ void setFlags(uint8_t);
+ void setManufacturerData(std::string data);
+ void setName(std::string name);
+ void setPartialServices(BLEUUID uuid);
+ void setServiceData(BLEUUID uuid, std::string data);
+ void setShortName(std::string name);
+
+private:
+ friend class BLEAdvertising;
+ std::string m_payload; // The payload of the advertisement.
+
+ void addData(std::string data); // Add data to the payload.
+ std::string getPayload(); // Retrieve the current advert payload.
+}; // BLEAdvertisementData
+
+
+/**
* @brief Perform and manage %BLE advertising.
*
* A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
@@ -26,10 +52,16 @@ public:
void start();
void stop();
void setAppearance(uint16_t appearance);
+ void setAdvertisementData(BLEAdvertisementData& advertisementData);
+ void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
+ void setScanResponseData(BLEAdvertisementData& advertisementData);
+
private:
esp_ble_adv_data_t m_advData;
esp_ble_adv_params_t m_advParams;
std::vector<BLEUUID> m_serviceUUIDs;
+ bool m_customAdvData; // Are we using custom advertising data?
+ bool m_customScanResponseData; // Are we using custom scan response data?
};
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */
diff --git a/src/BLEBeacon.cpp b/src/BLEBeacon.cpp
new file mode 100644
index 0000000..a63197c
--- /dev/null
+++ b/src/BLEBeacon.cpp
@@ -0,0 +1,84 @@
+/*
+ * BLEBeacon.cpp
+ *
+ * Created on: Jan 4, 2018
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <string.h>
+#include <esp_log.h>
+#include "BLEBeacon.h"
+
+#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8))
+
+static const char LOG_TAG[] = "BLEBeacon";
+
+BLEBeacon::BLEBeacon() {
+ 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));
+} // BLEBeacon
+
+std::string BLEBeacon::getData() {
+ return std::string((char*)&m_beaconData, sizeof(m_beaconData));
+} // getData
+
+uint16_t BLEBeacon::getMajor() {
+ return m_beaconData.major;
+}
+
+uint16_t BLEBeacon::getManufacturerId() {
+ return m_beaconData.manufacturerId;
+}
+
+uint16_t BLEBeacon::getMinor() {
+ return m_beaconData.minor;
+}
+
+BLEUUID BLEBeacon::getProximityUUID() {
+ return BLEUUID(m_beaconData.proximityUUID, 16, false);
+}
+
+int8_t BLEBeacon::getSignalPower() {
+ return m_beaconData.signalPower;
+}
+
+/**
+ * Set the raw data for the beacon record.
+ */
+void BLEBeacon::setData(std::string data) {
+ if (data.length() != sizeof(m_beaconData)) {
+ ESP_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 BLEBeacon::setMajor(uint16_t major) {
+ m_beaconData.major = ENDIAN_CHANGE_U16(major);
+} // setMajor
+
+void BLEBeacon::setManufacturerId(uint16_t manufacturerId) {
+ m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
+} // setManufacturerId
+
+void BLEBeacon::setMinor(uint16_t minor) {
+ m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
+} // setMinior
+
+void BLEBeacon::setProximityUUID(BLEUUID uuid) {
+ uuid = uuid.to128();
+ memcpy(m_beaconData.proximityUUID, uuid.getNative()->uuid.uuid128, 16);
+} // setProximityUUID
+
+void BLEBeacon::setSignalPower(int8_t signalPower) {
+ m_beaconData.signalPower = signalPower;
+} // setSignalPower
+
+
+#endif
diff --git a/src/BLEBeacon.h b/src/BLEBeacon.h
new file mode 100644
index 0000000..0b02e2b
--- /dev/null
+++ b/src/BLEBeacon.h
@@ -0,0 +1,43 @@
+/*
+ * BLEBeacon2.h
+ *
+ * Created on: Jan 4, 2018
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_
+#define COMPONENTS_CPP_UTILS_BLEBEACON_H_
+#include "BLEUUID.h"
+/**
+ * @brief Representation of a beacon.
+ * See:
+ * * https://en.wikipedia.org/wiki/IBeacon
+ */
+class BLEBeacon {
+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:
+ BLEBeacon();
+ std::string getData();
+ uint16_t getMajor();
+ uint16_t getMinor();
+ uint16_t getManufacturerId();
+ BLEUUID 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(BLEUUID uuid);
+ void setSignalPower(int8_t signalPower);
+}; // BLEBeacon
+
+#endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */
diff --git a/src/BLECharacteristic.cpp b/src/BLECharacteristic.cpp
index 6d746cf..5e5aa2a 100644
--- a/src/BLECharacteristic.cpp
+++ b/src/BLECharacteristic.cpp
@@ -15,6 +15,7 @@
#include <esp_err.h>
#include "BLECharacteristic.h"
#include "BLEService.h"
+#include "BLEDevice.h"
#include "BLEUtils.h"
#include "BLE2902.h"
#include "GeneralUtils.h"
@@ -107,7 +108,7 @@ void BLECharacteristic::executeCreate(BLEService* pService) {
esp_err_t errRc = ::esp_ble_gatts_add_char(
m_pService->getHandle(),
getUUID().getNative(),
- static_cast<esp_gatt_perm_t>(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
+ static_cast<esp_gatt_perm_t>(m_permissions),
getProperties(),
//&value,
nullptr,
@@ -163,6 +164,9 @@ uint16_t BLECharacteristic::getHandle() {
return m_handle;
} // getHandle
+void BLECharacteristic::setAccessPermissions(esp_gatt_perm_t perm) {
+ m_permissions = perm;
+}
esp_gatt_char_prop_t BLECharacteristic::getProperties() {
return m_properties;
@@ -195,17 +199,27 @@ std::string BLECharacteristic::getValue() {
} // getValue
+/**
+ * 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_WRITE_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.
//
@@ -213,7 +227,7 @@ void BLECharacteristic::handleGATTServerEvent(
// - uint16_t conn_id
// - uint32_t trans_id
// - esp_bd_addr_t bda
- // - uint8_t exec_write_flag
+ // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL
//
case ESP_GATTS_EXEC_WRITE_EVT: {
if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
@@ -321,20 +335,18 @@ void BLECharacteristic::handleGATTServerEvent(
// - bool need_rsp
//
case ESP_GATTS_READ_EVT: {
- ESP_LOGD(LOG_TAG, "- Testing: 0x%.2x == 0x%.2x", param->read.handle, m_handle);
if (param->read.handle == m_handle) {
- if (m_pCallbacks != nullptr) {
- m_pCallbacks->onRead(this); // Invoke the read callback.
- }
+
// 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 uints of 22 bytes.
-// The apparent algorithm is as follows.
+// 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.
@@ -348,12 +360,19 @@ void BLECharacteristic::handleGATTServerEvent(
// The following code has deliberately not been factored to make it fewer statements because this would cloud the
// the logic flow comprehension.
//
+ // TODO requires some more research to confirm that 512 is max PDU like in bluetooth specs
+ uint16_t maxOffset = BLEDevice::getMTU() - 1;
+ if (BLEDevice::getMTU() > 512) {
+ maxOffset = 512;
+ }
if (param->read.need_rsp) {
ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)");
esp_gatt_rsp_t rsp;
- std::string value = m_value.getValue();
+
if (param->read.is_long) {
- if (value.length() - m_value.getReadOffset() < 22) {
+ 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();
@@ -361,16 +380,23 @@ void BLECharacteristic::handleGATTServerEvent(
m_value.setReadOffset(0);
} else {
// There will be more to come.
- rsp.attr_value.len = 22;
+ 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 + 22);
+ 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.
}
- } else {
- if (value.length() > 21) {
+
+ std::string value = m_value.getValue();
+
+ if (value.length()+1 > maxOffset) {
// Too big for a single shot entry.
- m_value.setReadOffset(22);
- rsp.attr_value.len = 22;
+ 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 {
@@ -400,6 +426,7 @@ void BLECharacteristic::handleGATTServerEvent(
break;
} // ESP_GATTS_READ_EVT
+
// ESP_GATTS_CONF_EVT
//
// conf:
@@ -411,6 +438,16 @@ void BLECharacteristic::handleGATTServerEvent(
break;
}
+ case ESP_GATTS_CONNECT_EVT: {
+ m_semaphoreConfEvt.give();
+ break;
+ }
+
+ case ESP_GATTS_DISCONNECT_EVT: {
+ m_semaphoreConfEvt.give();
+ break;
+ }
+
default: {
break;
} // default
@@ -421,7 +458,7 @@ void BLECharacteristic::handleGATTServerEvent(
// event.
m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
-
+ ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
} // handleGATTServerEvent
@@ -454,14 +491,11 @@ void BLECharacteristic::indicate() {
return;
}
- if (m_value.getValue().length() > 20) {
- ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
+ if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) {
+ ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum indicate size)", BLEDevice::getMTU() - 3);
}
size_t length = m_value.getValue().length();
- if (length > 20) {
- length = 20;
- }
m_semaphoreConfEvt.take("indicate");
@@ -510,14 +544,13 @@ void BLECharacteristic::notify() {
return;
}
- if (m_value.getValue().length() > 20) {
- ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
+ if (m_value.getValue().length() > (BLEDevice::getMTU() - 3)) {
+ ESP_LOGI(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", BLEDevice::getMTU() - 3);
}
size_t length = m_value.getValue().length();
- if (length > 20) {
- length = 20;
- }
+
+ m_semaphoreConfEvt.take("notify");
esp_err_t errRc = ::esp_ble_gatts_send_indicate(
getService()->getServer()->getGattsIf(),
@@ -528,6 +561,8 @@ void BLECharacteristic::notify() {
return;
}
+ m_semaphoreConfEvt.wait("notify");
+
ESP_LOGD(LOG_TAG, "<< notify");
} // Notify
@@ -695,8 +730,10 @@ std::string BLECharacteristic::toString() {
return stringstream.str();
} // toString
+
BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {}
+
/**
* @brief Callback function to support a read request.
* @param [in] pCharacteristic The characteristic that is the source of the event.
diff --git a/src/BLECharacteristic.h b/src/BLECharacteristic.h
index cacf1e5..10dc787 100644
--- a/src/BLECharacteristic.h
+++ b/src/BLECharacteristic.h
@@ -13,6 +13,7 @@
#include <map>
#include "BLEUUID.h"
#include <esp_gatts_api.h>
+#include <esp_gap_ble_api.h>
#include "BLEDescriptor.h"
#include "BLEValue.h"
#include "FreeRTOS.h"
@@ -78,7 +79,7 @@ public:
void setWriteNoResponseProperty(bool value);
std::string toString();
uint16_t getHandle();
-
+ void setAccessPermissions(esp_gatt_perm_t perm);
static const uint32_t PROPERTY_READ = 1<<0;
static const uint32_t PROPERTY_WRITE = 1<<1;
@@ -88,6 +89,7 @@ public:
static const uint32_t PROPERTY_WRITE_NR = 1<<5;
private:
+
friend class BLEServer;
friend class BLEService;
friend class BLEDescriptor;
@@ -100,6 +102,7 @@ private:
BLECharacteristicCallbacks* m_pCallbacks;
BLEService* m_pService;
BLEValue m_value;
+ esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
void handleGATTServerEvent(
esp_gatts_cb_event_t event,
diff --git a/src/BLEClient.cpp b/src/BLEClient.cpp
index 83e778d..55af054 100644
--- a/src/BLEClient.cpp
+++ b/src/BLEClient.cpp
@@ -7,7 +7,7 @@
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_log.h>
-#include <bt.h>
+#include <esp_bt.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
@@ -67,6 +67,21 @@ BLEClient::~BLEClient() {
/**
+ * @brief Clear any existing services.
+ *
+ */
+void BLEClient::clearServices() {
+ ESP_LOGD(LOG_TAG, ">> clearServices");
+ // Delete all the services.
+ for (auto &myPair : m_servicesMap) {
+ delete myPair.second;
+ }
+ m_servicesMap.clear();
+ ESP_LOGD(LOG_TAG, "<< clearServices");
+} // clearServices
+
+
+/**
* @brief Connect to the partner (BLE Server).
* @param [in] address The address of the partner.
* @return True on success.
@@ -78,6 +93,8 @@ bool BLEClient::connect(BLEAddress address) {
// and then block on its completion. When the event has arrived, we will have the handle.
m_semaphoreRegEvt.take("connect");
+ clearServices(); // Delete any services that may exist.
+
esp_err_t errRc = ::esp_ble_gattc_app_register(0);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
@@ -100,7 +117,7 @@ bool BLEClient::connect(BLEAddress address) {
return false;
}
- uint32_t rc = m_semaphoreOpenEvt.wait("connect");
+ 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
@@ -144,11 +161,13 @@ void BLEClient::gattClientEventHandler(
case ESP_GATTC_DISCONNECT_EVT: {
// If we receive a disconnect event, set the class flag that indicates that we are
// no longer connected.
+ if (m_pClientCallbacks != nullptr) {
+ m_pClientCallbacks->onDisconnect(this);
+ }
m_isConnected = false;
break;
} // ESP_GATTC_DISCONNECT_EVT
-
//
// ESP_GATTC_OPEN_EVT
//
@@ -293,6 +312,7 @@ BLERemoteService* BLEClient::getService(const char* uuid) {
* @brief Get the service object corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
* @return A reference to the Service or nullptr if don't know about it.
+ * @throws BLEUuidNotFound
*/
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
@@ -313,7 +333,7 @@ BLERemoteService* BLEClient::getService(BLEUUID uuid) {
}
} // End of each of the services.
ESP_LOGD(LOG_TAG, "<< getService: not found");
- return nullptr;
+ throw new BLEUuidNotFoundException;
} // getService
@@ -332,7 +352,9 @@ std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
* and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received.
*/
ESP_LOGD(LOG_TAG, ">> getServices");
- m_servicesMap.clear();
+
+ clearServices(); // Clear any services that may exist.
+
esp_err_t errRc = esp_ble_gattc_search_service(
getGattcIf(),
getConnId(),
@@ -349,6 +371,21 @@ std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
return &m_servicesMap;
} // getServices
+
+/**
+ * @brief Get the value of a specific characteristic associated with a specific service.
+ * @param [in] serviceUUID The service that owns the characteristic.
+ * @param [in] characteristicUUID The characteristic whose value we wish to read.
+ * @throws BLEUuidNotFound
+ */
+std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) {
+ ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
+ std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue();
+ ESP_LOGD(LOG_TAG, "<<getValue");
+ return ret;
+} // getValue
+
+
/**
* @brief Handle a received GAP event.
*
@@ -388,6 +425,8 @@ bool BLEClient::isConnected() {
} // isConnected
+
+
/**
* @brief Set the callbacks that will be invoked.
*/
@@ -397,6 +436,19 @@ void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
/**
+ * @brief Set the value of a specific characteristic associated with a specific service.
+ * @param [in] serviceUUID The service that owns the characteristic.
+ * @param [in] characteristicUUID The characteristic whose value we wish to write.
+ * @throws BLEUuidNotFound
+ */
+void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
+ ESP_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
+ getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value);
+ ESP_LOGD(LOG_TAG, "<< setValue");
+} // setValue
+
+
+/**
* @brief Return a string representation of this client.
* @return A string representation of this client.
*/
@@ -411,4 +463,5 @@ std::string BLEClient::toString() {
return ss.str();
} // toString
+
#endif // CONFIG_BT_ENABLED
diff --git a/src/BLEClient.h b/src/BLEClient.h
index b24c71a..a60ed10 100644
--- a/src/BLEClient.h
+++ b/src/BLEClient.h
@@ -15,6 +15,7 @@
#include <string.h>
#include <map>
#include <string>
+#include "BLEExceptions.h"
#include "BLERemoteService.h"
#include "BLEService.h"
#include "BLEAddress.h"
@@ -29,19 +30,28 @@ class BLEClient {
public:
BLEClient();
~BLEClient();
- bool connect(BLEAddress address);
- void disconnect();
- BLEAddress getPeerAddress();
- int getRssi();
- std::map<std::string, BLERemoteService*>* getServices();
- BLERemoteService* getService(const char* uuid);
- BLERemoteService* getService(BLEUUID uuid);
+
+ bool connect(BLEAddress address); // Connect to the remote BLE Server
+ void disconnect(); // Disconnect from the remote BLE Server
+ BLEAddress getPeerAddress(); // Get the address of the remote BLE Server
+ int getRssi(); // Get the RSSI of the remote BLE Server
+ std::map<std::string, BLERemoteService*>* getServices(); // Get a map of the services offered by the remote BLE Server
+ BLERemoteService* getService(const char* uuid); // Get a reference to a specified service offered by the remote BLE server.
+ BLERemoteService* getService(BLEUUID uuid); // Get a reference to a specified service offered by the remote BLE server.
+ std::string getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a given characteristic at a given service.
+
+
void handleGAPEvent(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param);
- bool isConnected();
+
+ bool isConnected(); // Return true if we are connected.
+
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
- std::string toString();
+ void setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a given characteristic at a given service.
+
+ std::string toString(); // Return a string representation of this client.
+
private:
friend class BLEDevice;
@@ -56,11 +66,12 @@ private:
uint16_t getConnId();
esp_gatt_if_t getGattcIf();
- BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0");
+ BLEAddress m_peerAddress = BLEAddress((uint8_t*)"\0\0\0\0\0\0"); // The BD address of the remote server.
uint16_t m_conn_id;
// int m_deviceType;
esp_gatt_if_t m_gattc_if;
- bool m_isConnected;
+ bool m_haveServices; // Have we previously obtain the set of services from the remote server.
+ bool m_isConnected; // Are we currently connected.
BLEClientCallbacks* m_pClientCallbacks;
FreeRTOS::Semaphore m_semaphoreRegEvt = FreeRTOS::Semaphore("RegEvt");
@@ -68,7 +79,8 @@ private:
FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
std::map<std::string, BLERemoteService*> m_servicesMap;
- bool m_haveServices; // Have we previously obtain the set of services.
+ void clearServices(); // Clear any existing services.
+
}; // class BLEDevice
@@ -79,6 +91,7 @@ class BLEClientCallbacks {
public:
virtual ~BLEClientCallbacks() {};
virtual void onConnect(BLEClient *pClient) = 0;
+ virtual void onDisconnect(BLEClient *pClient) = 0;
};
#endif // CONFIG_BT_ENABLED
diff --git a/src/BLEDescriptor.cpp b/src/BLEDescriptor.cpp
index 054ef42..1a72ef3 100644
--- a/src/BLEDescriptor.cpp
+++ b/src/BLEDescriptor.cpp
@@ -37,12 +37,12 @@ BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) {
*/
BLEDescriptor::BLEDescriptor(BLEUUID uuid) {
m_bleUUID = uuid;
- m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value.
- m_value.attr_len = 0;
- m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN;
- m_handle = NULL_HANDLE;
- m_pCharacteristic = nullptr; // No initial characteristic.
- m_pCallback = nullptr; // No initial callback.
+ m_value.attr_value = (uint8_t *)malloc(ESP_GATT_MAX_ATTR_LEN); // Allocate storage for the value.
+ m_value.attr_len = 0; // Initial length is 0.
+ m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN; // Maximum length of the data.
+ m_handle = NULL_HANDLE; // Handle is initially unknown.
+ m_pCharacteristic = nullptr; // No initial characteristic.
+ m_pCallback = nullptr; // No initial callback.
} // BLEDescriptor
@@ -51,7 +51,7 @@ BLEDescriptor::BLEDescriptor(BLEUUID uuid) {
* @brief BLEDescriptor destructor.
*/
BLEDescriptor::~BLEDescriptor() {
- free(m_value.attr_value);
+ free(m_value.attr_value); // Release the storage we created in the constructor.
} // ~BLEDescriptor
@@ -301,6 +301,9 @@ void BLEDescriptor::setValue(std::string value) {
setValue((uint8_t *)value.data(), value.length());
} // setValue
+void BLEDescriptor::setAccessPermissions(esp_gatt_perm_t perm) {
+ m_permissions = perm;
+}
/**
* @brief Return a string representation of the descriptor.
diff --git a/src/BLEDescriptor.h b/src/BLEDescriptor.h
index 4eda4c9..d9e0aef 100644
--- a/src/BLEDescriptor.h
+++ b/src/BLEDescriptor.h
@@ -28,31 +28,37 @@ public:
BLEDescriptor(BLEUUID uuid);
virtual ~BLEDescriptor();
- uint16_t getHandle();
- size_t getLength();
- BLEUUID getUUID();
- uint8_t* getValue();
+ uint16_t getHandle(); // Get the handle of the descriptor.
+ size_t getLength(); // Get the length of the value of the descriptor.
+ BLEUUID 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 setCallbacks(BLEDescriptorCallbacks* pCallbacks);
- void setValue(uint8_t* data, size_t size);
- void setValue(std::string value);
- std::string toString();
+
+ void setAccessPermissions(esp_gatt_perm_t perm); // Set the permissions of the descriptor.
+ void setCallbacks(BLEDescriptorCallbacks* 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 BLEDescriptorMap;
friend class BLECharacteristic;
- BLEUUID m_bleUUID;
- esp_attr_value_t m_value;
- uint16_t m_handle;
- BLECharacteristic* m_pCharacteristic;
+ BLEUUID m_bleUUID;
+ uint16_t m_handle;
BLEDescriptorCallbacks* m_pCallback;
+ BLECharacteristic* m_pCharacteristic;
+ esp_gatt_perm_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
+ FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
+ esp_attr_value_t m_value;
+
void executeCreate(BLECharacteristic* pCharacteristic);
void setHandle(uint16_t handle);
- FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
-};
+}; // BLEDescriptor
+
/**
* @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
diff --git a/src/BLEDevice.cpp b/src/BLEDevice.cpp
index a701459..b333ed7 100644
--- a/src/BLEDevice.cpp
+++ b/src/BLEDevice.cpp
@@ -11,11 +11,13 @@
#include <freertos/task.h>
#include <esp_err.h>
#include <nvs_flash.h>
-#include <bt.h> // ESP32 BLE
+#include <esp_bt.h> // ESP32 BLE
+#include <esp_bt_device.h> // ESP32 BLE
#include <esp_bt_main.h> // ESP32 BLE
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <esp_gatts_api.h> // ESP32 BLE
#include <esp_gattc_api.h> // ESP32 BLE
+#include <esp_gatt_common_api.h>// ESP32 BLE
#include <esp_err.h> // ESP32 ESP-IDF
#include <esp_log.h> // ESP32 ESP-IDF
#include <map> // Part of C++ Standard library
@@ -32,20 +34,29 @@
static const char* LOG_TAG = "BLEDevice";
-
/**
* Singletons for the BLEDevice.
*/
BLEServer* BLEDevice::m_pServer = nullptr;
BLEScan* BLEDevice::m_pScan = nullptr;
BLEClient* BLEDevice::m_pClient = nullptr;
-bool initialized = false;
+bool initialized = false; // Have we been initialized?
+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;
+
/**
* @brief Create a new instance of a client.
* @return A new instance of the client.
*/
-BLEClient* BLEDevice::createClient() {
+/* STATIC */ BLEClient* BLEDevice::createClient() {
+ ESP_LOGD(LOG_TAG, ">> createClient");
+#ifndef CONFIG_GATTC_ENABLE // Check that BLE GATTC is enabled in make menuconfig
+ ESP_LOGE(LOG_TAG, "BLE GATTC is not enabled - CONFIG_GATTC_ENABLE not defined");
+ abort();
+#endif // CONFIG_GATTC_ENABLE
m_pClient = new BLEClient();
+ ESP_LOGD(LOG_TAG, "<< createClient");
return m_pClient;
} // createClient
@@ -54,8 +65,12 @@ BLEClient* BLEDevice::createClient() {
* @brief Create a new instance of a server.
* @return A new instance of the server.
*/
-BLEServer* BLEDevice::createServer() {
+/* STATIC */ BLEServer* BLEDevice::createServer() {
ESP_LOGD(LOG_TAG, ">> createServer");
+#ifndef CONFIG_GATTS_ENABLE // Check that BLE GATTS is enabled in make menuconfig
+ ESP_LOGE(LOG_TAG, "BLE GATTS is not enabled - CONFIG_GATTS_ENABLE not defined");
+ abort();
+#endif // CONFIG_GATTS_ENABLE
m_pServer = new BLEServer();
m_pServer->createApp(0);
ESP_LOGD(LOG_TAG, "<< createServer");
@@ -70,7 +85,7 @@ BLEServer* BLEDevice::createServer() {
* @param [in] gatts_if The connection to the GATT interface.
* @param [in] param Parameters for the event.
*/
-void BLEDevice::gattServerEventHandler(
+/* STATIC */ void BLEDevice::gattServerEventHandler(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param
@@ -81,6 +96,26 @@ void BLEDevice::gattServerEventHandler(
BLEUtils::dumpGattServerEvent(event, gatts_if, param);
+ switch(event) {
+ case ESP_GATTS_CONNECT_EVT: {
+ BLEDevice::m_localMTU = 23;
+ if(BLEDevice::m_securityLevel){
+ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
+ }
+ break;
+ } // ESP_GATTS_CONNECT_EVT
+
+ case ESP_GATTS_MTU_EVT: {
+ BLEDevice::m_localMTU = param->mtu.mtu;
+ ESP_LOGI(LOG_TAG, "ESP_GATTS_MTU_EVT, MTU %d", BLEDevice::m_localMTU);
+ break;
+ }
+ default: {
+ break;
+ }
+ } // switch
+
+
if (BLEDevice::m_pServer != nullptr) {
BLEDevice::m_pServer->handleGATTServerEvent(event, gatts_if, param);
}
@@ -96,7 +131,7 @@ void BLEDevice::gattServerEventHandler(
* @param [in] gattc_if
* @param [in] param
*/
-void BLEDevice::gattClientEventHandler(
+/* STATIC */ void BLEDevice::gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* param) {
@@ -104,13 +139,26 @@ void BLEDevice::gattClientEventHandler(
ESP_LOGD(LOG_TAG, "gattClientEventHandler [esp_gatt_if: %d] ... %s",
gattc_if, BLEUtils::gattClientEventTypeToString(event).c_str());
BLEUtils::dumpGattClientEvent(event, gattc_if, param);
-/*
+
switch(event) {
+ case ESP_GATTC_CONNECT_EVT: {
+ if(BLEDevice::getMTU() != 23){
+ esp_err_t errRc = esp_ble_gattc_send_mtu_req(gattc_if, param->connect.conn_id);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ }
+ if(BLEDevice::m_securityLevel){
+ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
+ }
+ break;
+ } // ESP_GATTC_CONNECT_EVT
+
default: {
break;
}
} // switch
- */
+
// If we have a client registered, call it.
if (BLEDevice::m_pClient != nullptr) {
@@ -123,21 +171,70 @@ void BLEDevice::gattClientEventHandler(
/**
* @brief Handle GAP events.
*/
-void BLEDevice::gapEventHandler(
+/* STATIC */ void BLEDevice::gapEventHandler(
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t *param) {
BLEUtils::dumpGapEvent(event, param);
switch(event) {
- case ESP_GAP_BLE_SEC_REQ_EVT: {
- esp_err_t errRc = ::esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
- if (errRc != ESP_OK) {
- ESP_LOGE(LOG_TAG, "esp_ble_gap_security_rsp: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+
+ case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_OOB_REQ_EVT");
+ break;
+ case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_IR_EVT");
+ break;
+ case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_LOCAL_ER_EVT");
+ break;
+ case ESP_GAP_BLE_NC_REQ_EVT:
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_NC_REQ_EVT");
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey));
+ }
+ break;
+ case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_REQ_EVT: ");
+ // esp_log_buffer_hex(LOG_TAG, m_remote_bda, sizeof(m_remote_bda));
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
}
break;
- }
-
+ /*
+ * TODO should we add white/black list comparison?
+ */
+ case ESP_GAP_BLE_SEC_REQ_EVT:
+ /* send the positive(true) security response to the peer device to accept the security request.
+ If not accept the security request, should sent the security response with negative(false) accept value*/
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_SEC_REQ_EVT");
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest());
+ }
+ else{
+ esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
+ }
+ break;
+ /*
+ *
+ */
+ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
+ ///show the passkey number to the user to input it in the peer deivce.
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PASSKEY_NOTIF_EVT");
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ ESP_LOGI(LOG_TAG, "passKey = %d", param->ble_security.key_notif.passkey);
+ BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey);
+ }
+ break;
+ case ESP_GAP_BLE_KEY_EVT:
+ //shows the ble key type info share with peer device to the user.
+ ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
+ break;
+ case ESP_GAP_BLE_AUTH_CMPL_EVT:
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
+ }
+ break;
default: {
break;
}
@@ -154,15 +251,33 @@ void BLEDevice::gapEventHandler(
if (BLEDevice::m_pScan != nullptr) {
BLEDevice::getScan()->handleGAPEvent(event, param);
}
+
+ /*
+ * Security events:
+ */
+
+
} // gapEventHandler
/**
+ * @brief Get the BLE device address.
+ * @return The BLE device address.
+ */
+/* STATIC*/ BLEAddress BLEDevice::getAddress() {
+ const uint8_t* bdAddr = esp_bt_dev_get_address();
+ esp_bd_addr_t addr;
+ memcpy(addr, bdAddr, sizeof(addr));
+ return BLEAddress(addr);
+} // getAddress
+
+
+/**
* @brief Retrieve the Scan object that we use for scanning.
* @return The scanning object reference. This is a singleton object. The caller should not
* try and release/delete it.
*/
-BLEScan* BLEDevice::getScan() {
+/* STATIC */ BLEScan* BLEDevice::getScan() {
//ESP_LOGD(LOG_TAG, ">> getScan");
if (m_pScan == nullptr) {
m_pScan = new BLEScan();
@@ -174,12 +289,30 @@ BLEScan* BLEDevice::getScan() {
/**
+ * @brief Get the value of a characteristic of a service on a remote device.
+ * @param [in] bdAddress
+ * @param [in] serviceUUID
+ * @param [in] characteristicUUID
+ */
+/* STATIC */ std::string BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID) {
+ ESP_LOGD(LOG_TAG, ">> getValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
+ BLEClient *pClient = createClient();
+ pClient->connect(bdAddress);
+ std::string ret = pClient->getValue(serviceUUID, characteristicUUID);
+ pClient->disconnect();
+ ESP_LOGD(LOG_TAG, "<< getValue");
+ return ret;
+} // getValue
+
+
+/**
* @brief Initialize the %BLE environment.
* @param deviceName The device name of the device.
*/
-void BLEDevice::init(std::string deviceName) {
+/* STATIC */ void BLEDevice::init(std::string deviceName) {
if(!initialized){
- initialized = true;
+ initialized = true; // Set the initialization flag to ensure we are only initialized once.
+
esp_err_t errRc = ::nvs_flash_init();
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
@@ -192,6 +325,7 @@ void BLEDevice::init(std::string deviceName) {
ESP_LOGE(LOG_TAG, "esp_bt_controller_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
+
#ifndef CLASSIC_BT_ENABLED
// esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); //FIXME waiting for response from esp-idf issue
errRc = esp_bt_controller_enable(ESP_BT_MODE_BLE);
@@ -207,6 +341,7 @@ void BLEDevice::init(std::string deviceName) {
return;
}
#endif
+
errRc = esp_bluedroid_init();
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
@@ -225,17 +360,21 @@ void BLEDevice::init(std::string deviceName) {
return;
}
+#ifdef CONFIG_GATTC_ENABLE // Check that BLE client is configured in make menuconfig
errRc = esp_ble_gattc_register_callback(BLEDevice::gattClientEventHandler);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
+#endif // CONFIG_GATTC_ENABLE
+#ifdef CONFIG_GATTS_ENABLE // Check that BLE server is configured in make menuconfig
errRc = esp_ble_gatts_register_callback(BLEDevice::gattServerEventHandler);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gatts_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return;
}
+#endif // CONFIG_GATTS_ENABLE
errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());
if (errRc != ESP_OK) {
@@ -276,4 +415,96 @@ void BLEDevice::init(std::string deviceName) {
ESP_LOGD(LOG_TAG, "<< setPower");
} // setPower
+
+/**
+ * @brief Set the value of a characteristic of a service on a remote device.
+ * @param [in] bdAddress
+ * @param [in] serviceUUID
+ * @param [in] characteristicUUID
+ */
+/* STATIC */ void BLEDevice::setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
+ ESP_LOGD(LOG_TAG, ">> setValue: bdAddress: %s, serviceUUID: %s, characteristicUUID: %s", bdAddress.toString().c_str(), serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
+ BLEClient *pClient = createClient();
+ pClient->connect(bdAddress);
+ pClient->setValue(serviceUUID, characteristicUUID, value);
+ pClient->disconnect();
+} // setValue
+
+
+/**
+ * @brief Return a string representation of the nature of this device.
+ * @return A string representation of the nature of this device.
+ */
+/* STATIC */ std::string BLEDevice::toString() {
+ std::ostringstream oss;
+ oss << "BD Address: " << getAddress().toString();
+ return oss.str();
+} // toString
+
+
+/**
+ * @brief Add an entry to the BLE white list.
+ * @param [in] address The address to add to the white list.
+ */
+void BLEDevice::whiteListAdd(BLEAddress address) {
+ ESP_LOGD(LOG_TAG, ">> whiteListAdd: %s", address.toString().c_str());
+ esp_err_t errRc = esp_ble_gap_update_whitelist(true, *address.getNative()); // True to add an entry.
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ ESP_LOGD(LOG_TAG, "<< whiteListAdd");
+} // whiteListAdd
+
+
+/**
+ * @brief Remove an entry from the BLE white list.
+ * @param [in] address The address to remove from the white list.
+ */
+void BLEDevice::whiteListRemove(BLEAddress address) {
+ ESP_LOGD(LOG_TAG, ">> whiteListRemove: %s", address.toString().c_str());
+ esp_err_t errRc = esp_ble_gap_update_whitelist(false, *address.getNative()); // False to remove an entry.
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_update_whitelist: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ ESP_LOGD(LOG_TAG, "<< whiteListRemove");
+} // whiteListRemove
+
+/*
+ * @brief Set encryption level that will be negotiated with peer device durng connection
+ * @param [in] level Requested encryption level
+ */
+void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) {
+ BLEDevice::m_securityLevel = level;
+}
+
+/*
+ * @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
+ * @param [in] cllbacks Pointer to BLESecurityCallbacks class callback
+ */
+void BLEDevice::setSecurityCallbacks(BLESecurityCallbacks* callbacks) {
+ BLEDevice::m_securityCallbacks = callbacks;
+}
+
+/*
+ * @brief Setup local mtu that will be used to negotiate mtu during request from client peer
+ * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to 517
+ */
+esp_err_t BLEDevice::setMTU(uint16_t mtu) {
+ ESP_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
+ esp_err_t err = esp_ble_gatt_set_local_mtu(mtu);
+ if(err == ESP_OK){
+ m_localMTU = mtu;
+ } else {
+ ESP_LOGE(LOG_TAG, "can't set local mtu value: %d", mtu);
+ }
+ ESP_LOGD(LOG_TAG, "<< setLocalMTU");
+ return err;
+}
+
+/*
+ * @brief Get local MTU value set during mtu request or default value
+ */
+uint16_t BLEDevice::getMTU() {
+ return m_localMTU;
+}
#endif // CONFIG_BT_ENABLED
diff --git a/src/BLEDevice.h b/src/BLEDevice.h
index acf20f3..7f33143 100644
--- a/src/BLEDevice.h
+++ b/src/BLEDevice.h
@@ -13,29 +13,43 @@
#include <esp_gattc_api.h> // ESP32 BLE
#include <map> // Part of C++ STL
#include <string>
-#include <bt.h>
+#include <esp_bt.h>
#include "BLEServer.h"
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLEScan.h"
#include "BLEAddress.h"
+
/**
* @brief %BLE functions.
*/
class BLEDevice {
public:
- static BLEClient* createClient();
- static BLEServer* createServer();
- static void dumpDevices();
- static BLEScan* getScan();
- static void init(std::string deviceName);
+ static BLEClient* createClient(); // Create a new BLE client.
+ static BLEServer* createServer(); // Cretae a new BLE server.
+ static BLEAddress getAddress(); // Retrieve our own local BD address.
+ static BLEScan* getScan(); // Get the scan object
+ static std::string getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID); // Get the value of a characteristic of a service on a server.
+ static void init(std::string deviceName); // Initialize the local BLE environment.
+ static void setPower(esp_power_level_t powerLevel); // Set our power level.
+ static void setValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value); // Set the value of a characteristic on a service on a server.
+ static std::string toString(); // Return a string representation of our device.
+ static void whiteListAdd(BLEAddress address); // Add an entry to the BLE white list.
+ static void whiteListRemove(BLEAddress address); // Remove an entry from the BLE white list.
+ static void setEncryptionLevel(esp_ble_sec_act_t level);
+ static void setSecurityCallbacks(BLESecurityCallbacks* pCallbacks);
+ static esp_err_t setMTU(uint16_t mtu);
+ static uint16_t getMTU();
private:
static BLEServer *m_pServer;
static BLEScan *m_pScan;
static BLEClient *m_pClient;
+ static esp_ble_sec_act_t m_securityLevel;
+ static BLESecurityCallbacks* m_securityCallbacks;
+ static uint16_t m_localMTU;
static esp_gatt_if_t getGattcIF();
@@ -53,9 +67,6 @@ private:
esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param);
-public:
- static void setPower(esp_power_level_t powerLevel);
-
}; // class BLE
#endif // CONFIG_BT_ENABLED
diff --git a/src/BLEExceptions.cpp b/src/BLEExceptions.cpp
new file mode 100644
index 0000000..b6adfd8
--- /dev/null
+++ b/src/BLEExceptions.cpp
@@ -0,0 +1,9 @@
+/*
+ * BLExceptions.cpp
+ *
+ * Created on: Nov 27, 2017
+ * Author: kolban
+ */
+
+#include "BLEExceptions.h"
+
diff --git a/src/BLEExceptions.h b/src/BLEExceptions.h
new file mode 100644
index 0000000..369fcaf
--- /dev/null
+++ b/src/BLEExceptions.h
@@ -0,0 +1,31 @@
+/*
+ * BLExceptions.h
+ *
+ * Created on: Nov 27, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
+#define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_
+#include "sdkconfig.h"
+
+#if CONFIG_CXX_EXCEPTIONS != 1
+#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."
+#endif
+
+#include <exception>
+
+
+class BLEDisconnectedException : public std::exception {
+ const char *what() const throw () {
+ return "BLE Disconnected";
+ }
+};
+
+class BLEUuidNotFoundException : public std::exception {
+ const char *what() const throw () {
+ return "No such UUID";
+ }
+};
+
+#endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */
diff --git a/src/BLEHIDDevice.cpp b/src/BLEHIDDevice.cpp
new file mode 100644
index 0000000..29376f3
--- /dev/null
+++ b/src/BLEHIDDevice.cpp
@@ -0,0 +1,234 @@
+/*
+ * BLEHIDDevice.cpp
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLEHIDDevice.h"
+#include "BLE2904.h"
+
+
+BLEHIDDevice::BLEHIDDevice(BLEServer* server) {
+ /*
+ * Here we create mandatory services described in bluetooth specification
+ */
+ m_deviceInfoService = server->createService(BLEUUID((uint16_t) 0x180a));
+ m_hidService = server->createService(BLEUUID((uint16_t) 0x1812), 40);
+ m_batteryService = server->createService(BLEUUID((uint16_t) 0x180f));
+
+ /*
+ * Mandatory characteristic for device info service
+ */
+ m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a50, BLECharacteristic::PROPERTY_READ);
+
+ /*
+ * Mandatory characteristics for HID service
+ */
+ m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4a, BLECharacteristic::PROPERTY_READ);
+ m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4b, BLECharacteristic::PROPERTY_READ);
+ m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4c, BLECharacteristic::PROPERTY_WRITE_NR);
+ m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4e, BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_READ);
+
+ /*
+ * Mandatory battery level characteristic with notification and presence descriptor
+ */
+ BLE2904* batteryLevelDescriptor = new BLE2904();
+ batteryLevelDescriptor->setFormat(BLE2904::FORMAT_UINT8);
+ batteryLevelDescriptor->setNamespace(1);
+ batteryLevelDescriptor->setUnit(0x27ad);
+
+ m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t)0x2a19, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
+ m_batteryLevelCharacteristic->addDescriptor(batteryLevelDescriptor);
+ m_batteryLevelCharacteristic->addDescriptor(new BLE2902());
+
+ /*
+ * This value is setup here because its default value in most usage cases, its very rare to use boot mode
+ * and we want to simplify library using as much as possible
+ */
+ const uint8_t pMode[] = {0x01};
+ protocolMode()->setValue((uint8_t*)pMode, 1);
+}
+
+BLEHIDDevice::~BLEHIDDevice() {
+}
+
+/*
+ * @brief
+ */
+void BLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
+ m_reportMapCharacteristic->setValue(map, size);
+}
+
+/*
+ * @brief This function suppose to be called at the end, when we have created all characteristics we need to build HID service
+ */
+void BLEHIDDevice::startServices() {
+ m_deviceInfoService->start();
+ m_hidService->start();
+ m_batteryService->start();
+}
+
+/*
+ * @brief Create manufacturer characteristic (this characteristic is optional)
+ */
+BLECharacteristic* BLEHIDDevice::manufacturer() {
+ m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t)0x2a29, BLECharacteristic::PROPERTY_READ);
+ return m_manufacturerCharacteristic;
+}
+
+/*
+ * @brief Set manufacturer name
+ * @param [in] name manufacturer name
+ */
+void BLEHIDDevice::manufacturer(std::string name) {
+ m_manufacturerCharacteristic->setValue(name);
+}
+
+/*
+ * @brief
+ */
+void BLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
+ uint8_t pnp[] = {sig, (uint8_t)(vid>>8), (uint8_t)vid, (uint8_t)(pid>>8), (uint8_t)pid, (uint8_t)(version>>8), (uint8_t)version};
+ m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
+}
+
+/*
+ * @brief
+ */
+void BLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
+ uint8_t info[] = {0x11,0x1, country, flags};
+ m_hidInfoCharacteristic->setValue(info, sizeof(info));;
+}
+
+/*
+ * @brief Create input report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID input report ID, the same as in report map for input object related to created characteristic
+ * @return pointer to new input report characteristic
+ */
+BLECharacteristic* BLEHIDDevice::inputReport(uint8_t reportID) {
+ BLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
+ BLEDescriptor* inputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908));
+
+ uint8_t desc1_val[] = {reportID, 0x01};
+ inputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
+ inputReportCharacteristic->addDescriptor(new BLE2902());
+ inputReportCharacteristic->addDescriptor(inputReportDescriptor);
+
+ return inputReportCharacteristic;
+}
+
+/*
+ * @brief Create output report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID Output report ID, the same as in report map for output object related to created characteristic
+ * @return Pointer to new output report characteristic
+ */
+BLECharacteristic* BLEHIDDevice::outputReport(uint8_t reportID) {
+ BLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
+ BLEDescriptor* outputReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908));
+
+ uint8_t desc1_val[] = {reportID, 0x02};
+ outputReportDescriptor->setValue((uint8_t*)desc1_val, 2);
+ outputReportCharacteristic->addDescriptor(outputReportDescriptor);
+
+ return outputReportCharacteristic;
+}
+
+/*
+ * @brief Create feature report characteristic that need to be saved as new characteristic object so can be further used
+ * @param [in] reportID Feature report ID, the same as in report map for feature object related to created characteristic
+ * @return Pointer to new feature report characteristic
+ */
+BLECharacteristic* BLEHIDDevice::featureReport(uint8_t reportID) {
+ BLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a4d, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
+ BLEDescriptor* featureReportDescriptor = new BLEDescriptor(BLEUUID((uint16_t)0x2908));
+
+ uint8_t desc1_val[] = {reportID, 0x03};
+ featureReportDescriptor->setValue((uint8_t*)desc1_val, 2);
+ featureReportCharacteristic->addDescriptor(featureReportDescriptor);
+
+ return featureReportCharacteristic;
+}
+
+/*
+ * @brief
+ */
+BLECharacteristic* BLEHIDDevice::bootInput() {
+ BLECharacteristic* bootInputCharacteristic = m_hidService->createCharacteristic((uint16_t)0x2a22, BLECharacteristic::PROPERTY_NOTIFY);
+ bootInputCharacteristic->addDescriptor(new BLE2902());
+
+ return bootInputCharacteristic;
+}
+
+/*
+ * @brief
+ */
+BLECharacteristic* BLEHIDDevice::bootOutput() {
+ return m_hidService->createCharacteristic((uint16_t)0x2a32, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
+}
+
+/*
+ * @brief
+ */
+BLECharacteristic* BLEHIDDevice::hidControl() {
+ return m_hidControlCharacteristic;
+}
+
+/*
+ * @brief
+ */
+BLECharacteristic* BLEHIDDevice::protocolMode() {
+ return m_protocolModeCharacteristic;
+}
+
+void BLEHIDDevice::setBatteryLevel(uint8_t level) {
+ m_batteryLevelCharacteristic->setValue(&level, 1);
+}
+/*
+ * @brief Returns battery level characteristic
+ * @ return battery level characteristic
+ *//*
+BLECharacteristic* BLEHIDDevice::batteryLevel() {
+ return m_batteryLevelCharacteristic;
+}
+
+
+
+BLECharacteristic* BLEHIDDevice::reportMap() {
+ return m_reportMapCharacteristic;
+}
+
+BLECharacteristic* BLEHIDDevice::pnp() {
+ return m_pnpCharacteristic;
+}
+
+
+BLECharacteristic* BLEHIDDevice::hidInfo() {
+ return m_hidInfoCharacteristic;
+}
+*/
+/*
+ * @brief
+ */
+BLEService* BLEHIDDevice::deviceInfo() {
+ return m_deviceInfoService;
+}
+
+/*
+ * @brief
+ */
+BLEService* BLEHIDDevice::hidService() {
+ return m_hidService;
+}
+
+/*
+ * @brief
+ */
+BLEService* BLEHIDDevice::batteryService() {
+ return m_batteryService;
+}
+
+#endif // CONFIG_BT_ENABLED
+
diff --git a/src/BLEHIDDevice.h b/src/BLEHIDDevice.h
new file mode 100644
index 0000000..319fd42
--- /dev/null
+++ b/src/BLEHIDDevice.h
@@ -0,0 +1,75 @@
+/*
+ * BLEHIDDevice.h
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+
+#ifndef _BLEHIDDEVICE_H_
+#define _BLEHIDDEVICE_H_
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLECharacteristic.h"
+#include "BLEService.h"
+#include "BLEDescriptor.h"
+#include "BLE2902.h"
+#include "HIDTypes.h"
+
+#define GENERIC_HID 960
+#define HID_KEYBOARD 961
+#define HID_MOUSE 962
+#define HID_JOYSTICK 963
+#define HID_GAMEPAD 964
+#define HID_TABLET 965
+#define HID_CARD_READER 966
+#define HID_DIGITAL_PEN 967
+#define HID_BARCODE 968
+
+class BLEHIDDevice {
+public:
+ BLEHIDDevice(BLEServer*);
+ virtual ~BLEHIDDevice();
+
+ void reportMap(uint8_t* map, uint16_t);
+ void startServices();
+
+ BLEService* deviceInfo();
+ BLEService* hidService();
+ BLEService* batteryService();
+
+ BLECharacteristic* manufacturer();
+ void manufacturer(std::string name);
+ //BLECharacteristic* pnp();
+ void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
+ //BLECharacteristic* hidInfo();
+ void hidInfo(uint8_t country, uint8_t flags);
+ //BLECharacteristic* batteryLevel();
+ void setBatteryLevel(uint8_t level);
+
+
+ //BLECharacteristic* reportMap();
+ BLECharacteristic* hidControl();
+ BLECharacteristic* inputReport(uint8_t reportID);
+ BLECharacteristic* outputReport(uint8_t reportID);
+ BLECharacteristic* featureReport(uint8_t reportID);
+ BLECharacteristic* protocolMode();
+ BLECharacteristic* bootInput();
+ BLECharacteristic* bootOutput();
+
+private:
+ BLEService* m_deviceInfoService; //0x180a
+ BLEService* m_hidService; //0x1812
+ BLEService* m_batteryService = 0; //0x180f
+
+ BLECharacteristic* m_manufacturerCharacteristic; //0x2a29
+ BLECharacteristic* m_pnpCharacteristic; //0x2a50
+ BLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
+ BLECharacteristic* m_reportMapCharacteristic; //0x2a4b
+ BLECharacteristic* m_hidControlCharacteristic; //0x2a4c
+ BLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
+ BLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
+};
+#endif // CONFIG_BT_ENABLED
+#endif /* _BLEHIDDEVICE_H_ */
diff --git a/src/BLERemoteCharacteristic.cpp b/src/BLERemoteCharacteristic.cpp
index 64b0a92..d9c64c9 100644
--- a/src/BLERemoteCharacteristic.cpp
+++ b/src/BLERemoteCharacteristic.cpp
@@ -15,6 +15,7 @@
#include <esp_err.h>
#include <sstream>
+#include "BLEExceptions.h"
#include "BLEUtils.h"
#include "GeneralUtils.h"
#include "BLERemoteDescriptor.h"
@@ -432,6 +433,12 @@ uint8_t BLERemoteCharacteristic::readUInt8(void) {
std::string BLERemoteCharacteristic::readValue() {
ESP_LOGD(LOG_TAG, ">> readValue(): uuid: %s, handle: %d 0x%.2x", getUUID().toString().c_str(), getHandle(), getHandle());
+ // Check to see that we are connected.
+ if (!getRemoteService()->getClient()->isConnected()) {
+ ESP_LOGE(LOG_TAG, "Disconnected");
+ throw BLEDisconnectedException();
+ }
+
m_semaphoreReadCharEvt.take("readValue");
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
@@ -543,6 +550,12 @@ std::string BLERemoteCharacteristic::toString() {
void BLERemoteCharacteristic::writeValue(std::string newValue, bool response) {
ESP_LOGD(LOG_TAG, ">> writeValue(), length: %d", newValue.length());
+ // Check to see that we are connected.
+ if (!getRemoteService()->getClient()->isConnected()) {
+ ESP_LOGE(LOG_TAG, "Disconnected");
+ throw BLEDisconnectedException();
+ }
+
m_semaphoreWriteCharEvt.take("writeValue");
// Invoke the ESP-IDF API to perform the write.
diff --git a/src/BLERemoteDescriptor.cpp b/src/BLERemoteDescriptor.cpp
index 7a509f8..4d14ba8 100644
--- a/src/BLERemoteDescriptor.cpp
+++ b/src/BLERemoteDescriptor.cpp
@@ -27,6 +27,7 @@ BLERemoteDescriptor::BLERemoteDescriptor(
m_pRemoteCharacteristic = pRemoteCharacteristic;
}
+
/**
* @brief Retrieve the handle associated with this remote descriptor.
* @return The handle associated with this remote descriptor.
@@ -37,6 +38,15 @@ uint16_t BLERemoteDescriptor::getHandle() {
/**
+ * @brief Get the characteristic that owns this descriptor.
+ * @return The characteristic that owns this descriptor.
+ */
+BLERemoteCharacteristic* BLERemoteDescriptor::getRemoteCharacteristic() {
+ return m_pRemoteCharacteristic;
+} // getRemoteCharacteristic
+
+
+/**
* @brief Retrieve the UUID associated this remote descriptor.
* @return The UUID associated this remote descriptor.
*/
@@ -46,6 +56,14 @@ BLEUUID BLERemoteDescriptor::getUUID() {
std::string BLERemoteDescriptor::readValue(void) {
+ ESP_LOGD(LOG_TAG, ">> readValue: %s", toString().c_str());
+
+ // Check to see that we are connected.
+ if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
+ ESP_LOGE(LOG_TAG, "Disconnected");
+ throw BLEDisconnectedException();
+ }
+
m_semaphoreReadDescrEvt.take("readValue");
// Ask the BLE subsystem to retrieve the value for the remote hosted characteristic.
@@ -66,7 +84,6 @@ std::string BLERemoteDescriptor::readValue(void) {
ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
return m_value;
- return "";
} // readValue
@@ -119,6 +136,12 @@ void BLERemoteDescriptor::writeValue(
size_t length,
bool response) {
ESP_LOGD(LOG_TAG, ">> writeValue: %s", toString().c_str());
+ // Check to see that we are connected.
+ if (!getRemoteCharacteristic()->getRemoteService()->getClient()->isConnected()) {
+ ESP_LOGE(LOG_TAG, "Disconnected");
+ throw BLEDisconnectedException();
+ }
+
esp_err_t errRc = ::esp_ble_gattc_write_char_descr(
m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(),
diff --git a/src/BLERemoteDescriptor.h b/src/BLERemoteDescriptor.h
index c2cf383..7bbc48f 100644
--- a/src/BLERemoteDescriptor.h
+++ b/src/BLERemoteDescriptor.h
@@ -24,13 +24,13 @@ class BLERemoteCharacteristic;
class BLERemoteDescriptor {
public:
uint16_t getHandle();
+ BLERemoteCharacteristic* getRemoteCharacteristic();
BLEUUID getUUID();
std::string readValue(void);
uint8_t readUInt8(void);
uint16_t readUInt16(void);
uint32_t readUInt32(void);
std::string toString(void);
- //void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify));
void writeValue(uint8_t* data, size_t length, bool response = false);
void writeValue(std::string newValue, bool response = false);
void writeValue(uint8_t newValue, bool response = false);
diff --git a/src/BLERemoteService.cpp b/src/BLERemoteService.cpp
index 5227964..78ff991 100644
--- a/src/BLERemoteService.cpp
+++ b/src/BLERemoteService.cpp
@@ -115,7 +115,7 @@ void BLERemoteService::gattClientEventHandler(
// Send the event to each of the characteristics owned by this service.
for (auto &myPair : m_characteristicMap) {
- myPair.first->gattClientEventHandler(event, gattc_if, evtParam);
+ myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
@@ -124,6 +124,7 @@ void BLERemoteService::gattClientEventHandler(
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return Reference to the remote characteristic object.
+ * @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(BLEUUID(uuid));
@@ -134,6 +135,7 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) {
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return Reference to the characteristic object.
+ * @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) {
// Design
@@ -147,11 +149,11 @@ BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) {
}
std::string v = uuid.toString();
for (auto &myPair : m_characteristicMap) {
- if (myPair.second == v) {
- return myPair.first;
+ if (myPair.first == v) {
+ return myPair.second;
}
}
- return nullptr;
+ throw new BLEUuidNotFoundException();
} // getCharacteristic
@@ -165,60 +167,6 @@ void BLERemoteService::retrieveCharacteristics() {
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
removeCharacteristics(); // Forget any previous characteristics.
- /*
- m_semaphoreGetCharEvt.take("getCharacteristics");
-
- esp_err_t errRc = ::esp_ble_gattc_get_characteristic(
- m_pClient->getGattcIf(),
- m_pClient->getConnId(),
- &m_srvcId,
- nullptr);
-
- if (errRc != ESP_OK) {
- ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
- return;
- }
-
- m_semaphoreGetCharEvt.wait("getCharacteristics"); // Wait for the characteristics to become available.
-
- m_haveCharacteristics = true; // Remember that we have received the characteristics.
- */
- //ESP_LOGE(LOG_TAG, "!!! NOT IMPLEMENTED !!!");
- //ESP_LOGD(LOG_TAG, "--- test code ---");
- /*
- uint16_t count;
- esp_gatt_status_t status = ::esp_ble_gattc_get_attr_count(
- getClient()->getGattcIf(),
- getClient()->getConnId(),
- ESP_GATT_DB_CHARACTERISTIC,
- m_startHandle,
- m_endHandle,
- 0, // Characteristic handle ... only used for ESP_GATT_DB_DESCRIPTOR
- &count
- );
- if (status != ESP_GATT_OK) {
- ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_attr_count: %s", BLEUtils::gattStatusToString(status).c_str());
- } else {
- ESP_LOGD(LOG_TAG, "Number of characteristics associated with service is %d", count);
- }
-
- count = 1;
- esp_gattc_service_elem_t srvcElem;
- status = ::esp_ble_gattc_get_service(
- getClient()->getGattcIf(),
- getClient()->getConnId(),
- &m_srvcId.uuid, // UUID of service
- &srvcElem, // Records
- &count, // records retrieved
- 0 // offset
- );
- if (status != ESP_GATT_OK) {
- ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_service: %s", BLEUtils::gattStatusToString(status).c_str());
- }
- else {
- ESP_LOGD(LOG_TAG, "%s", BLEUtils::gattcServiceElementToString(&srvcElem).c_str());
- }
- */
uint16_t offset = 0;
esp_gattc_char_elem_t result;
@@ -257,7 +205,7 @@ void BLERemoteService::retrieveCharacteristics() {
this
);
- m_characteristicMap.insert(std::pair<BLERemoteCharacteristic*, std::string>(pNewRemoteCharacteristic, pNewRemoteCharacteristic->getUUID().toString()));
+ m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
offset++; // Increment our count of number of descriptors found.
} // Loop forever (until we break inside the loop).
@@ -271,7 +219,7 @@ void BLERemoteService::retrieveCharacteristics() {
* @brief Retrieve a map of all the characteristics of this service.
* @return A map of all the characteristics of this service.
*/
-std::map<BLERemoteCharacteristic*, std::string>* BLERemoteService::getCharacteristics() {
+std::map<std::string, BLERemoteCharacteristic *> * BLERemoteService::getCharacteristics() {
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
// If is possible that we have not read the characteristics associated with the service so do that
// now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking
@@ -284,37 +232,52 @@ std::map<BLERemoteCharacteristic*, std::string>* BLERemoteService::getCharacteri
} // getCharacteristics
+/**
+ * @brief Get the client associated with this service.
+ * @return A reference to the client associated with this service.
+ */
BLEClient* BLERemoteService::getClient() {
return m_pClient;
-}
+} // getClient
uint16_t BLERemoteService::getEndHandle() {
return m_endHandle;
-}
+} // getEndHandle
esp_gatt_id_t* BLERemoteService::getSrvcId() {
return &m_srvcId;
-}
+} // getSrvcId
uint16_t BLERemoteService::getStartHandle() {
return m_startHandle;
-}
+} // getStartHandle
+
uint16_t BLERemoteService::getHandle() {
ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str());
- //ESP_LOGE(LOG_TAG, "!!! getHandle: NOT IMPLEMENTED !!!");
ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle());
return getStartHandle();
-}
+} // getHandle
BLEUUID BLERemoteService::getUUID() {
return m_uuid;
}
+/**
+ * @brief Read the value of a characteristic associated with this service.
+ */
+std::string BLERemoteService::getValue(BLEUUID characteristicUuid) {
+ ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
+ std::string ret = getCharacteristic(characteristicUuid)->readValue();
+ ESP_LOGD(LOG_TAG, "<< readValue");
+ return ret;
+} // readValue
+
+
/**
* @brief Delete the characteristics in the characteristics map.
@@ -325,13 +288,25 @@ BLEUUID BLERemoteService::getUUID() {
*/
void BLERemoteService::removeCharacteristics() {
for (auto &myPair : m_characteristicMap) {
- delete myPair.first;
- m_characteristicMap.erase(myPair.first);
+ delete myPair.second;
+ //m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear
}
m_characteristicMap.clear(); // Clear the map
} // removeCharacteristics
+/**
+ * @brief Set the value of a characteristic.
+ * @param [in] characteristicUuid The characteristic to set.
+ * @param [in] value The value to set.
+ * @throws BLEUuidNotFound
+ */
+void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) {
+ ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
+ getCharacteristic(characteristicUuid)->writeValue(value);
+ ESP_LOGD(LOG_TAG, "<< setValue");
+} // setValue
+
/**
* @brief Create a string representation of this remote service.
@@ -343,14 +318,11 @@ std::string BLERemoteService::toString() {
ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle <<
", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle;
for (auto &myPair : m_characteristicMap) {
- ss << "\n" << myPair.first->toString();
+ ss << "\n" << myPair.second->toString();
// myPair.second is the value
}
return ss.str();
} // toString
-
-
-
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLERemoteService.h b/src/BLERemoteService.h
index 521effc..222c6e4 100644
--- a/src/BLERemoteService.h
+++ b/src/BLERemoteService.h
@@ -30,15 +30,17 @@ public:
virtual ~BLERemoteService();
// Public methods
- BLERemoteCharacteristic* getCharacteristic(const char* uuid);
- BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid);
- BLERemoteCharacteristic* getCharacteristic(uint16_t uuid);
- std::map<BLERemoteCharacteristic*, std::string>* getCharacteristics();
- void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* ptr);
-
- BLEClient* getClient(void);
- uint16_t getHandle();
- BLEUUID getUUID(void);
+ BLERemoteCharacteristic* getCharacteristic(const char* uuid); // Get the specified characteristic reference.
+ BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid); // Get the specified characteristic reference.
+ BLERemoteCharacteristic* getCharacteristic(uint16_t uuid); // Get the specified characteristic reference.
+ std::map<std::string, BLERemoteCharacteristic*>* getCharacteristics();
+ void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap); // Get the characteristics map.
+
+ BLEClient* getClient(void); // Get a reference to the client associated with this service.
+ uint16_t getHandle(); // Get the handle of this service.
+ BLEUUID getUUID(void); // Get the UUID of this service.
+ std::string getValue(BLEUUID characteristicUuid); // Get the value of a characteristic.
+ void setValue(BLEUUID characteristicUuid, std::string value); // Set the value of a characteristic.
std::string toString(void);
private:
@@ -50,20 +52,23 @@ private:
friend class BLERemoteCharacteristic;
// Private methods
- void retrieveCharacteristics(void);
+ void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server.
esp_gatt_id_t* getSrvcId(void);
- uint16_t getStartHandle();
- uint16_t getEndHandle();
+ uint16_t getStartHandle(); // Get the start handle for this service.
+ uint16_t getEndHandle(); // Get the end handle for this service.
+
void gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* evtParam);
+
void removeCharacteristics();
// Properties
// We maintain a map of characteristics owned by this service keyed by a string representation of the UUID.
- std::map<BLERemoteCharacteristic *, std::string> m_characteristicMap;
+ std::map<std::string, BLERemoteCharacteristic *> m_characteristicMap;
+
// We maintain a map of characteristics owned by this service keyed by a handle.
std::map<uint16_t, BLERemoteCharacteristic *> m_characteristicMapByHandle;
@@ -71,9 +76,9 @@ private:
BLEClient* m_pClient;
FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
esp_gatt_id_t m_srvcId;
- BLEUUID m_uuid;
- uint16_t m_startHandle;
- uint16_t m_endHandle;
+ BLEUUID m_uuid; // The UUID of this service.
+ uint16_t m_startHandle; // The starting handle of this service.
+ uint16_t m_endHandle; // The ending handle of this service.
}; // BLERemoteService
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLEScan.cpp b/src/BLEScan.cpp
index 7c5b7bd..e450664 100644
--- a/src/BLEScan.cpp
+++ b/src/BLEScan.cpp
@@ -33,6 +33,7 @@ BLEScan::BLEScan() {
m_scan_params.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
m_pAdvertisedDeviceCallbacks = nullptr;
m_stopped = true;
+ m_wantDuplicates = false;
setInterval(100);
setWindow(100);
} // BLEScan
@@ -97,7 +98,7 @@ void BLEScan::handleGAPEvent(
break;
}
}
- if (found) {
+ if (found && !m_wantDuplicates) { // If we found a previous entry AND we don't want duplicates, then we are done.
ESP_LOGD(LOG_TAG, "Ignoring %s, already seen it.", advertisedAddress.toString().c_str());
break;
}
@@ -115,7 +116,9 @@ void BLEScan::handleGAPEvent(
m_pAdvertisedDeviceCallbacks->onResult(advertisedDevice);
}
- m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice);
+ if (!found) { // If we have previously seen this device, don't record it again.
+ m_scanResults.m_vectorAdvertisedDevices.push_back(advertisedDevice);
+ }
break;
} // ESP_GAP_SEARCH_INQ_RES_EVT
@@ -154,8 +157,10 @@ void BLEScan::setActiveScan(bool active) {
/**
* @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
+ * @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false.
*/
-void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks) {
+void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates) {
+ m_wantDuplicates = wantDuplicates;
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
} // setAdvertisedDeviceCallbacks
diff --git a/src/BLEScan.h b/src/BLEScan.h
index edf79a9..c905c43 100644
--- a/src/BLEScan.h
+++ b/src/BLEScan.h
@@ -48,7 +48,9 @@ private:
class BLEScan {
public:
void setActiveScan(bool active);
- void setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks);
+ void setAdvertisedDeviceCallbacks(
+ BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
+ bool wantDuplicates = false);
void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs);
BLEScanResults start(uint32_t duration);
@@ -68,6 +70,7 @@ private:
bool m_stopped;
FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
BLEScanResults m_scanResults;
+ bool m_wantDuplicates;
}; // BLEScan
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLESecurity.cpp b/src/BLESecurity.cpp
new file mode 100644
index 0000000..4cf964a
--- /dev/null
+++ b/src/BLESecurity.cpp
@@ -0,0 +1,105 @@
+/*
+ * BLESecurity.cpp
+ *
+ * Created on: Dec 17, 2017
+ * Author: chegewara
+ */
+
+#include <BLESecurity.h>
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+BLESecurity::BLESecurity() {
+}
+
+BLESecurity::~BLESecurity() {
+}
+/*
+ * @brief Set requested authentication mode
+ */
+void BLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
+ m_authReq = auth_req;
+ esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); // <--- setup requested authentication mode
+}
+
+/**
+ * @brief Set our device IO capability to let end user perform authorization
+ * either by displaying or entering generated 6-digits pin code
+ */
+void BLESecurity::setCapability(esp_ble_io_cap_t iocap) {
+ m_iocap = iocap;
+ esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
+} // setCapability
+
+
+/**
+ * @brief Init encryption key by server
+ * @param key_size is value between 7 and 16
+ */
+void BLESecurity::setInitEncryptionKey(uint8_t init_key) {
+ m_initKey = init_key;
+ esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t));
+} // setInitEncryptionKey
+
+
+/**
+ * @brief Init encryption key by client
+ * @param key_size is value between 7 and 16
+ */
+void BLESecurity::setRespEncryptionKey(uint8_t resp_key) {
+ m_respKey = resp_key;
+ esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t));
+} // setRespEncryptionKey
+
+
+/**
+ *
+ *
+ */
+void BLESecurity::setKeySize(uint8_t key_size) {
+ m_keySize = key_size;
+ esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t));
+} //setKeySize
+
+
+/**
+ * @brief Debug function to display what keys are exchanged by peers
+ */
+char* BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type)
+{
+ char* key_str = nullptr;
+ switch(key_type) {
+ case ESP_LE_KEY_NONE:
+ key_str = (char*)"ESP_LE_KEY_NONE";
+ break;
+ case ESP_LE_KEY_PENC:
+ key_str = (char*)"ESP_LE_KEY_PENC";
+ break;
+ case ESP_LE_KEY_PID:
+ key_str = (char*)"ESP_LE_KEY_PID";
+ break;
+ case ESP_LE_KEY_PCSRK:
+ key_str = (char*)"ESP_LE_KEY_PCSRK";
+ break;
+ case ESP_LE_KEY_PLK:
+ key_str = (char*)"ESP_LE_KEY_PLK";
+ break;
+ case ESP_LE_KEY_LLK:
+ key_str = (char*)"ESP_LE_KEY_LLK";
+ break;
+ case ESP_LE_KEY_LENC:
+ key_str = (char*)"ESP_LE_KEY_LENC";
+ break;
+ case ESP_LE_KEY_LID:
+ key_str = (char*)"ESP_LE_KEY_LID";
+ break;
+ case ESP_LE_KEY_LCSRK:
+ key_str = (char*)"ESP_LE_KEY_LCSRK";
+ break;
+ default:
+ key_str = (char*)"INVALID BLE KEY TYPE";
+ break;
+ }
+ return key_str;
+} // esp_key_type_to_str
+#endif // CONFIG_BT_ENABLED
diff --git a/src/BLESecurity.h b/src/BLESecurity.h
new file mode 100644
index 0000000..67c41ef
--- /dev/null
+++ b/src/BLESecurity.h
@@ -0,0 +1,71 @@
+/*
+ * BLESecurity.h
+ *
+ * Created on: Dec 17, 2017
+ * Author: chegewara
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLESECURITY_H_
+#define COMPONENTS_CPP_UTILS_BLESECURITY_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <esp_gap_ble_api.h>
+
+class BLESecurity {
+public:
+ BLESecurity();
+ virtual ~BLESecurity();
+ void setAuthenticationMode(esp_ble_auth_req_t auth_req);
+ void setCapability(esp_ble_io_cap_t iocap);
+ void setInitEncryptionKey(uint8_t init_key);
+ void setRespEncryptionKey(uint8_t resp_key);
+ void setKeySize(uint8_t key_size = 16);
+ static char* esp_key_type_to_str(esp_ble_key_type_t key_type);
+
+private:
+ esp_ble_auth_req_t m_authReq;
+ esp_ble_io_cap_t m_iocap;
+ uint8_t m_initKey;
+ uint8_t m_respKey;
+ uint8_t m_keySize;
+}; // BLESecurity
+
+
+/*
+ * @brief Callbacks to handle GAP events related to authorization
+ */
+class BLESecurityCallbacks {
+public:
+ virtual ~BLESecurityCallbacks() {};
+
+ /**
+ * @brief Its request from peer device to input authentication pin code displayed on peer device.
+ * It requires that our device is capable to input 6-digits code by end user
+ * @return Return 6-digits integer value from input device
+ */
+ virtual uint32_t onPassKeyRequest() = 0;
+
+ /**
+ * @brief Provide us 6-digits code to perform authentication.
+ * It requires that our device is capable to display this code to end user
+ * @param
+ */
+ virtual void onPassKeyNotify(uint32_t pass_key);
+
+ /**
+ * @brief Here we can make decision if we want to let negotiate authorization with peer device or not
+ * return Return true if we accept this peer device request
+ */
+
+ virtual bool onSecurityRequest();
+ /**
+ * Provide us information when authentication process is completed
+ */
+ virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t);
+
+ virtual bool onConfirmPIN(uint32_t pin);
+}; // BLESecurityCallbacks
+
+#endif // CONFIG_BT_ENABLED
+#endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_
diff --git a/src/BLEServer.cpp b/src/BLEServer.cpp
index 0d9bd6b..9d26eb5 100644
--- a/src/BLEServer.cpp
+++ b/src/BLEServer.cpp
@@ -8,10 +8,10 @@
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_log.h>
-#include <bt.h>
+#include <esp_bt.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
-#include <esp_gatts_api.h>
+//#include <esp_gatts_api.h>
#include "BLEDevice.h"
#include "BLEServer.h"
#include "BLEService.h"
@@ -151,6 +151,7 @@ void BLEServer::handleGAPEvent(
*/
break;
}
+
default:
break;
}
@@ -178,108 +179,114 @@ void BLEServer::handleGATTServerEvent(
m_serviceMap.handleGATTServerEvent(event, gatts_if, 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
+ //
+ case ESP_GATTS_ADD_CHAR_EVT: {
+ break;
+ } // ESP_GATTS_ADD_CHAR_EVT
// ESP_GATTS_CONNECT_EVT
// connect:
- // - uint16_t conn_id
+ // - uint16_t conn_id
// - esp_bd_addr_t remote_bda
- // - bool is_connected
+ // - bool is_connected
+ //
case ESP_GATTS_CONNECT_EVT: {
m_connId = param->connect.conn_id; // Save the connection id.
if (m_pServerCallbacks != nullptr) {
m_pServerCallbacks->onConnect(this);
}
- m_connectedCount++;
+ m_connectedCount++; // Increment the number of connected devices count.
break;
} // ESP_GATTS_CONNECT_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_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_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);
m_serviceMap.setByHandle(param->create.service_handle, pService);
- //pService->setHandle(param->create.service_handle);
m_semaphoreCreateEvt.give();
break;
} // ESP_GATTS_CREATE_EVT
+ // ESP_GATTS_DISCONNECT_EVT
+ //
+ // disconnect
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ // - bool is_connected
+ //
+ // 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()
+ 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
+ // - 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
+ // - 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
+ // - 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
-
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool need_rsp
+ // - bool is_prep
+ // - uint16_t len
+ // - uint8_t* value
+ //
case ESP_GATTS_WRITE_EVT: {
break;
}
- // ESP_GATTS_DISCONNECT_EVT
- // 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--;
- if (m_pServerCallbacks != nullptr) {
- m_pServerCallbacks->onDisconnect(this);
- }
- startAdvertising();
- break;
- } // ESP_GATTS_DISCONNECT_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: {
- break;
- } // ESP_GATTS_ADD_CHAR_EVT
-
-
default: {
break;
}
@@ -303,7 +310,7 @@ void BLEServer::registerApp() {
/**
- * @brief Set the callbacks.
+ * @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
@@ -329,36 +336,16 @@ void BLEServer::startAdvertising() {
} // startAdvertising
-/*
-void BLEServer::addCharacteristic(BLECharacteristic *characteristic, BLEService *pService) {
- ESP_LOGD(tag, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s, serviceHandle=0x%.2x",
- characteristic->m_bleUUID.toString().c_str(),
- pService->getHandle());
-
- m_characteristicMap.setByUUID(characteristic->m_bleUUID, characteristic);
-
- esp_err_t errRc = ::esp_ble_gatts_add_char(
- pService->getHandle(),
- characteristic->getUUID().getNative(),
- (esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
- characteristic->getProperties(),
- &characteristic->m_value,
- NULL);
-
- if (errRc != ESP_OK) {
- ESP_LOGE(tag, "esp_ble_gatts_add_char: rc=%d %s", errRc, espToString(errRc));
- return;
- }
-}
-*/
-
void BLEServerCallbacks::onConnect(BLEServer* pServer) {
ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default");
+ ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
ESP_LOGD("BLEServerCallbacks", "<< onConnect()");
} // onConnect
+
void BLEServerCallbacks::onDisconnect(BLEServer* pServer) {
ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default");
+ ESP_LOGD("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str());
ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()");
} // onDisconnect
diff --git a/src/BLEServer.h b/src/BLEServer.h
index 64b34b4..bc0ef05 100644
--- a/src/BLEServer.h
+++ b/src/BLEServer.h
@@ -18,6 +18,7 @@
#include "BLEAdvertising.h"
#include "BLECharacteristic.h"
#include "BLEService.h"
+#include "BLESecurity.h"
#include "FreeRTOS.h"
class BLEServerCallbacks;
@@ -55,7 +56,7 @@ public:
BLEService* createService(const char* uuid);
BLEService* createService(BLEUUID uuid, uint32_t numHandles=15);
BLEAdvertising* getAdvertising();
- void setCallbacks(BLEServerCallbacks *pCallbacks);
+ void setCallbacks(BLEServerCallbacks* pCallbacks);
void startAdvertising();
diff --git a/src/BLEService.h b/src/BLEService.h
index b37092f..2b78e62 100644
--- a/src/BLEService.h
+++ b/src/BLEService.h
@@ -52,9 +52,6 @@ private:
*/
class BLEService {
public:
- BLEService(const char* uuid, uint32_t numHandles=10);
- BLEService(BLEUUID uuid, uint32_t numHandles=10);
-
void addCharacteristic(BLECharacteristic* pCharacteristic);
BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties);
BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties);
@@ -69,6 +66,8 @@ public:
uint16_t getHandle();
private:
+ BLEService(const char* uuid, uint32_t numHandles);
+ BLEService(BLEUUID uuid, uint32_t numHandles);
friend class BLEServer;
friend class BLEServiceMap;
friend class BLEDescriptor;
@@ -80,8 +79,7 @@ private:
BLECharacteristic* m_lastCreatedCharacteristic;
BLEServer* m_pServer;
BLEUUID m_uuid;
- char deleteMe[10];
- //FreeRTOS::Semaphore m_serializeMutex;
+
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt");
diff --git a/src/BLEUUID.cpp b/src/BLEUUID.cpp
index f5c6cee..9ca7cdd 100644
--- a/src/BLEUUID.cpp
+++ b/src/BLEUUID.cpp
@@ -11,6 +11,8 @@
#include <sstream>
#include <iomanip>
#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
#include "BLEUUID.h"
static const char* LOG_TAG = "BLEUUID";
@@ -189,6 +191,32 @@ BLEUUID::BLEUUID() {
/**
+ * @brief Get the number of bits in this uuid.
+ * @return The number of bits in the UUID. One of 16, 32 or 128.
+ */
+int BLEUUID::bitSize() {
+ if (m_valueSet == false) {
+ return 0;
+ }
+ switch(m_uuid.len) {
+ case ESP_UUID_LEN_16: {
+ return 16;
+ }
+ case ESP_UUID_LEN_32: {
+ return 32;
+ }
+ case ESP_UUID_LEN_128: {
+ return 128;
+ }
+ default: {
+ ESP_LOGE(LOG_TAG, "Unknown UUID length: %d", m_uuid.len);
+ return 0;
+ }
+ } // End of switch
+} // bitSize
+
+
+/**
* @brief Compare a UUID against this UUID.
*
* @param [in] uuid The UUID to compare against.
@@ -217,6 +245,35 @@ bool BLEUUID::equals(BLEUUID uuid) {
/**
+ * Create a BLEUUID from a string of the form:
+ * 0xNNNN
+ * 0xNNNNNNNN
+ * 0x<UUID>
+ * NNNN
+ * NNNNNNNN
+ * <UUID>
+ */
+BLEUUID BLEUUID::fromString(std::string _uuid){
+ uint8_t start = 0;
+ if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
+ start = 2;
+ }
+ uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use.
+
+ if( len == 4) {
+ uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
+ return BLEUUID(x);
+ } else if (len == 8) {
+ uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
+ return BLEUUID(x);
+ } else if (len == 36) {
+ return BLEUUID(_uuid);
+ }
+ return BLEUUID();
+} // fromString
+
+
+/**
* @brief Get the native UUID value.
*
* @return The native UUID value or NULL if not set.
@@ -350,21 +407,4 @@ std::string BLEUUID::toString() {
return ss.str();
} // toString
-BLEUUID BLEUUID::fromString(std::string _uuid){
- uint8_t start = 0;
- if(strstr(_uuid.c_str(), "0x") != nullptr){
- start = 2;
- }
- uint8_t len = _uuid.length() - start;
- if(len == 4 ){
- uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
- return BLEUUID(x);
- }else if(len == 8){
- uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
- return BLEUUID(x);
- }else if (len == 36){
- return BLEUUID(_uuid);
- }
- return BLEUUID();
-}
#endif /* CONFIG_BT_ENABLED */
diff --git a/src/BLEUUID.h b/src/BLEUUID.h
index bbe9b87..5fb7795 100644
--- a/src/BLEUUID.h
+++ b/src/BLEUUID.h
@@ -24,11 +24,12 @@ public:
BLEUUID(uint8_t* pData, size_t size, bool msbFirst);
BLEUUID(esp_gatt_id_t gattId);
BLEUUID();
+ int bitSize(); // Get the number of bits in this uuid.
bool equals(BLEUUID uuid);
esp_bt_uuid_t* getNative();
BLEUUID to128();
std::string toString();
- static BLEUUID fromString(std::string uuid);
+ static BLEUUID fromString(std::string uuid); // Create a BLEUUID from a string
private:
esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps.
diff --git a/src/BLEUtils.cpp b/src/BLEUtils.cpp
index 5d1dec9..ff4ebfa 100644
--- a/src/BLEUtils.cpp
+++ b/src/BLEUtils.cpp
@@ -14,7 +14,7 @@
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
-#include <bt.h> // ESP32 BLE
+#include <esp_bt.h> // ESP32 BLE
#include <esp_bt_main.h> // ESP32 BLE
#include <esp_gap_ble_api.h> // ESP32 BLE
#include <esp_gattc_api.h> // ESP32 BLE
@@ -838,28 +838,52 @@ std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) {
} // buildPrintData
+/**
+ * @brief Convert a close/disconnect reason to a string.
+ * @param [in] reason The close reason.
+ * @return A string representation of the reason.
+ */
std::string BLEUtils::gattCloseReasonToString(esp_gatt_conn_reason_t reason) {
switch(reason) {
- case ESP_GATT_CONN_UNKNOWN:
+ case ESP_GATT_CONN_UNKNOWN: {
return "ESP_GATT_CONN_UNKNOWN";
- case ESP_GATT_CONN_L2C_FAILURE:
+ }
+
+ case ESP_GATT_CONN_L2C_FAILURE: {
return "ESP_GATT_CONN_L2C_FAILURE";
- case ESP_GATT_CONN_TIMEOUT:
+ }
+
+ case ESP_GATT_CONN_TIMEOUT: {
return "ESP_GATT_CONN_TIMEOUT";
- case ESP_GATT_CONN_TERMINATE_PEER_USER:
+ }
+
+ case ESP_GATT_CONN_TERMINATE_PEER_USER: {
return "ESP_GATT_CONN_TERMINATE_PEER_USER";
- case ESP_GATT_CONN_TERMINATE_LOCAL_HOST:
+ }
+
+ case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: {
return "ESP_GATT_CONN_TERMINATE_LOCAL_HOST";
- case ESP_GATT_CONN_FAIL_ESTABLISH:
+ }
+
+ case ESP_GATT_CONN_FAIL_ESTABLISH: {
return "ESP_GATT_CONN_FAIL_ESTABLISH";
- case ESP_GATT_CONN_LMP_TIMEOUT:
+ }
+
+ case ESP_GATT_CONN_LMP_TIMEOUT: {
return "ESP_GATT_CONN_LMP_TIMEOUT";
- case ESP_GATT_CONN_CONN_CANCEL:
+ }
+
+ case ESP_GATT_CONN_CONN_CANCEL: {
return "ESP_GATT_CONN_CONN_CANCEL";
- case ESP_GATT_CONN_NONE:
+ }
+
+ case ESP_GATT_CONN_NONE: {
return "ESP_GATT_CONN_NONE";
- default:
+ }
+
+ default: {
return "Unknown";
+ }
}
} // gattCloseReasonToString
@@ -1264,6 +1288,15 @@ void BLEUtils::dumpGapEvent(
//
+ // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT
+ //
+ case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_raw_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT
+
+
+ //
// ESP_GAP_BLE_SCAN_START_COMPLETE_EVT
//
// scan_start_cmpl
@@ -1379,11 +1412,12 @@ void BLEUtils::dumpGattClientEvent(
// ESP_GATTC_DISCONNECT_EVT
//
// disconnect:
- // - esp_gatt_status_t status
- // - uint16_t conn_id
- // - esp_bd_addr_t remote_bda
+ // - esp_gatt_conn_reason_t reason
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
case ESP_GATTC_DISCONNECT_EVT: {
- ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]",
+ ESP_LOGD(LOG_TAG, "[reason: %s, conn_id: %d, remote_bda: %s]",
+ BLEUtils::gattCloseReasonToString(evtParam->disconnect.reason).c_str(),
evtParam->disconnect.conn_id,
BLEAddress(evtParam->disconnect.remote_bda).toString().c_str()
);
@@ -1577,13 +1611,15 @@ void BLEUtils::dumpGattClientEvent(
// - esp_gatt_status_t status
// - uint16_t conn_id
// - uint16_t handle
+ // - uint16_t offset
//
case ESP_GATTC_WRITE_CHAR_EVT: {
- ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x]",
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, offset: %d]",
BLEUtils::gattStatusToString(evtParam->write.status).c_str(),
evtParam->write.conn_id,
evtParam->write.handle,
- evtParam->write.handle
+ evtParam->write.handle,
+ evtParam->write.offset
);
break;
} // ESP_GATTC_WRITE_CHAR_EVT
diff --git a/src/FreeRTOS.cpp b/src/FreeRTOS.cpp
index 9239042..1ae01d7 100644
--- a/src/FreeRTOS.cpp
+++ b/src/FreeRTOS.cpp
@@ -133,9 +133,9 @@ void FreeRTOS::Semaphore::give() {
} else {
xSemaphoreGive(m_semaphore);
}
-#ifdef ARDUINO_ARCH_ESP32
- FreeRTOS::sleep(10);
-#endif
+// #ifdef ARDUINO_ARCH_ESP32
+// FreeRTOS::sleep(10);
+// #endif
m_owner = std::string("<N/A>");
} // Semaphore::give
@@ -168,17 +168,25 @@ void FreeRTOS::Semaphore::giveFromISR() {
/**
* @brief Take a semaphore.
* Take a semaphore and wait indefinitely.
+ * @param [in] owner The new owner (for debugging)
+ * @return True if we took the semaphore.
*/
-void FreeRTOS::Semaphore::take(std::string owner)
+bool FreeRTOS::Semaphore::take(std::string owner)
{
ESP_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);
} else {
- xSemaphoreTake(m_semaphore, portMAX_DELAY);
+ rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY);
}
m_owner = owner;
- ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
+ if (rc) {
+ ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
+ } else {
+ ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
+ }
+ return rc;
} // Semaphore::take
@@ -186,21 +194,33 @@ void FreeRTOS::Semaphore::take(std::string owner)
* @brief Take a semaphore.
* Take a semaphore but return if we haven't obtained it in the given period of milliseconds.
* @param [in] timeoutMs Timeout in milliseconds.
+ * @param [in] owner The new owner (for debugging)
+ * @return True if we took the semaphore.
*/
-void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
+bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
-
+ bool rc = false;
if (m_usePthreads) {
- assert(false);
+ assert(false); // We apparently don't have a timed wait for pthreads.
} else {
- xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS);
+ rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS);
}
m_owner = owner;
- ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str());
+ if (rc) {
+ ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str());
+ } else {
+ ESP_LOGE(LOG_TAG, "Semaphore NOT taken: %s", toString().c_str());
+ }
+ return rc;
} // Semaphore::take
+
+/**
+ * @brief Create a string representation of the semaphore.
+ * @return A string representation of the semaphore.
+ */
std::string FreeRTOS::Semaphore::toString() {
std::stringstream stringStream;
stringStream << "name: "<< m_name << " (0x" << std::hex << std::setfill('0') << (uint32_t)m_semaphore << "), owner: " << m_owner;
@@ -208,6 +228,10 @@ std::string FreeRTOS::Semaphore::toString() {
} // toString
+/**
+ * @brief Set the name of the semaphore.
+ * @param [in] name The name of the semaphore.
+ */
void FreeRTOS::Semaphore::setName(std::string name) {
m_name = name;
} // setName
diff --git a/src/FreeRTOS.h b/src/FreeRTOS.h
index 43a3b8f..ab0e83d 100644
--- a/src/FreeRTOS.h
+++ b/src/FreeRTOS.h
@@ -36,8 +36,8 @@ public:
void give(uint32_t value);
void giveFromISR();
void setName(std::string name);
- void take(std::string owner="<Unknown>");
- void take(uint32_t timeoutMs, std::string owner="<Unknown>");
+ bool take(std::string owner="<Unknown>");
+ bool take(uint32_t timeoutMs, std::string owner="<Unknown>");
std::string toString();
uint32_t wait(std::string owner="<Unknown>");
diff --git a/src/GeneralUtils.cpp b/src/GeneralUtils.cpp
index e8eb3a0..ccf74f7 100644
--- a/src/GeneralUtils.cpp
+++ b/src/GeneralUtils.cpp
@@ -17,6 +17,8 @@
#include <esp_err.h>
#include <nvs.h>
#include <esp_wifi.h>
+#include <esp_heap_caps.h>
+#include <esp_system.h>
static const char* LOG_TAG = "GeneralUtils";
@@ -99,6 +101,23 @@ bool GeneralUtils::base64Encode(const std::string &in, std::string *out) {
/**
+ * @brief Dump general info to the log.
+ * Data includes:
+ * * Amount of free RAM
+ */
+void GeneralUtils::dumpInfo() {
+ size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+ esp_chip_info_t chipInfo;
+ esp_chip_info(&chipInfo);
+ ESP_LOGD(LOG_TAG, "--- dumpInfo ---");
+ ESP_LOGD(LOG_TAG, "Free heap: %d", freeHeap);
+ ESP_LOGD(LOG_TAG, "Chip Info: Model: %d, cores: %d, revision: %d", chipInfo.model, chipInfo.cores, chipInfo.revision);
+ ESP_LOGD(LOG_TAG, "ESP-IDF version: %s", esp_get_idf_version());
+ ESP_LOGD(LOG_TAG, "---");
+} // dumpInfo
+
+
+/**
* @brief Does the string end with a specific character?
* @param [in] str The string to examine.
* @param [in] c The character to look form.
@@ -321,6 +340,24 @@ std::string GeneralUtils::ipToString(uint8_t *ip) {
/**
+ * @brief Split a string into parts based on a delimiter.
+ * @param [in] source The source string to split.
+ * @param [in] delimiter The delimiter characters.
+ * @return A vector of strings that are the split of the input.
+ */
+std::vector<std::string> GeneralUtils::split(std::string source, char delimiter) {
+ // See also: https://stackoverflow.com/questions/5167625/splitting-a-c-stdstring-using-tokens-e-g
+ std::vector<std::string> strings;
+ std::istringstream iss(source);
+ std::string s;
+ while(std::getline(iss, s, delimiter)) {
+ strings.push_back(trim(s));
+ }
+ return strings;
+} // split
+
+
+/**
* @brief Convert an ESP error code to a string.
* @param [in] errCode The errCode to be converted.
* @return A string representation of the error code.
@@ -399,8 +436,30 @@ const char* GeneralUtils::errorToString(esp_err_t errCode) {
/**
- * @brief Restart the ESP32.
+ * @brief Convert a string to lower case.
+ * @param [in] value The string to convert to lower case.
+ * @return A lower case representation of the string.
+ */
+std::string GeneralUtils::toLower(std::string& value) {
+ // Question: Could this be improved with a signature of:
+ // std::string& GeneralUtils::toLower(std::string& value)
+ std::transform(value.begin(), value.end(), value.begin(), ::tolower);
+ return value;
+} // toLower
+
+
+/**
+ * @brief Remove white space from a string.
*/
-void GeneralUtils::restart() {
- esp_restart();
-} // restart
+std::string GeneralUtils::trim(const std::string& str)
+{
+ size_t first = str.find_first_not_of(' ');
+ if (std::string::npos == first)
+ {
+ return str;
+ }
+ size_t last = str.find_last_not_of(' ');
+ return str.substr(first, (last - first + 1));
+} // trim
+
+
diff --git a/src/GeneralUtils.h b/src/GeneralUtils.h
index 2d55abf..013953d 100644
--- a/src/GeneralUtils.h
+++ b/src/GeneralUtils.h
@@ -10,6 +10,8 @@
#include <stdint.h>
#include <string>
#include <esp_err.h>
+#include <algorithm>
+#include <vector>
/**
* @brief General utilities.
@@ -18,11 +20,15 @@ class GeneralUtils {
public:
static bool base64Decode(const std::string& in, std::string* out);
static bool base64Encode(const std::string& in, std::string* out);
+ static void dumpInfo();
static bool endsWith(std::string str, char c);
static const char* errorToString(esp_err_t errCode);
static void hexDump(const uint8_t* pData, uint32_t length);
static std::string ipToString(uint8_t* ip);
- static void restart();
+ static std::vector<std::string> split(std::string source, char delimiter);
+ static std::string toLower(std::string& value);
+ static std::string trim(const std::string& str);
+
};
#endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */