summaryrefslogtreecommitdiff
path: root/sensor/patchedBLE/src
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2018-08-03 12:23:17 +0100
committerdakkar <dakkar@thenautilus.net>2018-08-03 12:23:17 +0100
commitbb00c2da73b6ab8837f50c9f889ee151f8abc036 (patch)
tree3922eb826386260568ccaa1da7056c141f752a97 /sensor/patchedBLE/src
parentdon't print from callback, there's races (diff)
parentUpload of 0.4.16 (diff)
downloadthermostat-bb00c2da73b6ab8837f50c9f889ee151f8abc036.tar.gz
thermostat-bb00c2da73b6ab8837f50c9f889ee151f8abc036.tar.bz2
thermostat-bb00c2da73b6ab8837f50c9f889ee151f8abc036.zip
Add 'sensor/patchedBLE/' from commit '7951347ed68313d75c367e1f2cce763cb56d1eb2'
git-subtree-dir: sensor/patchedBLE git-subtree-mainline: 27e209cf58abeda1dc110777f99a98804a13fdbf git-subtree-split: 7951347ed68313d75c367e1f2cce763cb56d1eb2
Diffstat (limited to 'sensor/patchedBLE/src')
-rw-r--r--sensor/patchedBLE/src/BLE2902.cpp69
-rw-r--r--sensor/patchedBLE/src/BLE2902.h34
-rw-r--r--sensor/patchedBLE/src/BLE2904.cpp74
-rw-r--r--sensor/patchedBLE/src/BLE2904.h74
-rw-r--r--sensor/patchedBLE/src/BLEAddress.cpp96
-rw-r--r--sensor/patchedBLE/src/BLEAddress.h34
-rw-r--r--sensor/patchedBLE/src/BLEAdvertisedDevice.cpp520
-rw-r--r--sensor/patchedBLE/src/BLEAdvertisedDevice.h120
-rw-r--r--sensor/patchedBLE/src/BLEAdvertising.cpp462
-rw-r--r--sensor/patchedBLE/src/BLEAdvertising.h68
-rw-r--r--sensor/patchedBLE/src/BLEBeacon.cpp84
-rw-r--r--sensor/patchedBLE/src/BLEBeacon.h43
-rw-r--r--sensor/patchedBLE/src/BLECharacteristic.cpp793
-rw-r--r--sensor/patchedBLE/src/BLECharacteristic.h140
-rw-r--r--sensor/patchedBLE/src/BLECharacteristicMap.cpp143
-rw-r--r--sensor/patchedBLE/src/BLEClient.cpp471
-rw-r--r--sensor/patchedBLE/src/BLEClient.h98
-rw-r--r--sensor/patchedBLE/src/BLEDescriptor.cpp342
-rw-r--r--sensor/patchedBLE/src/BLEDescriptor.h77
-rw-r--r--sensor/patchedBLE/src/BLEDescriptorMap.cpp152
-rw-r--r--sensor/patchedBLE/src/BLEDevice.cpp548
-rw-r--r--sensor/patchedBLE/src/BLEDevice.h74
-rw-r--r--sensor/patchedBLE/src/BLEExceptions.cpp9
-rw-r--r--sensor/patchedBLE/src/BLEExceptions.h31
-rw-r--r--sensor/patchedBLE/src/BLEHIDDevice.cpp234
-rw-r--r--sensor/patchedBLE/src/BLEHIDDevice.h75
-rw-r--r--sensor/patchedBLE/src/BLERemoteCharacteristic.cpp606
-rw-r--r--sensor/patchedBLE/src/BLERemoteCharacteristic.h85
-rw-r--r--sensor/patchedBLE/src/BLERemoteDescriptor.cpp185
-rw-r--r--sensor/patchedBLE/src/BLERemoteDescriptor.h55
-rw-r--r--sensor/patchedBLE/src/BLERemoteService.cpp328
-rw-r--r--sensor/patchedBLE/src/BLERemoteService.h85
-rw-r--r--sensor/patchedBLE/src/BLEScan.cpp292
-rw-r--r--sensor/patchedBLE/src/BLEScan.h79
-rw-r--r--sensor/patchedBLE/src/BLESecurity.cpp105
-rw-r--r--sensor/patchedBLE/src/BLESecurity.h71
-rw-r--r--sensor/patchedBLE/src/BLEServer.cpp361
-rw-r--r--sensor/patchedBLE/src/BLEServer.h121
-rw-r--r--sensor/patchedBLE/src/BLEService.cpp435
-rw-r--r--sensor/patchedBLE/src/BLEService.h104
-rw-r--r--sensor/patchedBLE/src/BLEServiceMap.cpp132
-rw-r--r--sensor/patchedBLE/src/BLEUUID.cpp410
-rw-r--r--sensor/patchedBLE/src/BLEUUID.h39
-rw-r--r--sensor/patchedBLE/src/BLEUtils.cpp2172
-rw-r--r--sensor/patchedBLE/src/BLEUtils.h63
-rw-r--r--sensor/patchedBLE/src/BLEValue.cpp142
-rw-r--r--sensor/patchedBLE/src/BLEValue.h38
-rw-r--r--sensor/patchedBLE/src/FreeRTOS.cpp286
-rw-r--r--sensor/patchedBLE/src/FreeRTOS.h70
-rw-r--r--sensor/patchedBLE/src/GeneralUtils.cpp465
-rw-r--r--sensor/patchedBLE/src/GeneralUtils.h34
-rw-r--r--sensor/patchedBLE/src/HIDKeyboardTypes.h402
-rw-r--r--sensor/patchedBLE/src/HIDTypes.h96
53 files changed, 12126 insertions, 0 deletions
diff --git a/sensor/patchedBLE/src/BLE2902.cpp b/sensor/patchedBLE/src/BLE2902.cpp
new file mode 100644
index 0000000..8984da8
--- /dev/null
+++ b/sensor/patchedBLE/src/BLE2902.cpp
@@ -0,0 +1,69 @@
+/*
+ * BLE2902.cpp
+ *
+ * Created on: Jun 25, 2017
+ * Author: kolban
+ */
+
+/*
+ * See also:
+ * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLE2902.h"
+
+BLE2902::BLE2902() : BLEDescriptor(BLEUUID((uint16_t) 0x2902)) {
+ uint8_t data[2] = {0,0};
+ setValue(data, 2);
+} // BLE2902
+
+
+/**
+ * @brief Get the notifications value.
+ * @return The notifications value. True if notifications are enabled and false if not.
+ */
+bool BLE2902::getNotifications() {
+ return (getValue()[0] & (1 << 0)) != 0;
+} // getNotifications
+
+
+/**
+ * @brief Get the indications value.
+ * @return The indications value. True if indications are enabled and false if not.
+ */
+bool BLE2902::getIndications() {
+ return (getValue()[0] & (1 << 1)) != 0;
+} // getIndications
+
+
+/**
+ * @brief Set the indications flag.
+ * @param [in] flag The indications flag.
+ */
+void BLE2902::setIndications(bool flag) {
+ uint8_t *pValue = getValue();
+ if (flag) {
+ pValue[0] |= 1<<1;
+ } else {
+ pValue[0] &= ~(1<<1);
+ }
+} // setIndications
+
+
+/**
+ * @brief Set the notifications flag.
+ * @param [in] flag The notifications flag.
+ */
+void BLE2902::setNotifications(bool flag) {
+ uint8_t *pValue = getValue();
+ if (flag) {
+ pValue[0] |= 1<<0;
+ } else {
+ pValue[0] &= ~(1<<0);
+ }
+} // setNotifications
+
+
+#endif
diff --git a/sensor/patchedBLE/src/BLE2902.h b/sensor/patchedBLE/src/BLE2902.h
new file mode 100644
index 0000000..397360a
--- /dev/null
+++ b/sensor/patchedBLE/src/BLE2902.h
@@ -0,0 +1,34 @@
+/*
+ * BLE2902.h
+ *
+ * Created on: Jun 25, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLE2902_H_
+#define COMPONENTS_CPP_UTILS_BLE2902_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLEDescriptor.h"
+
+/**
+ * @brief Descriptor for Client Characteristic Configuration.
+ *
+ * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
+ *
+ * See also:
+ * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
+ */
+class BLE2902: public BLEDescriptor {
+public:
+ BLE2902();
+ bool getNotifications();
+ bool getIndications();
+ void setNotifications(bool flag);
+ void setIndications(bool flag);
+
+}; // BLE2902
+
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */
diff --git a/sensor/patchedBLE/src/BLE2904.cpp b/sensor/patchedBLE/src/BLE2904.cpp
new file mode 100644
index 0000000..4423ed8
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLE2904.h b/sensor/patchedBLE/src/BLE2904.h
new file mode 100644
index 0000000..cb337e2
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLEAddress.cpp b/sensor/patchedBLE/src/BLEAddress.cpp
new file mode 100644
index 0000000..895feda
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAddress.cpp
@@ -0,0 +1,96 @@
+/*
+ * BLEAddress.cpp
+ *
+ * Created on: Jul 2, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "BLEAddress.h"
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <string.h>
+#include <stdio.h>
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+
+/**
+ * @brief Create an address from the native ESP32 representation.
+ * @param [in] address The native representation.
+ */
+BLEAddress::BLEAddress(esp_bd_addr_t address) {
+ memcpy(m_address, address, ESP_BD_ADDR_LEN);
+} // BLEAddress
+
+
+/**
+ * @brief Create an address from a hex string
+ *
+ * A hex string is of the format:
+ * ```
+ * 00:00:00:00:00:00
+ * ```
+ * which is 17 characters in length.
+ *
+ * @param [in] stringAddress The hex representation of the address.
+ */
+BLEAddress::BLEAddress(std::string stringAddress) {
+ if (stringAddress.length() != 17) {
+ return;
+ }
+ int data[6];
+ sscanf(stringAddress.c_str(), "%x:%x:%x:%x:%x:%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]);
+ m_address[0] = (uint8_t)data[0];
+ m_address[1] = (uint8_t)data[1];
+ m_address[2] = (uint8_t)data[2];
+ m_address[3] = (uint8_t)data[3];
+ m_address[4] = (uint8_t)data[4];
+ m_address[5] = (uint8_t)data[5];
+} // BLEAddress
+
+
+/**
+ * @brief Determine if this address equals another.
+ * @param [in] otherAddress The other address to compare against.
+ * @return True if the addresses are equal.
+ */
+bool BLEAddress::equals(BLEAddress otherAddress) {
+ return memcmp(otherAddress.getNative(), m_address, 6) == 0;
+} // equals
+
+
+/**
+ * @brief Return the native representation of the address.
+ * @return The native representation of the address.
+ */
+esp_bd_addr_t *BLEAddress::getNative() {
+ return &m_address;
+} // getNative
+
+
+/**
+ * @brief Convert a BLE address to a string.
+ *
+ * A string representation of an address is in the format:
+ *
+ * ```
+ * xx:xx:xx:xx:xx:xx
+ * ```
+ *
+ * @return The string representation of the address.
+ */
+std::string BLEAddress::toString() {
+ std::stringstream stream;
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[0] << ':';
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[1] << ':';
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[2] << ':';
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[3] << ':';
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[4] << ':';
+ stream << std::setfill('0') << std::setw(2) << std::hex << (int)((uint8_t *)(m_address))[5];
+ return stream.str();
+} // toString
+#endif
diff --git a/sensor/patchedBLE/src/BLEAddress.h b/sensor/patchedBLE/src/BLEAddress.h
new file mode 100644
index 0000000..7eff4da
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAddress.h
@@ -0,0 +1,34 @@
+/*
+ * BLEAddress.h
+ *
+ * Created on: Jul 2, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_
+#define COMPONENTS_CPP_UTILS_BLEADDRESS_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gap_ble_api.h> // ESP32 BLE
+#include <string>
+
+
+/**
+ * @brief A %BLE device address.
+ *
+ * Every %BLE device has a unique address which can be used to identify it and form connections.
+ */
+class BLEAddress {
+public:
+ BLEAddress(esp_bd_addr_t address);
+ BLEAddress(std::string stringAddress);
+ bool equals(BLEAddress otherAddress);
+ esp_bd_addr_t* getNative();
+ std::string toString();
+
+private:
+ esp_bd_addr_t m_address;
+};
+
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */
diff --git a/sensor/patchedBLE/src/BLEAdvertisedDevice.cpp b/sensor/patchedBLE/src/BLEAdvertisedDevice.cpp
new file mode 100644
index 0000000..67603df
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAdvertisedDevice.cpp
@@ -0,0 +1,520 @@
+/*
+ * BLEAdvertisedDevice.cpp
+ *
+ * During the scanning procedure, we will be finding advertised BLE devices. This class
+ * models a found device.
+ *
+ *
+ * See also:
+ * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+ *
+ * Created on: Jul 3, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_log.h>
+#include <sstream>
+#include "BLEAdvertisedDevice.h"
+#include "BLEUtils.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+static const char* LOG_TAG="BLEAdvertisedDevice";
+
+BLEAdvertisedDevice::BLEAdvertisedDevice() {
+ m_adFlag = 0;
+ m_appearance = 0;
+ m_deviceType = 0;
+ m_manufacturerData = "";
+ m_name = "";
+ m_rssi = -9999;
+ m_serviceData = "";
+ m_txPower = 0;
+ m_pScan = nullptr;
+
+ m_haveAppearance = false;
+ m_haveManufacturerData = false;
+ m_haveName = false;
+ m_haveRSSI = false;
+ m_haveServiceData = false;
+ m_haveServiceUUID = false;
+ m_haveTXPower = false;
+
+} // BLEAdvertisedDevice
+
+
+/**
+ * @brief Get the address.
+ *
+ * Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
+ * Call this function to obtain the address of the advertised device.
+ *
+ * @return The address of the advertised device.
+ */
+BLEAddress BLEAdvertisedDevice::getAddress() {
+ return m_address;
+} // getAddress
+
+
+/**
+ * @brief Get the appearance.
+ *
+ * A %BLE device can declare its own appearance. The appearance is how it would like to be shown to an end user
+ * typcially in the form of an icon.
+ *
+ * @return The appearance of the advertised device.
+ */
+uint16_t BLEAdvertisedDevice::getAppearance() {
+ return m_appearance;
+} // getAppearance
+
+
+/**
+ * @brief Get the manufacturer data.
+ * @return The manufacturer data of the advertised device.
+ */
+std::string BLEAdvertisedDevice::getManufacturerData() {
+ return m_manufacturerData;
+} // getManufacturerData
+
+
+/**
+ * @brief Get the name.
+ * @return The name of the advertised device.
+ */
+std::string BLEAdvertisedDevice::getName() {
+ return m_name;
+} // getName
+
+
+/**
+ * @brief Get the RSSI.
+ * @return The RSSI of the advertised device.
+ */
+int BLEAdvertisedDevice::getRSSI() {
+ return m_rssi;
+} // getRSSI
+
+
+/**
+ * @brief Get the scan object that created this advertisement.
+ * @return The scan object.
+ */
+BLEScan* BLEAdvertisedDevice::getScan() {
+ return m_pScan;
+} // 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.
+ */
+BLEUUID BLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
+ return m_serviceUUIDs[0];
+} // getServiceUUID
+
+/**
+ * @brief Check advertised serviced for existence required UUID
+ * @return Return true if service is advertised
+ */
+bool BLEAdvertisedDevice::isAdvertisingService(BLEUUID uuid){
+ for (int i = 0; i < m_serviceUUIDs.size(); ++i) {
+ if(m_serviceUUIDs[i].equals(uuid))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * @brief Get the TX Power.
+ * @return The TX Power of the advertised device.
+ */
+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.
+ */
+bool BLEAdvertisedDevice::haveAppearance() {
+ return m_haveAppearance;
+} // haveAppearance
+
+
+/**
+ * @brief Does this advertisement have manufacturer data?
+ * @return True if there is manufacturer data present.
+ */
+bool BLEAdvertisedDevice::haveManufacturerData() {
+ return m_haveManufacturerData;
+} // haveManufacturerData
+
+
+/**
+ * @brief Does this advertisement have a name value?
+ * @return True if there is a name value present.
+ */
+bool BLEAdvertisedDevice::haveName() {
+ return m_haveName;
+} // haveName
+
+
+/**
+ * @brief Does this advertisement have a signal strength value?
+ * @return True if there is a signal strength value present.
+ */
+bool BLEAdvertisedDevice::haveRSSI() {
+ return m_haveRSSI;
+} // 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.
+ */
+bool BLEAdvertisedDevice::haveServiceUUID() {
+ return m_haveServiceUUID;
+} // haveServiceUUID
+
+
+/**
+ * @brief Does this advertisement have a transmission power value?
+ * @return True if there is a transmission power value present.
+ */
+bool BLEAdvertisedDevice::haveTXPower() {
+ return m_haveTXPower;
+} // haveTXPower
+
+
+/**
+ * @brief Parse the advertising pay load.
+ *
+ * The pay load is a buffer of bytes that is either 31 bytes long or terminated by
+ * a 0 length value. Each entry in the buffer has the format:
+ * [length][type][data...]
+ *
+ * The length does not include itself but does include everything after it until the next record. A record
+ * with a length value of 0 indicates a terminator.
+ *
+ * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+ */
+void BLEAdvertisedDevice::parseAdvertisement(uint8_t* payload) {
+ uint8_t length;
+ uint8_t ad_type;
+ uint8_t sizeConsumed = 0;
+ bool finished = false;
+ setPayload(payload);
+
+ while(!finished) {
+ 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.
+ ad_type = *payload;
+ payload++;
+ length--;
+
+ char* pHex = BLEUtils::buildHexData(nullptr, payload, length);
+ ESP_LOGD(LOG_TAG, "Type: 0x%.2x (%s), length: %d, data: %s",
+ ad_type, BLEUtils::advTypeToString(ad_type), length, pHex);
+ free(pHex);
+
+ switch(ad_type) {
+ 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
+ setTXPower(*payload);
+ break;
+ } // ESP_BLE_AD_TYPE_TX_PWR
+
+ case ESP_BLE_AD_TYPE_APPEARANCE: { // Adv Data Type: 0x19
+ setAppearance(*reinterpret_cast<uint16_t*>(payload));
+ break;
+ } // ESP_BLE_AD_TYPE_APPEARANCE
+
+ 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
+ for (int var = 0; var < length/2; ++var) {
+ setServiceUUID(BLEUUID(*reinterpret_cast<uint16_t*>(payload+var*2)));
+ }
+ break;
+ } // ESP_BLE_AD_TYPE_16SRV_PART
+
+ case ESP_BLE_AD_TYPE_32SRV_CMPL:
+ 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)));
+ }
+ break;
+ } // ESP_BLE_AD_TYPE_32SRV_PART
+
+ case ESP_BLE_AD_TYPE_128SRV_CMPL: { // Adv Data Type: 0x07
+ setServiceUUID(BLEUUID(payload, 16, false));
+ break;
+ } // ESP_BLE_AD_TYPE_128SRV_CMPL
+
+ case ESP_BLE_AD_TYPE_128SRV_PART: { // Adv Data Type: 0x06
+ setServiceUUID(BLEUUID(payload, 16, false));
+ break;
+ } // ESP_BLE_AD_TYPE_128SRV_PART
+
+ // See CSS Part A 1.4 Manufacturer Specific Data
+ case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: {
+ setManufacturerData(std::string(reinterpret_cast<char*>(payload), length));
+ 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;
+ }
+ } // switch
+ payload += length;
+ } // Length <> 0
+
+
+ if (sizeConsumed >=31 || length == 0) {
+ finished = true;
+ }
+ } // !finished
+} // parseAdvertisement
+
+
+/**
+ * @brief Set the address of the advertised device.
+ * @param [in] address The address of the advertised device.
+ */
+void BLEAdvertisedDevice::setAddress(BLEAddress address) {
+ m_address = address;
+} // setAddress
+
+
+/**
+ * @brief Set the adFlag for this device.
+ * @param [in] The discovered adFlag.
+ */
+void BLEAdvertisedDevice::setAdFlag(uint8_t adFlag) {
+ m_adFlag = adFlag;
+} // setAdFlag
+
+
+/**
+ * @brief Set the appearance for this device.
+ * @param [in] The discovered appearance.
+ */
+void BLEAdvertisedDevice::setAppearance(uint16_t appearance) {
+ m_appearance = appearance;
+ m_haveAppearance = true;
+ ESP_LOGD(LOG_TAG, "- appearance: %d", m_appearance);
+} // setAppearance
+
+
+/**
+ * @brief Set the manufacturer data for this device.
+ * @param [in] The discovered manufacturer data.
+ */
+void BLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
+ m_manufacturerData = manufacturerData;
+ m_haveManufacturerData = true;
+ char* pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)m_manufacturerData.data(), (uint8_t)m_manufacturerData.length());
+ ESP_LOGD(LOG_TAG, "- manufacturer data: %s", pHex);
+ free(pHex);
+} // setManufacturerData
+
+
+/**
+ * @brief Set the name for this device.
+ * @param [in] name The discovered name.
+ */
+void BLEAdvertisedDevice::setName(std::string name) {
+ m_name = name;
+ m_haveName = true;
+ ESP_LOGD(LOG_TAG, "- setName(): name: %s", m_name.c_str());
+} // setName
+
+
+/**
+ * @brief Set the RSSI for this device.
+ * @param [in] rssi The discovered RSSI.
+ */
+void BLEAdvertisedDevice::setRSSI(int rssi) {
+ m_rssi = rssi;
+ m_haveRSSI = true;
+ ESP_LOGD(LOG_TAG, "- setRSSI(): rssi: %d", m_rssi);
+} // setRSSI
+
+
+/**
+ * @brief Set the Scan that created this advertised device.
+ * @param pScan The Scan that created this advertised device.
+ */
+void BLEAdvertisedDevice::setScan(BLEScan* pScan) {
+ m_pScan = pScan;
+} // setScan
+
+
+/**
+ * @brief Set the Service UUID for this device.
+ * @param [in] serviceUUID The discovered serviceUUID
+ */
+void BLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
+ return setServiceUUID(BLEUUID(serviceUUID));
+} // setServiceUUID
+
+
+/**
+ * @brief Set the Service UUID for this device.
+ * @param [in] serviceUUID The discovered serviceUUID
+ */
+void BLEAdvertisedDevice::setServiceUUID(BLEUUID serviceUUID) {
+ m_serviceUUIDs.push_back(serviceUUID);
+ m_haveServiceUUID = true;
+ ESP_LOGD(LOG_TAG, "- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
+} // setServiceUUID
+
+
+/**
+ * @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.
+ */
+void BLEAdvertisedDevice::setTXPower(int8_t txPower) {
+ m_txPower = txPower;
+ m_haveTXPower = true;
+ ESP_LOGD(LOG_TAG, "- txPower: %d", m_txPower);
+} // setTXPower
+
+
+/**
+ * @brief Create a string representation of this device.
+ * @return A string representation of this device.
+ */
+std::string BLEAdvertisedDevice::toString() {
+ std::stringstream ss;
+ ss << "Name: " << getName() << ", Address: " << getAddress().toString();
+ if (haveAppearance()) {
+ ss << ", appearance: " << getAppearance();
+ }
+ if (haveManufacturerData()) {
+ char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)getManufacturerData().data(), getManufacturerData().length());
+ ss << ", manufacturer data: " << pHex;
+ free(pHex);
+ }
+ if (haveServiceUUID()) {
+ ss << ", serviceUUID: " << getServiceUUID().toString();
+ }
+ if (haveTXPower()) {
+ ss << ", txPower: " << (int)getTXPower();
+ }
+ return ss.str();
+} // toString
+
+uint8_t* BLEAdvertisedDevice::getPayload() {
+ return m_payload;
+}
+
+void BLEAdvertisedDevice::setPayload(uint8_t* payload) {
+ m_payload = payload;
+}
+
+
+#endif /* CONFIG_BT_ENABLED */
+
diff --git a/sensor/patchedBLE/src/BLEAdvertisedDevice.h b/sensor/patchedBLE/src/BLEAdvertisedDevice.h
new file mode 100644
index 0000000..a3b1e6e
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAdvertisedDevice.h
@@ -0,0 +1,120 @@
+/*
+ * BLEAdvertisedDevice.h
+ *
+ * Created on: Jul 3, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
+#define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gattc_api.h>
+
+#include <map>
+
+#include "BLEAddress.h"
+#include "BLEScan.h"
+#include "BLEUUID.h"
+
+
+class BLEScan;
+/**
+ * @brief A representation of a %BLE advertised device found by a scan.
+ *
+ * When we perform a %BLE scan, the result will be a set of devices that are advertising. This
+ * class provides a model of a detected device.
+ */
+class BLEAdvertisedDevice {
+public:
+ BLEAdvertisedDevice();
+
+ BLEAddress getAddress();
+ uint16_t getAppearance();
+ std::string getManufacturerData();
+ std::string getName();
+ int getRSSI();
+ BLEScan* getScan();
+ std::string getServiceData();
+ BLEUUID getServiceDataUUID();
+ BLEUUID getServiceUUID();
+ int8_t getTXPower();
+ uint8_t* getPayload();
+
+
+ bool isAdvertisingService(BLEUUID uuid);
+ bool haveAppearance();
+ bool haveManufacturerData();
+ bool haveName();
+ bool haveRSSI();
+ bool haveServiceData();
+ bool haveServiceUUID();
+ bool haveTXPower();
+
+ std::string toString();
+
+private:
+ friend class BLEScan;
+
+ void parseAdvertisement(uint8_t* payload);
+ void setAddress(BLEAddress address);
+ void setAdFlag(uint8_t adFlag);
+ void setAdvertizementResult(uint8_t* payload);
+ void setAppearance(uint16_t appearance);
+ void setManufacturerData(std::string manufacturerData);
+ 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);
+ void setPayload(uint8_t* payload);
+
+
+ bool m_haveAppearance;
+ bool m_haveManufacturerData;
+ bool m_haveName;
+ bool m_haveRSSI;
+ bool m_haveServiceData;
+ bool m_haveServiceUUID;
+ bool m_haveTXPower;
+
+
+ BLEAddress m_address = BLEAddress((uint8_t*)"\0\0\0\0\0\0");
+ uint8_t m_adFlag;
+ uint16_t m_appearance;
+ int m_deviceType;
+ std::string m_manufacturerData;
+ std::string m_name;
+ BLEScan* m_pScan;
+ int m_rssi;
+ std::vector<BLEUUID> m_serviceUUIDs;
+ int8_t m_txPower;
+ std::string m_serviceData;
+ BLEUUID m_serviceDataUUID;
+ uint8_t* m_payload;
+};
+
+/**
+ * @brief A callback handler for callbacks associated device scanning.
+ *
+ * When we are performing a scan as a %BLE client, we may wish to know when a new device that is advertising
+ * has been found. This class can be sub-classed and registered such that when a scan is performed and
+ * a new advertised device has been found, we will be called back to be notified.
+ */
+class BLEAdvertisedDeviceCallbacks {
+public:
+ virtual ~BLEAdvertisedDeviceCallbacks() {}
+ /**
+ * @brief Called when a new scan result is detected.
+ *
+ * As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
+ * device that was found. During any individual scan, a device will only be detected one time.
+ */
+ virtual void onResult(BLEAdvertisedDevice advertisedDevice) = 0;
+};
+
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */
diff --git a/sensor/patchedBLE/src/BLEAdvertising.cpp b/sensor/patchedBLE/src/BLEAdvertising.cpp
new file mode 100644
index 0000000..4b3cb8d
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAdvertising.cpp
@@ -0,0 +1,462 @@
+/*
+ * BLEAdvertising.cpp
+ *
+ * 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)
+#include "BLEAdvertising.h"
+#include <esp_log.h>
+#include <esp_err.h>
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLEAdvertising";
+
+
+/**
+ * @brief Construct a default advertising object.
+ *
+ */
+BLEAdvertising::BLEAdvertising() {
+ m_advData.set_scan_rsp = false;
+ m_advData.include_name = true;
+ m_advData.include_txpower = true;
+ m_advData.min_interval = 0x20;
+ m_advData.max_interval = 0x40;
+ m_advData.appearance = 0x00;
+ m_advData.manufacturer_len = 0;
+ m_advData.p_manufacturer_data = nullptr;
+ m_advData.service_data_len = 0;
+ m_advData.p_service_data = nullptr;
+ m_advData.service_uuid_len = 0;
+ m_advData.p_service_uuid = nullptr;
+ m_advData.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
+
+ m_advParams.adv_int_min = 0x20;
+ m_advParams.adv_int_max = 0x40;
+ m_advParams.adv_type = ADV_TYPE_IND;
+ m_advParams.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
+ m_advParams.channel_map = ADV_CHNL_ALL;
+ m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
+
+ m_customAdvData = false; // No custom advertising data
+ m_customScanResponseData = false; // No custom scan response data
+} // BLEAdvertising
+
+
+/**
+ * @brief Add a service uuid to exposed list of services.
+ * @param [in] serviceUUID The UUID of the service to expose.
+ */
+void BLEAdvertising::addServiceUUID(BLEUUID serviceUUID) {
+ m_serviceUUIDs.push_back(serviceUUID);
+} // addServiceUUID
+
+
+/**
+ * @brief Add a service uuid to exposed list of services.
+ * @param [in] serviceUUID The string representation of the service to expose.
+ */
+void BLEAdvertising::addServiceUUID(const char* serviceUUID) {
+ addServiceUUID(BLEUUID(serviceUUID));
+} // addServiceUUID
+
+
+/**
+ * @brief Set the device appearance in the advertising data.
+ * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
+ * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
+ * @param [in] appearance The appearance of the device in the advertising data.
+ * @return N/A.
+ */
+void BLEAdvertising::setAppearance(uint16_t appearance) {
+ m_advData.appearance = appearance;
+} // setAppearance
+
+void BLEAdvertising::setMinInterval(uint16_t mininterval) {
+ m_advData.min_interval = mininterval;
+ m_advParams.adv_int_min = mininterval;
+} // setMinInterval
+
+void BLEAdvertising::setMaxInterval(uint16_t maxinterval) {
+ m_advData.max_interval = maxinterval;
+ m_advParams.adv_int_max = maxinterval;
+} // setMaxInterval
+
+
+/**
+ * @brief Set the filtering for the scan filter.
+ * @param [in] scanRequestWhitelistOnly If true, only allow scan requests from those on the white list.
+ * @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
+ */
+void BLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
+ ESP_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
+ if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
+ m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
+ ESP_LOGD(LOG_TAG, "<< setScanFilter");
+ return;
+ }
+ if (scanRequestWhitelistOnly && !connectWhitelistOnly) {
+ m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY;
+ ESP_LOGD(LOG_TAG, "<< setScanFilter");
+ return;
+ }
+ if (!scanRequestWhitelistOnly && connectWhitelistOnly) {
+ m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST;
+ ESP_LOGD(LOG_TAG, "<< setScanFilter");
+ return;
+ }
+ if (scanRequestWhitelistOnly && connectWhitelistOnly) {
+ m_advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST;
+ ESP_LOGD(LOG_TAG, "<< setScanFilter");
+ return;
+ }
+} // setScanFilter
+
+
+/**
+ * @brief Set the advertisement data that is to be published in a regular advertisement.
+ * @param [in] advertisementData The data to be advertised.
+ */
+void BLEAdvertising::setAdvertisementData(BLEAdvertisementData& advertisementData) {
+ ESP_LOGD(LOG_TAG, ">> setAdvertisementData");
+ esp_err_t errRc = ::esp_ble_gap_config_adv_data_raw(
+ (uint8_t*)advertisementData.getPayload().data(),
+ advertisementData.getPayload().length());
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_config_adv_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
+ ESP_LOGD(LOG_TAG, "<< setAdvertisementData");
+} // setAdvertisementData
+
+
+/**
+ * @brief Set the advertisement data that is to be published in a scan response.
+ * @param [in] advertisementData The data to be advertised.
+ */
+void BLEAdvertising::setScanResponseData(BLEAdvertisementData& advertisementData) {
+ ESP_LOGD(LOG_TAG, ">> setScanResponseData");
+ esp_err_t errRc = ::esp_ble_gap_config_scan_rsp_data_raw(
+ (uint8_t*)advertisementData.getPayload().data(),
+ advertisementData.getPayload().length());
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_config_scan_rsp_data_raw: %d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
+ ESP_LOGD(LOG_TAG, "<< setScanResponseData");
+} // setScanResponseData
+
+/**
+ * @brief Start advertising.
+ * Start advertising.
+ * @return N/A.
+ */
+void BLEAdvertising::start() {
+ ESP_LOGD(LOG_TAG, ">> start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
+
+
+ // We have a vector of service UUIDs that we wish to advertise. In order to use the
+ // ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte)
+ // representations. If we have 1 or more services to advertise then we allocate enough
+ // storage to host them and then copy them in one at a time into the contiguous storage.
+ int numServices = m_serviceUUIDs.size();
+ if (numServices > 0) {
+ m_advData.service_uuid_len = 16*numServices;
+ m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len];
+ uint8_t* p = m_advData.p_service_uuid;
+ for (int i=0; i<numServices; i++) {
+ ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str());
+ BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128();
+ memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16);
+ p+=16;
+ }
+ } else {
+ m_advData.service_uuid_len = 0;
+ ESP_LOGD(LOG_TAG, "- no services advertised");
+ }
+
+ esp_err_t errRc;
+
+ if (m_customAdvData == false) {
+ // Set the configuration for advertising.
+ 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;
+ }
+ }
+
+ 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.
+ // Here we release that storage.
+ if (m_advData.service_uuid_len > 0) {
+ delete[] m_advData.p_service_uuid;
+ m_advData.p_service_uuid = nullptr;
+ }
+
+ // Start advertising.
+ errRc = ::esp_ble_gap_start_advertising(&m_advParams);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gap_start_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ ESP_LOGD(LOG_TAG, "<< start");
+} // start
+
+
+/**
+ * @brief Stop advertising.
+ * Stop advertising.
+ * @return N/A.
+ */
+void BLEAdvertising::stop() {
+ ESP_LOGD(LOG_TAG, ">> stop");
+ esp_err_t errRc = ::esp_ble_gap_stop_advertising();
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_advertising: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ ESP_LOGD(LOG_TAG, "<< stop");
+} // stop
+
+/**
+ * @brief Add data to the payload to be advertised.
+ * @param [in] data The data to be added to the payload.
+ */
+void BLEAdvertisementData::addData(std::string data) {
+ if ((m_payload.length() + data.length()) > ESP_BLE_ADV_DATA_LEN_MAX) {
+ return;
+ }
+ m_payload.append(data);
+} // addData
+
+
+/**
+ * @brief Set the appearance.
+ * @param [in] appearance The appearance code value.
+ *
+ * See also:
+ * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
+ */
+void BLEAdvertisementData::setAppearance(uint16_t appearance) {
+ char cdata[2];
+ cdata[0] = 3;
+ cdata[1] = ESP_BLE_AD_TYPE_APPEARANCE; // 0x19
+ addData(std::string(cdata, 2) + std::string((char *)&appearance,2));
+} // setAppearance
+
+
+/**
+ * @brief Set the complete services.
+ * @param [in] uuid The single service to advertise.
+ */
+void BLEAdvertisementData::setCompleteServices(BLEUUID uuid) {
+ char cdata[2];
+ switch(uuid.bitSize()) {
+ case 16: {
+ // [Len] [0x02] [LL] [HH]
+ cdata[0] = 3;
+ cdata[1] = ESP_BLE_AD_TYPE_16SRV_CMPL; // 0x03
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2));
+ break;
+ }
+
+ case 32: {
+ // [Len] [0x04] [LL] [LL] [HH] [HH]
+ cdata[0] = 5;
+ cdata[1] = ESP_BLE_AD_TYPE_32SRV_CMPL; // 0x05
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4));
+ break;
+ }
+
+ case 128: {
+ // [Len] [0x04] [0] [1] ... [15]
+ cdata[0] = 17;
+ cdata[1] = ESP_BLE_AD_TYPE_128SRV_CMPL; // 0x07
+ addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16));
+ break;
+ }
+
+ default:
+ return;
+ }
+} // setCompleteServices
+
+
+/**
+ * @brief Set the advertisement flags.
+ * @param [in] The flags to be set in the advertisement.
+ *
+ * * ESP_BLE_ADV_FLAG_LIMIT_DISC
+ * * ESP_BLE_ADV_FLAG_GEN_DISC
+ * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
+ * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
+ * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
+ * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
+ */
+void BLEAdvertisementData::setFlags(uint8_t flag) {
+ char cdata[3];
+ cdata[0] = 2;
+ cdata[1] = ESP_BLE_AD_TYPE_FLAG; // 0x01
+ cdata[2] = flag;
+ addData(std::string(cdata, 3));
+} // setFlag
+
+
+
+/**
+ * @brief Set manufacturer specific data.
+ * @param [in] data Manufacturer data.
+ */
+void BLEAdvertisementData::setManufacturerData(std::string data) {
+ ESP_LOGD("BLEAdvertisementData", ">> setManufacturerData");
+ char cdata[2];
+ cdata[0] = data.length() + 1;
+ cdata[1] = ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE; // 0xff
+ addData(std::string(cdata, 2) + data);
+ ESP_LOGD("BLEAdvertisementData", "<< setManufacturerData");
+} // setManufacturerData
+
+
+/**
+ * @brief Set the name.
+ * @param [in] The complete name of the device.
+ */
+void BLEAdvertisementData::setName(std::string name) {
+ ESP_LOGD("BLEAdvertisementData", ">> setName: %s", name.c_str());
+ char cdata[2];
+ cdata[0] = name.length() + 1;
+ cdata[1] = ESP_BLE_AD_TYPE_NAME_CMPL; // 0x09
+ addData(std::string(cdata, 2) + name);
+ ESP_LOGD("BLEAdvertisementData", "<< setName");
+} // setName
+
+
+/**
+ * @brief Set the partial services.
+ * @param [in] uuid The single service to advertise.
+ */
+void BLEAdvertisementData::setPartialServices(BLEUUID uuid) {
+ char cdata[2];
+ switch(uuid.bitSize()) {
+ case 16: {
+ // [Len] [0x02] [LL] [HH]
+ cdata[0] = 3;
+ cdata[1] = ESP_BLE_AD_TYPE_16SRV_PART; // 0x02
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2));
+ break;
+ }
+
+ case 32: {
+ // [Len] [0x04] [LL] [LL] [HH] [HH]
+ cdata[0] = 5;
+ cdata[1] = ESP_BLE_AD_TYPE_32SRV_PART; // 0x04
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4));
+ break;
+ }
+
+ case 128: {
+ // [Len] [0x04] [0] [1] ... [15]
+ cdata[0] = 17;
+ cdata[1] = ESP_BLE_AD_TYPE_128SRV_PART; // 0x06
+ addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16));
+ break;
+ }
+
+ default:
+ return;
+ }
+} // setPartialServices
+
+
+/**
+ * @brief Set the service data (UUID + data)
+ * @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
+ * @param [in] data The data to be associated with the service data advert.
+ */
+void BLEAdvertisementData::setServiceData(BLEUUID uuid, std::string data) {
+ char cdata[2];
+ switch(uuid.bitSize()) {
+ case 16: {
+ // [Len] [0x16] [UUID16] data
+ cdata[0] = data.length() + 3;
+ cdata[1] = ESP_BLE_AD_TYPE_SERVICE_DATA; // 0x16
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid16,2) + data);
+ break;
+ }
+
+ case 32: {
+ // [Len] [0x20] [UUID32] data
+ cdata[0] = data.length() + 5;
+ cdata[1] = ESP_BLE_AD_TYPE_32SERVICE_DATA; // 0x20
+ addData(std::string(cdata, 2) + std::string((char *)&uuid.getNative()->uuid.uuid32,4) + data);
+ break;
+ }
+
+ case 128: {
+ // [Len] [0x21] [UUID128] data
+ cdata[0] = data.length() + 17;
+ cdata[1] = ESP_BLE_AD_TYPE_128SERVICE_DATA; // 0x21
+ addData(std::string(cdata, 2) + std::string((char *)uuid.getNative()->uuid.uuid128,16) + data);
+ break;
+ }
+
+ default:
+ return;
+ }
+} // setServiceData
+
+
+/**
+ * @brief Set the short name.
+ * @param [in] The short name of the device.
+ */
+void BLEAdvertisementData::setShortName(std::string name) {
+ ESP_LOGD("BLEAdvertisementData", ">> setShortName: %s", name.c_str());
+ char cdata[2];
+ cdata[0] = name.length() + 1;
+ cdata[1] = ESP_BLE_AD_TYPE_NAME_SHORT; // 0x08
+ addData(std::string(cdata, 2) + name);
+ ESP_LOGD("BLEAdvertisementData", "<< setShortName");
+} // setShortName
+
+
+
+/**
+ * @brief Retrieve the payload that is to be advertised.
+ * @return The payload that is to be advertised.
+ */
+std::string BLEAdvertisementData::getPayload() {
+ return m_payload;
+} // getPayload
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEAdvertising.h b/sensor/patchedBLE/src/BLEAdvertising.h
new file mode 100644
index 0000000..e316529
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEAdvertising.h
@@ -0,0 +1,68 @@
+/*
+ * BLEAdvertising.h
+ *
+ * Created on: Jun 21, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
+#define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gap_ble_api.h>
+#include "BLEUUID.h"
+#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);
+ void addData(std::string data); // Add data to the payload.
+ std::string getPayload(); // Retrieve the current advert payload.
+
+private:
+ friend class BLEAdvertising;
+ std::string m_payload; // The payload of the advertisement.
+}; // BLEAdvertisementData
+
+
+/**
+ * @brief Perform and manage %BLE advertising.
+ *
+ * A %BLE server will want to perform advertising in order to make itself known to %BLE clients.
+ */
+class BLEAdvertising {
+public:
+ BLEAdvertising();
+ void addServiceUUID(BLEUUID serviceUUID);
+ void addServiceUUID(const char* serviceUUID);
+ void start();
+ void stop();
+ void setAppearance(uint16_t appearance);
+ void setMaxInterval(uint16_t maxinterval);
+ void setMinInterval(uint16_t mininterval);
+ void setAdvertisementData(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/sensor/patchedBLE/src/BLEBeacon.cpp b/sensor/patchedBLE/src/BLEBeacon.cpp
new file mode 100644
index 0000000..a63197c
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLEBeacon.h b/sensor/patchedBLE/src/BLEBeacon.h
new file mode 100644
index 0000000..0b02e2b
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLECharacteristic.cpp b/sensor/patchedBLE/src/BLECharacteristic.cpp
new file mode 100644
index 0000000..931c753
--- /dev/null
+++ b/sensor/patchedBLE/src/BLECharacteristic.cpp
@@ -0,0 +1,793 @@
+/*
+ * BLECharacteristic.cpp
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include <string.h>
+#include <iomanip>
+#include <stdlib.h>
+#include "sdkconfig.h"
+#include <esp_log.h>
+#include <esp_err.h>
+#include "BLECharacteristic.h"
+#include "BLEService.h"
+#include "BLEDevice.h"
+#include "BLEUtils.h"
+#include "BLE2902.h"
+#include "GeneralUtils.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLECharacteristic";
+
+#define NULL_HANDLE (0xffff)
+
+
+/**
+ * @brief Construct a characteristic
+ * @param [in] uuid - UUID (const char*) for the characteristic.
+ * @param [in] properties - Properties for the characteristic.
+ */
+BLECharacteristic::BLECharacteristic(const char* uuid, uint32_t properties) : BLECharacteristic(BLEUUID(uuid), properties) {
+}
+
+/**
+ * @brief Construct a characteristic
+ * @param [in] uuid - UUID for the characteristic.
+ * @param [in] properties - Properties for the characteristic.
+ */
+BLECharacteristic::BLECharacteristic(BLEUUID uuid, uint32_t properties) {
+ m_bleUUID = uuid;
+ m_handle = NULL_HANDLE;
+ m_properties = (esp_gatt_char_prop_t)0;
+ m_pCallbacks = nullptr;
+
+ setBroadcastProperty((properties & PROPERTY_BROADCAST) !=0);
+ setReadProperty((properties & PROPERTY_READ) !=0);
+ setWriteProperty((properties & PROPERTY_WRITE) !=0);
+ setNotifyProperty((properties & PROPERTY_NOTIFY) !=0);
+ setIndicateProperty((properties & PROPERTY_INDICATE) !=0);
+ setWriteNoResponseProperty((properties & PROPERTY_WRITE_NR) !=0);
+} // BLECharacteristic
+
+/**
+ * @brief Destructor.
+ */
+BLECharacteristic::~BLECharacteristic() {
+ //free(m_value.attr_value); // Release the storage for the value.
+} // ~BLECharacteristic
+
+
+/**
+ * @brief Associate a descriptor with this characteristic.
+ * @param [in] pDescriptor
+ * @return N/A.
+ */
+void BLECharacteristic::addDescriptor(BLEDescriptor* pDescriptor) {
+ ESP_LOGD(LOG_TAG, ">> addDescriptor(): Adding %s to %s", pDescriptor->toString().c_str(), toString().c_str());
+ m_descriptorMap.setByUUID(pDescriptor->getUUID(), pDescriptor);
+ ESP_LOGD(LOG_TAG, "<< addDescriptor()");
+} // addDescriptor
+
+
+/**
+ * @brief Register a new characteristic with the ESP runtime.
+ * @param [in] pService The service with which to associate this characteristic.
+ */
+void BLECharacteristic::executeCreate(BLEService* pService) {
+ ESP_LOGD(LOG_TAG, ">> executeCreate()");
+
+ if (m_handle != NULL_HANDLE) {
+ ESP_LOGE(LOG_TAG, "Characteristic already has a handle.");
+ return;
+ }
+
+ m_pService = pService; // Save the service for to which this characteristic belongs.
+
+ ESP_LOGD(LOG_TAG, "Registering characteristic (esp_ble_gatts_add_char): uuid: %s, service: %s",
+ getUUID().toString().c_str(),
+ m_pService->toString().c_str());
+
+ esp_attr_control_t control;
+ control.auto_rsp = ESP_GATT_RSP_BY_APP;
+
+ m_semaphoreCreateEvt.take("executeCreate");
+
+ /*
+ esp_attr_value_t value;
+ value.attr_len = m_value.getLength();
+ value.attr_max_len = ESP_GATT_MAX_ATTR_LEN;
+ value.attr_value = m_value.getData();
+ */
+
+ esp_err_t errRc = ::esp_ble_gatts_add_char(
+ m_pService->getHandle(),
+ getUUID().getNative(),
+ static_cast<esp_gatt_perm_t>(m_permissions),
+ getProperties(),
+ //&value,
+ nullptr,
+ &control); // Whether to auto respond or not.
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreCreateEvt.wait("executeCreate");
+
+ // Now that we have registered the characteristic, we must also register all the descriptors associated with this
+ // characteristic. We iterate through each of those and invoke the registration call to register them with the
+ // ESP environment.
+
+ BLEDescriptor* pDescriptor = m_descriptorMap.getFirst();
+
+ while (pDescriptor != nullptr) {
+ pDescriptor->executeCreate(this);
+ pDescriptor = m_descriptorMap.getNext();
+ } // End while
+
+ ESP_LOGD(LOG_TAG, "<< executeCreate");
+} // executeCreate
+
+
+/**
+ * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
+ * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
+ * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ */
+BLEDescriptor* BLECharacteristic::getDescriptorByUUID(const char* descriptorUUID) {
+ return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID));
+} // getDescriptorByUUID
+
+
+/**
+ * @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
+ * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
+ * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ */
+BLEDescriptor* BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) {
+ return m_descriptorMap.getByUUID(descriptorUUID);
+} // getDescriptorByUUID
+
+
+/**
+ * @brief Get the handle of the characteristic.
+ * @return The handle of the characteristic.
+ */
+uint16_t 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;
+} // getProperties
+
+
+/**
+ * @brief Get the service associated with this characteristic.
+ */
+BLEService* BLECharacteristic::getService() {
+ return m_pService;
+} // getService
+
+
+/**
+ * @brief Get the UUID of the characteristic.
+ * @return The UUID of the characteristic.
+ */
+BLEUUID BLECharacteristic::getUUID() {
+ return m_bleUUID;
+} // getUUID
+
+
+/**
+ * @brief Retrieve the current value of the characteristic.
+ * @return A pointer to storage containing the current characteristic value.
+ */
+std::string BLECharacteristic::getValue() {
+ return m_value.getValue();
+} // getValue
+
+
+/**
+ * Handle a GATT server event.
+ */
+void BLECharacteristic::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param) {
+ ESP_LOGD(LOG_TAG, ">> handleGATTServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str());
+
+ switch(event) {
+ // Events handled:
+ //
+ // ESP_GATTS_ADD_CHAR_EVT
+ // ESP_GATTS_CONF_EVT
+ // ESP_GATTS_CONNECT_EVT
+ // ESP_GATTS_DISCONNECT_EVT
+ // ESP_GATTS_EXEC_WRITE_EVT
+ // ESP_GATTS_READ_EVT
+ // ESP_GATTS_WRITE_EVT
+
+ //
+ // ESP_GATTS_EXEC_WRITE_EVT
+ // When we receive this event it is an indication that a previous write long needs to be committed.
+ //
+ // exec_write:
+ // - uint16_t conn_id
+ // - uint32_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint8_t exec_write_flag - Either ESP_GATT_PREP_WRITE_EXEC or ESP_GATT_PREP_WRITE_CANCEL
+ //
+ case ESP_GATTS_EXEC_WRITE_EVT: {
+ if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) {
+ m_value.commit();
+ if (m_pCallbacks != nullptr) {
+ m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
+ }
+ } else {
+ m_value.cancel();
+ }
+
+ esp_err_t errRc = ::esp_ble_gatts_send_response(
+ gatts_if,
+ param->write.conn_id,
+ param->write.trans_id, ESP_GATT_OK, nullptr);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ break;
+ } // ESP_GATTS_EXEC_WRITE_EVT
+
+
+ // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
+ // add_char:
+ // - esp_gatt_status_t status
+ // - uint16_t attr_handle
+ // - uint16_t service_handle
+ // - esp_bt_uuid_t char_uuid
+ case ESP_GATTS_ADD_CHAR_EVT: {
+ if (getUUID().equals(BLEUUID(param->add_char.char_uuid)) &&
+ getHandle() == param->add_char.attr_handle &&
+ getService()->getHandle()==param->add_char.service_handle) {
+ m_semaphoreCreateEvt.give();
+ }
+ break;
+ } // ESP_GATTS_ADD_CHAR_EVT
+
+
+ // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
+ //
+ // write:
+ // - uint16_t conn_id
+ // - uint16_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool need_rsp
+ // - bool is_prep
+ // - uint16_t len
+ // - uint8_t *value
+ //
+ case ESP_GATTS_WRITE_EVT: {
+// We check if this write request is for us by comparing the handles in the event. If it is for us
+// we save the new value. Next we look at the need_rsp flag which indicates whether or not we need
+// to send a response. If we do, then we formulate a response and send it.
+ if (param->write.handle == m_handle) {
+ if (param->write.is_prep) {
+ m_value.addPart(param->write.value, param->write.len);
+ } else {
+ setValue(param->write.value, param->write.len);
+ }
+
+ ESP_LOGD(LOG_TAG, " - Response to write event: New value: handle: %.2x, uuid: %s",
+ getHandle(), getUUID().toString().c_str());
+
+ char* pHexData = BLEUtils::buildHexData(nullptr, param->write.value, param->write.len);
+ ESP_LOGD(LOG_TAG, " - Data: length: %d, data: %s", param->write.len, pHexData);
+ free(pHexData);
+
+ if (param->write.need_rsp) {
+ esp_gatt_rsp_t rsp;
+
+ rsp.attr_value.len = param->write.len;
+ rsp.attr_value.handle = m_handle;
+ rsp.attr_value.offset = param->write.offset;
+ rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
+ memcpy(rsp.attr_value.value, param->write.value, param->write.len);
+
+ esp_err_t errRc = ::esp_ble_gatts_send_response(
+ gatts_if,
+ param->write.conn_id,
+ param->write.trans_id, ESP_GATT_OK, &rsp);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ } // Response needed
+
+ if (m_pCallbacks != nullptr && param->write.is_prep != true) {
+ m_pCallbacks->onWrite(this); // Invoke the onWrite callback handler.
+ }
+ } // Match on handles.
+ break;
+ } // ESP_GATTS_WRITE_EVT
+
+
+ // ESP_GATTS_READ_EVT - A request to read the value of a characteristic has arrived.
+ //
+ // read:
+ // - uint16_t conn_id
+ // - uint32_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool is_long
+ // - bool need_rsp
+ //
+ case ESP_GATTS_READ_EVT: {
+ if (param->read.handle == m_handle) {
+
+
+
+// Here's an interesting thing. The read request has the option of saying whether we need a response
+// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like
+// a very strange read.
+//
+// We have to handle the case where the data we wish to send back to the client is greater than the maximum
+// packet size of 22 bytes. In this case, we become responsible for chunking the data into units of 22 bytes.
+// The apparent algorithm is as follows:
+//
+// If the is_long flag is set then this is a follow on from an original read and we will already have sent at least 22 bytes.
+// If the is_long flag is not set then we need to check how much data we are going to send. If we are sending LESS than
+// 22 bytes, then we "just" send it and thats the end of the story.
+// If we are sending 22 bytes exactly, we just send it BUT we will get a follow on request.
+// If we are sending more than 22 bytes, we send the first 22 bytes and we will get a follow on request.
+// Because of follow on request processing, we need to maintain an offset of how much data we have already sent
+// so that when a follow on request arrives, we know where to start in the data to send the next sequence.
+// Note that the indication that the client will send a follow on request is that we sent exactly 22 bytes as a response.
+// If our payload is divisible by 22 then the last response will be a response of 0 bytes in length.
+//
+// The following code has deliberately not been factored to make it fewer statements because this would cloud the
+// the logic flow comprehension.
+//
+ // 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;
+
+ if (param->read.is_long) {
+ std::string value = m_value.getValue();
+
+ if (value.length() - m_value.getReadOffset() < maxOffset) {
+ // This is the last in the chain
+ rsp.attr_value.len = value.length() - m_value.getReadOffset();
+ rsp.attr_value.offset = m_value.getReadOffset();
+ memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
+ m_value.setReadOffset(0);
+ } else {
+ // There will be more to come.
+ rsp.attr_value.len = maxOffset;
+ rsp.attr_value.offset = m_value.getReadOffset();
+ memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
+ m_value.setReadOffset(rsp.attr_value.offset + maxOffset);
+ }
+ } else { // read.is_long == false
+
+ if (m_pCallbacks != nullptr) { // If is.long is false then this is the first (or only) request to read data, so invoke the callback
+ m_pCallbacks->onRead(this); // Invoke the read callback.
+ }
+
+ std::string value = m_value.getValue();
+
+ if (value.length()+1 > maxOffset) {
+ // Too big for a single shot entry.
+ m_value.setReadOffset(maxOffset);
+ rsp.attr_value.len = maxOffset;
+ rsp.attr_value.offset = 0;
+ memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
+ } else {
+ // Will fit in a single packet with no callbacks required.
+ rsp.attr_value.len = value.length();
+ rsp.attr_value.offset = 0;
+ memcpy(rsp.attr_value.value, value.data(), rsp.attr_value.len);
+ }
+ }
+ rsp.attr_value.handle = param->read.handle;
+ rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
+
+ char *pHexData = BLEUtils::buildHexData(nullptr, rsp.attr_value.value, rsp.attr_value.len);
+ ESP_LOGD(LOG_TAG, " - Data: length=%d, data=%s, offset=%d", rsp.attr_value.len, pHexData, rsp.attr_value.offset);
+ free(pHexData);
+
+ esp_err_t errRc = ::esp_ble_gatts_send_response(
+ gatts_if, param->read.conn_id,
+ param->read.trans_id,
+ ESP_GATT_OK,
+ &rsp);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ } // Response needed
+ } // Handle matches this characteristic.
+ break;
+ } // ESP_GATTS_READ_EVT
+
+
+ // ESP_GATTS_CONF_EVT
+ //
+ // conf:
+ // - esp_gatt_status_t status – The status code.
+ // - uint16_t conn_id – The connection used.
+ //
+ case ESP_GATTS_CONF_EVT: {
+ m_semaphoreConfEvt.give();
+ break;
+ }
+
+ case ESP_GATTS_CONNECT_EVT: {
+ m_semaphoreConfEvt.give();
+ break;
+ }
+
+ case ESP_GATTS_DISCONNECT_EVT: {
+ m_semaphoreConfEvt.give();
+ break;
+ }
+
+ default: {
+ break;
+ } // default
+
+ } // switch event
+
+ // Give each of the descriptors associated with this characteristic the opportunity to handle the
+ // event.
+
+ m_descriptorMap.handleGATTServerEvent(event, gatts_if, param);
+ ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
+} // handleGATTServerEvent
+
+
+/**
+ * @brief Send an indication.
+ * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
+ * will block waiting a positive confirmation from the client.
+ * @return N/A
+ */
+void BLECharacteristic::indicate() {
+
+ ESP_LOGD(LOG_TAG, ">> indicate: length: %d", m_value.getValue().length());
+
+ assert(getService() != nullptr);
+ assert(getService()->getServer() != nullptr);
+
+ GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
+
+ if (getService()->getServer()->getConnectedCount() == 0) {
+ ESP_LOGD(LOG_TAG, "<< indicate: No connected clients.");
+ return;
+ }
+
+ // Test to see if we have a 0x2902 descriptor. If we do, then check to see if indications are enabled
+ // and, if not, prevent the indication.
+
+ BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
+ if (p2902 != nullptr && !p2902->getIndications()) {
+ ESP_LOGD(LOG_TAG, "<< indications disabled; ignoring");
+ return;
+ }
+
+ 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();
+
+ m_semaphoreConfEvt.take("indicate");
+
+ esp_err_t errRc = ::esp_ble_gatts_send_indicate(
+ getService()->getServer()->getGattsIf(),
+ getService()->getServer()->getConnId(),
+ getHandle(), length, (uint8_t*)m_value.getValue().data(), true); // The need_confirm = true makes this an indication.
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreConfEvt.wait("indicate");
+ ESP_LOGD(LOG_TAG, "<< indicate");
+} // indicate
+
+
+/**
+ * @brief Send a notify.
+ * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
+ * will not block; it is a fire and forget.
+ * @return N/A.
+ */
+void BLECharacteristic::notify() {
+ ESP_LOGD(LOG_TAG, ">> notify: length: %d", m_value.getValue().length());
+
+
+ assert(getService() != nullptr);
+ assert(getService()->getServer() != nullptr);
+
+
+ GeneralUtils::hexDump((uint8_t*)m_value.getValue().data(), m_value.getValue().length());
+
+ if (getService()->getServer()->getConnectedCount() == 0) {
+ ESP_LOGD(LOG_TAG, "<< notify: No connected clients.");
+ return;
+ }
+
+ // Test to see if we have a 0x2902 descriptor. If we do, then check to see if notification is enabled
+ // and, if not, prevent the notification.
+
+ BLE2902 *p2902 = (BLE2902*)getDescriptorByUUID((uint16_t)0x2902);
+ if (p2902 != nullptr && !p2902->getNotifications()) {
+ ESP_LOGD(LOG_TAG, "<< notifications disabled; ignoring");
+ return;
+ }
+
+ 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();
+
+ m_semaphoreConfEvt.take("notify");
+
+ esp_err_t errRc = ::esp_ble_gatts_send_indicate(
+ getService()->getServer()->getGattsIf(),
+ getService()->getServer()->getConnId(),
+ getHandle(), length, (uint8_t*)m_value.getValue().data(), false); // The need_confirm = false makes this a notify.
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_send_indicate: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreConfEvt.wait("notify");
+
+ ESP_LOGD(LOG_TAG, "<< notify");
+} // Notify
+
+
+/**
+ * @brief Set the permission to broadcast.
+ * A characteristics has properties associated with it which define what it is capable of doing.
+ * One of these is the broadcast flag.
+ * @param [in] value The flag value of the property.
+ * @return N/A
+ */
+void BLECharacteristic::setBroadcastProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setBroadcastProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
+ }
+} // setBroadcastProperty
+
+
+/**
+ * @brief Set the callback handlers for this characteristic.
+ * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
+ */
+void BLECharacteristic::setCallbacks(BLECharacteristicCallbacks* pCallbacks) {
+ ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallbacks);
+ m_pCallbacks = pCallbacks;
+ ESP_LOGD(LOG_TAG, "<< setCallbacks");
+} // setCallbacks
+
+
+/**
+ * @brief Set the BLE handle associated with this characteristic.
+ * A user program will request that a characteristic be created against a service. When the characteristic has been
+ * registered, the service will be given a "handle" that it knows the characteristic as. This handle is unique to the
+ * server/service but it is told to the service, not the characteristic associated with the service. This internally
+ * exposed function can be invoked by the service against this model of the characteristic to allow the characteristic
+ * to learn its own handle. Once the characteristic knows its own handle, it will be able to see incoming GATT events
+ * that will be propagated down to it which contain a handle value and now know that the event is destined for it.
+ * @param [in] handle The handle associated with this characteristic.
+ */
+void BLECharacteristic::setHandle(uint16_t handle) {
+ ESP_LOGD(LOG_TAG, ">> setHandle: handle=0x%.2x, characteristic uuid=%s", handle, getUUID().toString().c_str());
+ m_handle = handle;
+ ESP_LOGD(LOG_TAG, "<< setHandle");
+} // setHandle
+
+
+/**
+ * @brief Set the Indicate property value.
+ * @param [in] value Set to true if we are to allow indicate messages.
+ */
+void BLECharacteristic::setIndicateProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setIndicateProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_INDICATE);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
+ }
+} // setIndicateProperty
+
+
+/**
+ * @brief Set the Notify property value.
+ * @param [in] value Set to true if we are to allow notification messages.
+ */
+void BLECharacteristic::setNotifyProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setNotifyProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
+ }
+} // setNotifyProperty
+
+
+/**
+ * @brief Set the Read property value.
+ * @param [in] value Set to true if we are to allow reads.
+ */
+void BLECharacteristic::setReadProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setReadProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_READ);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_READ);
+ }
+} // setReadProperty
+
+
+/**
+ * @brief Set the value of the characteristic.
+ * @param [in] data The data to set for the characteristic.
+ * @param [in] length The length of the data in bytes.
+ */
+void BLECharacteristic::setValue(uint8_t* data, size_t length) {
+ char *pHex = BLEUtils::buildHexData(nullptr, data, length);
+ ESP_LOGD(LOG_TAG, ">> setValue: length=%d, data=%s, characteristic UUID=%s", length, pHex, getUUID().toString().c_str());
+ free(pHex);
+ if (length > ESP_GATT_MAX_ATTR_LEN) {
+ ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
+ return;
+ }
+ m_value.setValue(data, length);
+ ESP_LOGD(LOG_TAG, "<< setValue");
+} // setValue
+
+
+/**
+ * @brief Set the value of the characteristic from string data.
+ * We set the value of the characteristic from the bytes contained in the
+ * string.
+ * @param [in] Set the value of the characteristic.
+ * @return N/A.
+ */
+void BLECharacteristic::setValue(std::string value) {
+ setValue((uint8_t*)(value.data()), value.length());
+} // setValue
+
+void BLECharacteristic::setValue(uint16_t& data16) {
+ uint8_t temp[2];
+ temp[0]=data16;
+ temp[1]=data16>>8;
+ setValue(temp, 2);
+} // setValue
+
+void BLECharacteristic::setValue(uint32_t& data32) {
+ uint8_t temp[4];
+ temp[0]=data32;
+ temp[1]=data32>>8;
+ temp[2]=data32>>16;
+ temp[3]=data32>>24;
+ setValue(temp, 4);
+} // setValue
+
+void BLECharacteristic::setValue(int& data32) {
+ uint8_t temp[4];
+ temp[0]=data32;
+ temp[1]=data32>>8;
+ temp[2]=data32>>16;
+ temp[3]=data32>>24;
+ setValue(temp, 4);
+} // setValue
+
+void BLECharacteristic::setValue(float& data32) {
+ uint8_t temp[4];
+ *((float *)temp) = data32;
+ setValue(temp, 4);
+} // setValue
+
+void BLECharacteristic::setValue(double& data64) {
+ uint8_t temp[8];
+ *((double *)temp) = data64;
+ setValue(temp, 8);
+} // setValue
+
+
+/**
+ * @brief Set the Write No Response property value.
+ * @param [in] value Set to true if we are to allow writes with no response.
+ */
+void BLECharacteristic::setWriteNoResponseProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setWriteNoResponseProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
+ }
+} // setWriteNoResponseProperty
+
+
+/**
+ * @brief Set the Write property value.
+ * @param [in] value Set to true if we are to allow writes.
+ */
+void BLECharacteristic::setWriteProperty(bool value) {
+ //ESP_LOGD(LOG_TAG, "setWriteProperty(%d)", value);
+ if (value) {
+ m_properties = (esp_gatt_char_prop_t)(m_properties | ESP_GATT_CHAR_PROP_BIT_WRITE);
+ } else {
+ m_properties = (esp_gatt_char_prop_t)(m_properties & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
+ }
+} // setWriteProperty
+
+
+/**
+ * @brief Return a string representation of the characteristic.
+ * @return A string representation of the characteristic.
+ */
+std::string BLECharacteristic::toString() {
+ std::stringstream stringstream;
+ stringstream << std::hex << std::setfill('0');
+ stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
+ stringstream << " " <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_READ)?"Read ":"") <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE)?"Write ":"") <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)?"WriteNoResponse ":"") <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST)?"Broadcast ":"") <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)?"Notify ":"") <<
+ ((m_properties & ESP_GATT_CHAR_PROP_BIT_INDICATE)?"Indicate ":"");
+ 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.
+ */
+void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) {
+ ESP_LOGD("BLECharacteristicCallbacks", ">> onRead: default");
+ ESP_LOGD("BLECharacteristicCallbacks", "<< onRead");
+} // onRead
+
+
+/**
+ * @brief Callback function to support a write request.
+ * @param [in] pCharacteristic The characteristic that is the source of the event.
+ */
+void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic) {
+ ESP_LOGD("BLECharacteristicCallbacks", ">> onWrite: default");
+ ESP_LOGD("BLECharacteristicCallbacks", "<< onWrite");
+} // onWrite
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLECharacteristic.h b/sensor/patchedBLE/src/BLECharacteristic.h
new file mode 100644
index 0000000..b3f8d2e
--- /dev/null
+++ b/sensor/patchedBLE/src/BLECharacteristic.h
@@ -0,0 +1,140 @@
+/*
+ * BLECharacteristic.h
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
+#define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <string>
+#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"
+
+class BLEService;
+class BLEDescriptor;
+class BLECharacteristicCallbacks;
+
+/**
+ * @brief A management structure for %BLE descriptors.
+ */
+class BLEDescriptorMap {
+public:
+ void setByUUID(const char* uuid, BLEDescriptor *pDescriptor);
+ void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor);
+ void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor);
+ BLEDescriptor* getByUUID(const char* uuid);
+ BLEDescriptor* getByUUID(BLEUUID uuid);
+ BLEDescriptor* getByHandle(uint16_t handle);
+ std::string toString();
+ void handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+ BLEDescriptor* getFirst();
+ BLEDescriptor* getNext();
+private:
+ std::map<std::string, BLEDescriptor *> m_uuidMap;
+ std::map<uint16_t, BLEDescriptor *> m_handleMap;
+ std::map<std::string, BLEDescriptor *>::iterator m_iterator;
+};
+
+
+/**
+ * @brief The model of a %BLE Characteristic.
+ *
+ * A %BLE Characteristic is an identified value container that manages a value. It is exposed by a %BLE server and
+ * can be read and written to by a %BLE client.
+ */
+class BLECharacteristic {
+public:
+ BLECharacteristic(const char* uuid, uint32_t properties = 0);
+ BLECharacteristic(BLEUUID uuid, uint32_t properties = 0);
+ virtual ~BLECharacteristic();
+
+ void addDescriptor(BLEDescriptor* pDescriptor);
+ BLEDescriptor* getDescriptorByUUID(const char* descriptorUUID);
+ BLEDescriptor* getDescriptorByUUID(BLEUUID descriptorUUID);
+ //size_t getLength();
+ BLEUUID getUUID();
+ std::string getValue();
+
+ void indicate();
+ void notify();
+ void setBroadcastProperty(bool value);
+ void setCallbacks(BLECharacteristicCallbacks* pCallbacks);
+ void setIndicateProperty(bool value);
+ void setNotifyProperty(bool value);
+ void setReadProperty(bool value);
+ void setValue(uint8_t* data, size_t size);
+ void setValue(std::string value);
+ void setValue(uint16_t& data16);
+ void setValue(uint32_t& data32);
+ void setValue(int& data32);
+ void setValue(float& data32);
+ void setValue(double& data64);
+ void setWriteProperty(bool value);
+ void setWriteNoResponseProperty(bool value);
+ std::string toString();
+ uint16_t getHandle();
+ void setAccessPermissions(esp_gatt_perm_t perm);
+
+ static const uint32_t PROPERTY_READ = 1<<0;
+ static const uint32_t PROPERTY_WRITE = 1<<1;
+ static const uint32_t PROPERTY_NOTIFY = 1<<2;
+ static const uint32_t PROPERTY_BROADCAST = 1<<3;
+ static const uint32_t PROPERTY_INDICATE = 1<<4;
+ static const uint32_t PROPERTY_WRITE_NR = 1<<5;
+
+private:
+
+ friend class BLEServer;
+ friend class BLEService;
+ friend class BLEDescriptor;
+ friend class BLECharacteristicMap;
+
+ BLEUUID m_bleUUID;
+ BLEDescriptorMap m_descriptorMap;
+ uint16_t m_handle;
+ esp_gatt_char_prop_t m_properties;
+ 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,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+
+ void executeCreate(BLEService* pService);
+ esp_gatt_char_prop_t getProperties();
+ BLEService* getService();
+ void setHandle(uint16_t handle);
+ FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
+ FreeRTOS::Semaphore m_semaphoreConfEvt = FreeRTOS::Semaphore("ConfEvt");
+}; // BLECharacteristic
+
+
+/**
+ * @brief Callbacks that can be associated with a %BLE characteristic to inform of events.
+ *
+ * When a server application creates a %BLE characteristic, we may wish to be informed when there is either
+ * a read or write request to the characteristic's value. An application can register a
+ * sub-classed instance of this class and will be notified when such an event happens.
+ */
+class BLECharacteristicCallbacks {
+public:
+ virtual ~BLECharacteristicCallbacks();
+ virtual void onRead(BLECharacteristic* pCharacteristic);
+ virtual void onWrite(BLECharacteristic* pCharacteristic);
+};
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */
diff --git a/sensor/patchedBLE/src/BLECharacteristicMap.cpp b/sensor/patchedBLE/src/BLECharacteristicMap.cpp
new file mode 100644
index 0000000..86a9744
--- /dev/null
+++ b/sensor/patchedBLE/src/BLECharacteristicMap.cpp
@@ -0,0 +1,143 @@
+/*
+ * BLECharacteristicMap.cpp
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include <iomanip>
+#include "BLEService.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+
+/**
+ * @brief Return the characteristic by handle.
+ * @param [in] handle The handle to look up the characteristic.
+ * @return The characteristic.
+ */
+BLECharacteristic* BLECharacteristicMap::getByHandle(uint16_t handle) {
+ return m_handleMap.at(handle);
+} // getByHandle
+
+
+/**
+ * @brief Return the characteristic by UUID.
+ * @param [in] UUID The UUID to look up the characteristic.
+ * @return The characteristic.
+ */
+BLECharacteristic* BLECharacteristicMap::getByUUID(const char* uuid) {
+ return getByUUID(BLEUUID(uuid));
+}
+
+
+/**
+ * @brief Return the characteristic by UUID.
+ * @param [in] UUID The UUID to look up the characteristic.
+ * @return The characteristic.
+ */
+BLECharacteristic* BLECharacteristicMap::getByUUID(BLEUUID uuid) {
+ for (auto &myPair : m_uuidMap) {
+ if (myPair.first->getUUID().equals(uuid)) {
+ return myPair.first;
+ }
+ }
+ //return m_uuidMap.at(uuid.toString());
+ return nullptr;
+} // getByUUID
+
+
+/**
+ * @brief Get the first characteristic in the map.
+ * @return The first characteristic in the map.
+ */
+BLECharacteristic* BLECharacteristicMap::getFirst() {
+ m_iterator = m_uuidMap.begin();
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLECharacteristic* pRet = m_iterator->first;
+ m_iterator++;
+ return pRet;
+} // getFirst
+
+
+/**
+ * @brief Get the next characteristic in the map.
+ * @return The next characteristic in the map.
+ */
+BLECharacteristic* BLECharacteristicMap::getNext() {
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLECharacteristic* pRet = m_iterator->first;
+ m_iterator++;
+ return pRet;
+} // getNext
+
+
+/**
+ * @brief Pass the GATT server event onwards to each of the characteristics found in the mapping
+ * @param [in] event
+ * @param [in] gatts_if
+ * @param [in] param
+ */
+void BLECharacteristicMap::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param) {
+ // Invoke the handler for every Service we have.
+ for (auto &myPair : m_uuidMap) {
+ myPair.first->handleGATTServerEvent(event, gatts_if, param);
+ }
+} // handleGATTServerEvent
+
+
+/**
+ * @brief Set the characteristic by handle.
+ * @param [in] handle The handle of the characteristic.
+ * @param [in] characteristic The characteristic to cache.
+ * @return N/A.
+ */
+void BLECharacteristicMap::setByHandle(uint16_t handle,
+ BLECharacteristic *characteristic) {
+ m_handleMap.insert(std::pair<uint16_t, BLECharacteristic *>(handle, characteristic));
+} // setByHandle
+
+
+/**
+ * @brief Set the characteristic by UUID.
+ * @param [in] uuid The uuid of the characteristic.
+ * @param [in] characteristic The characteristic to cache.
+ * @return N/A.
+ */
+void BLECharacteristicMap::setByUUID(
+ BLECharacteristic *pCharacteristic,
+ BLEUUID uuid) {
+ m_uuidMap.insert(std::pair<BLECharacteristic *, std::string>(pCharacteristic, uuid.toString()));
+} // setByUUID
+
+
+/**
+ * @brief Return a string representation of the characteristic map.
+ * @return A string representation of the characteristic map.
+ */
+std::string BLECharacteristicMap::toString() {
+ std::stringstream stringStream;
+ stringStream << std::hex << std::setfill('0');
+ int count=0;
+ for (auto &myPair: m_uuidMap) {
+ if (count > 0) {
+ stringStream << "\n";
+ }
+ count++;
+ stringStream << "handle: 0x" << std::setw(2) << myPair.first->getHandle() << ", uuid: " + myPair.first->getUUID().toString();
+ }
+ return stringStream.str();
+} // toString
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEClient.cpp b/sensor/patchedBLE/src/BLEClient.cpp
new file mode 100644
index 0000000..141cf0f
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEClient.cpp
@@ -0,0 +1,471 @@
+/*
+ * BLEDevice.cpp
+ *
+ * Created on: Mar 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_log.h>
+#include <esp_bt.h>
+#include <esp_bt_main.h>
+#include <esp_gap_ble_api.h>
+#include <esp_gattc_api.h>
+#include "BLEClient.h"
+#include "BLEUtils.h"
+#include "BLEService.h"
+#include "GeneralUtils.h"
+#include <string>
+#include <sstream>
+#include <unordered_set>
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+/*
+ * Design
+ * ------
+ * When we perform a searchService() requests, we are asking the BLE server to return each of the services
+ * that it exposes. For each service, we received an ESP_GATTC_SEARCH_RES_EVT event which contains details
+ * of the exposed service including its UUID.
+ *
+ * The objects we will invent for a BLEClient will be as follows:
+ * * BLERemoteService - A model of a remote service.
+ * * BLERemoteCharacteristic - A model of a remote characteristic
+ * * BLERemoteDescriptor - A model of a remote descriptor.
+ *
+ * Since there is a hierarchical relationship here, we will have the idea that from a BLERemoteService will own
+ * zero or more remote characteristics and a BLERemoteCharacteristic will own zero or more remote BLEDescriptors.
+ *
+ * We will assume that a BLERemoteService contains a map that maps BLEUUIDs to the set of owned characteristics
+ * and that a BLECharacteristic contains a map that maps BLEUUIDs to the set of owned descriptors.
+ *
+ *
+ */
+static const char* LOG_TAG = "BLEClient";
+
+BLEClient::BLEClient() {
+ m_pClientCallbacks = nullptr;
+ m_conn_id = 0;
+ m_gattc_if = 0;
+ m_haveServices = false;
+ m_isConnected = false; // Initially, we are flagged as not connected.
+} // BLEClient
+
+
+/**
+ * @brief Destructor.
+ */
+BLEClient::~BLEClient() {
+ // We may have allocated service references associated with this client. Before we are finished
+ // with the client, we must release resources.
+ for (auto &myPair : m_servicesMap) {
+ delete myPair.second;
+ }
+ m_servicesMap.clear();
+} // ~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();
+ m_haveServices = false;
+ 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.
+ */
+bool BLEClient::connect(BLEAddress address) {
+ ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
+
+// We need the connection handle that we get from registering the application. We register the app
+// 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));
+ return false;
+ }
+
+ m_semaphoreRegEvt.wait("connect");
+
+ m_peerAddress = address;
+
+ // Perform the open connection request against the target BLE Server.
+ m_semaphoreOpenEvt.take("connect");
+ errRc = ::esp_ble_gattc_open(
+ getGattcIf(),
+ *getPeerAddress().getNative(), // address
+ BLE_ADDR_TYPE_PUBLIC, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
+ 1 // direct connection
+ );
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return false;
+ }
+
+ uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
+ ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
+ return rc == ESP_GATT_OK;
+} // connect
+
+
+/**
+ * @brief Disconnect from the peer.
+ * @return N/A.
+ */
+void BLEClient::disconnect() {
+ ESP_LOGD(LOG_TAG, ">> disconnect()");
+ esp_err_t errRc = ::esp_ble_gattc_close(getGattcIf(), getConnId());
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_close: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ esp_ble_gattc_app_unregister(getGattcIf());
+ m_peerAddress = BLEAddress("00:00:00:00:00:00");
+ ESP_LOGD(LOG_TAG, "<< disconnect()");
+} // disconnect
+
+
+/**
+ * @brief Handle GATT Client events
+ */
+void BLEClient::gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* evtParam) {
+
+ // Execute handler code based on the type of event received.
+ switch(event) {
+
+ //
+ // ESP_GATTC_DISCONNECT_EVT
+ //
+ // disconnect:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ 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;
+ m_semaphoreRssiCmplEvt.give();
+ m_semaphoreSearchCmplEvt.give(1);
+ break;
+ } // ESP_GATTC_DISCONNECT_EVT
+
+ //
+ // ESP_GATTC_OPEN_EVT
+ //
+ // open:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ // - uint16_t mtu
+ //
+ case ESP_GATTC_OPEN_EVT: {
+ m_conn_id = evtParam->open.conn_id;
+ if (m_pClientCallbacks != nullptr) {
+ m_pClientCallbacks->onConnect(this);
+ }
+ if (evtParam->open.status == ESP_GATT_OK) {
+ m_isConnected = true; // Flag us as connected.
+ }
+ m_semaphoreOpenEvt.give(evtParam->open.status);
+ break;
+ } // ESP_GATTC_OPEN_EVT
+
+
+ //
+ // ESP_GATTC_REG_EVT
+ //
+ // reg:
+ // esp_gatt_status_t status
+ // uint16_t app_id
+ //
+ case ESP_GATTC_REG_EVT: {
+ m_gattc_if = gattc_if;
+ m_semaphoreRegEvt.give();
+ break;
+ } // ESP_GATTC_REG_EVT
+
+
+ //
+ // ESP_GATTC_SEARCH_CMPL_EVT
+ //
+ // search_cmpl:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ //
+ case ESP_GATTC_SEARCH_CMPL_EVT: {
+ m_semaphoreSearchCmplEvt.give(0);
+ break;
+ } // ESP_GATTC_SEARCH_CMPL_EVT
+
+
+ //
+ // ESP_GATTC_SEARCH_RES_EVT
+ //
+ // search_res:
+ // - uint16_t conn_id
+ // - uint16_t start_handle
+ // - uint16_t end_handle
+ // - esp_gatt_id_t srvc_id
+ //
+ case ESP_GATTC_SEARCH_RES_EVT: {
+ BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id);
+ BLERemoteService* pRemoteService = new BLERemoteService(
+ evtParam->search_res.srvc_id,
+ this,
+ evtParam->search_res.start_handle,
+ evtParam->search_res.end_handle
+ );
+ m_servicesMap.insert(std::pair<std::string, BLERemoteService *>(uuid.toString(), pRemoteService));
+ break;
+ } // ESP_GATTC_SEARCH_RES_EVT
+
+
+ default: {
+ break;
+ }
+ } // Switch
+
+ // Pass the request on to all services.
+ for (auto &myPair : m_servicesMap) {
+ myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
+ }
+
+} // gattClientEventHandler
+
+
+uint16_t BLEClient::getConnId() {
+ return m_conn_id;
+} // getConnId
+
+
+
+esp_gatt_if_t BLEClient::getGattcIf() {
+ return m_gattc_if;
+} // getGattcIf
+
+
+/**
+ * @brief Retrieve the address of the peer.
+ *
+ * Returns the Bluetooth device address of the %BLE peer to which this client is connected.
+ */
+BLEAddress BLEClient::getPeerAddress() {
+ return m_peerAddress;
+} // getAddress
+
+
+/**
+ * @brief Ask the BLE server for the RSSI value.
+ * @return The RSSI value.
+ */
+int BLEClient::getRssi() {
+ ESP_LOGD(LOG_TAG, ">> getRssi()");
+ if (!isConnected()) {
+ ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected");
+ return 0;
+ }
+ // We make the API call to read the RSSI value which is an asynchronous operation. We expect to receive
+ // an ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT to indicate completion.
+ //
+ m_semaphoreRssiCmplEvt.take("getRssi");
+ esp_err_t rc = ::esp_ble_gap_read_rssi(*getPeerAddress().getNative());
+ if (rc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< getRssi: esp_ble_gap_read_rssi: rc=%d %s", rc, GeneralUtils::errorToString(rc));
+ return 0;
+ }
+ int rssiValue = m_semaphoreRssiCmplEvt.wait("getRssi");
+ ESP_LOGD(LOG_TAG, "<< getRssi(): %d", rssiValue);
+ return rssiValue;
+} // getRssi
+
+
+/**
+ * @brief Get the service BLE Remote Service instance 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.
+ */
+BLERemoteService* BLEClient::getService(const char* uuid) {
+ return getService(BLEUUID(uuid));
+} // getService
+
+
+/**
+ * @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());
+// Design
+// ------
+// We wish to retrieve the service given its UUID. It is possible that we have not yet asked the
+// device what services it has in which case we have nothing to match against. If we have not
+// asked the device about its services, then we do that now. Once we get the results we can then
+// examine the services map to see if it has the service we are looking for.
+ if (!m_haveServices) {
+ getServices();
+ }
+ std::string uuidStr = uuid.toString();
+ for (auto &myPair : m_servicesMap) {
+ if (myPair.first == uuidStr) {
+ ESP_LOGD(LOG_TAG, "<< getService: found the service with uuid: %s", uuid.toString().c_str());
+ return myPair.second;
+ }
+ } // End of each of the services.
+ ESP_LOGD(LOG_TAG, "<< getService: not found");
+ throw new BLEUuidNotFoundException;
+} // getService
+
+
+/**
+ * @brief Ask the remote %BLE server for its services.
+ * A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
+ * services and wait until we have received them all.
+ * @return N/A
+ */
+std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
+/*
+ * Design
+ * ------
+ * We invoke esp_ble_gattc_search_service. This will request a list of the service exposed by the
+ * peer BLE partner to be returned as events. Each event will be an an instance of ESP_GATTC_SEARCH_RES_EVT
+ * and will culminate with an ESP_GATTC_SEARCH_CMPL_EVT when all have been received.
+ */
+ ESP_LOGD(LOG_TAG, ">> getServices");
+
+ clearServices(); // Clear any services that may exist.
+
+ esp_err_t errRc = esp_ble_gattc_search_service(
+ getGattcIf(),
+ getConnId(),
+ nullptr // Filter UUID
+ );
+ m_semaphoreSearchCmplEvt.take("getServices");
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_search_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return &m_servicesMap;
+ }
+ // If sucessfull, remember that we now have services.
+ m_haveServices = (m_semaphoreSearchCmplEvt.wait("getServices") == 0);
+ ESP_LOGD(LOG_TAG, "<< 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.
+ *
+ * @param [in] event
+ * @param [in] param
+ */
+void BLEClient::handleGAPEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param) {
+ ESP_LOGD(LOG_TAG, "BLEClient ... handling GAP event!");
+ switch(event) {
+ //
+ // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
+ //
+ // read_rssi_cmpl
+ // - esp_bt_status_t status
+ // - int8_t rssi
+ // - esp_bd_addr_t remote_addr
+ //
+ case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
+ m_semaphoreRssiCmplEvt.give((uint32_t)param->read_rssi_cmpl.rssi);
+ break;
+ } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
+
+ default:
+ break;
+ }
+} // handleGAPEvent
+
+
+/**
+ * @brief Are we connected to a partner?
+ * @return True if we are connected and false if we are not connected.
+ */
+bool BLEClient::isConnected() {
+ return m_isConnected;
+} // isConnected
+
+
+
+
+/**
+ * @brief Set the callbacks that will be invoked.
+ */
+void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
+ m_pClientCallbacks = pClientCallbacks;
+} // setClientCallbacks
+
+
+/**
+ * @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.
+ */
+std::string BLEClient::toString() {
+ std::ostringstream ss;
+ ss << "peer address: " << m_peerAddress.toString();
+ ss << "\nServices:\n";
+ for (auto &myPair : m_servicesMap) {
+ ss << myPair.second->toString() << "\n";
+ // myPair.second is the value
+ }
+ return ss.str();
+} // toString
+
+
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEClient.h b/sensor/patchedBLE/src/BLEClient.h
new file mode 100644
index 0000000..a60ed10
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEClient.h
@@ -0,0 +1,98 @@
+/*
+ * BLEDevice.h
+ *
+ * Created on: Mar 22, 2017
+ * Author: kolban
+ */
+
+#ifndef MAIN_BLEDEVICE_H_
+#define MAIN_BLEDEVICE_H_
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <esp_gattc_api.h>
+#include <string.h>
+#include <map>
+#include <string>
+#include "BLEExceptions.h"
+#include "BLERemoteService.h"
+#include "BLEService.h"
+#include "BLEAddress.h"
+
+class BLERemoteService;
+class BLEClientCallbacks;
+
+/**
+ * @brief A model of a %BLE client.
+ */
+class BLEClient {
+public:
+ BLEClient();
+ ~BLEClient();
+
+ 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(); // Return true if we are connected.
+
+ void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
+ 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;
+ friend class BLERemoteService;
+ friend class BLERemoteCharacteristic;
+ friend class BLERemoteDescriptor;
+
+ void gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* param);
+
+ uint16_t getConnId();
+ esp_gatt_if_t getGattcIf();
+ 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_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");
+ FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt");
+ FreeRTOS::Semaphore m_semaphoreSearchCmplEvt = FreeRTOS::Semaphore("SearchCmplEvt");
+ FreeRTOS::Semaphore m_semaphoreRssiCmplEvt = FreeRTOS::Semaphore("RssiCmplEvt");
+ std::map<std::string, BLERemoteService*> m_servicesMap;
+ void clearServices(); // Clear any existing services.
+
+}; // class BLEDevice
+
+
+/**
+ * @brief Callbacks associated with a %BLE client.
+ */
+class BLEClientCallbacks {
+public:
+ virtual ~BLEClientCallbacks() {};
+ virtual void onConnect(BLEClient *pClient) = 0;
+ virtual void onDisconnect(BLEClient *pClient) = 0;
+};
+
+#endif // CONFIG_BT_ENABLED
+#endif /* MAIN_BLEDEVICE_H_ */
diff --git a/sensor/patchedBLE/src/BLEDescriptor.cpp b/sensor/patchedBLE/src/BLEDescriptor.cpp
new file mode 100644
index 0000000..58ff78b
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEDescriptor.cpp
@@ -0,0 +1,342 @@
+/*
+ * BLEDescriptor.cpp
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include <string.h>
+#include <iomanip>
+#include <stdlib.h>
+#include "sdkconfig.h"
+#include <esp_log.h>
+#include <esp_err.h>
+#include "BLEService.h"
+#include "BLEDescriptor.h"
+#include "GeneralUtils.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLEDescriptor";
+
+
+#define NULL_HANDLE (0xffff)
+
+
+/**
+ * @brief BLEDescriptor constructor.
+ */
+BLEDescriptor::BLEDescriptor(const char* uuid) : BLEDescriptor(BLEUUID(uuid)) {
+}
+
+/**
+ * @brief BLEDescriptor constructor.
+ */
+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; // 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
+
+
+/**
+ * @brief BLEDescriptor destructor.
+ */
+BLEDescriptor::~BLEDescriptor() {
+ free(m_value.attr_value); // Release the storage we created in the constructor.
+} // ~BLEDescriptor
+
+
+/**
+ * @brief Execute the creation of the descriptor with the BLE runtime in ESP.
+ * @param [in] pCharacteristic The characteristic to which to register this descriptor.
+ */
+void BLEDescriptor::executeCreate(BLECharacteristic* pCharacteristic) {
+ ESP_LOGD(LOG_TAG, ">> executeCreate(): %s", toString().c_str());
+
+ if (m_handle != NULL_HANDLE) {
+ ESP_LOGE(LOG_TAG, "Descriptor already has a handle.");
+ return;
+ }
+
+ m_pCharacteristic = pCharacteristic; // Save the characteristic associated with this service.
+
+ esp_attr_control_t control;
+ control.auto_rsp = ESP_GATT_RSP_BY_APP;
+ m_semaphoreCreateEvt.take("executeCreate");
+ esp_err_t errRc = ::esp_ble_gatts_add_char_descr(
+ pCharacteristic->getService()->getHandle(),
+ getUUID().getNative(),
+ (esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
+ &m_value,
+ &control);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_add_char_descr: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreCreateEvt.wait("executeCreate");
+ ESP_LOGD(LOG_TAG, "<< executeCreate");
+} // executeCreate
+
+
+/**
+ * @brief Get the BLE handle for this descriptor.
+ * @return The handle for this descriptor.
+ */
+uint16_t BLEDescriptor::getHandle() {
+ return m_handle;
+} // getHandle
+
+
+/**
+ * @brief Get the length of the value of this descriptor.
+ * @return The length (in bytes) of the value of this descriptor.
+ */
+size_t BLEDescriptor::getLength() {
+ return m_value.attr_len;
+} // getLength
+
+
+/**
+ * @brief Get the UUID of the descriptor.
+ */
+BLEUUID BLEDescriptor::getUUID() {
+ return m_bleUUID;
+} // getUUID
+
+
+
+/**
+ * @brief Get the value of this descriptor.
+ * @return A pointer to the value of this descriptor.
+ */
+uint8_t* BLEDescriptor::getValue() {
+ return m_value.attr_value;
+} // getValue
+
+
+/**
+ * @brief Handle GATT server events for the descripttor.
+ * @param [in] event
+ * @param [in] gatts_if
+ * @param [in] param
+ */
+void BLEDescriptor::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t *param) {
+ switch(event) {
+ // ESP_GATTS_ADD_CHAR_DESCR_EVT
+ //
+ // add_char_descr:
+ // - esp_gatt_status_t status
+ // - uint16_t attr_handle
+ // - uint16_t service_handle
+ // - esp_bt_uuid_t char_uuid
+ case ESP_GATTS_ADD_CHAR_DESCR_EVT: {
+ /*
+ ESP_LOGD(LOG_TAG, "DEBUG: m_pCharacteristic: %x", (uint32_t)m_pCharacteristic);
+ ESP_LOGD(LOG_TAG, "DEBUG: m_bleUUID: %s, add_char_descr.char_uuid: %s, equals: %d",
+ m_bleUUID.toString().c_str(),
+ BLEUUID(param->add_char_descr.char_uuid).toString().c_str(),
+ m_bleUUID.equals(BLEUUID(param->add_char_descr.char_uuid)));
+ ESP_LOGD(LOG_TAG, "DEBUG: service->getHandle: %x, add_char_descr.service_handle: %x",
+ m_pCharacteristic->getService()->getHandle(), param->add_char_descr.service_handle);
+ ESP_LOGD(LOG_TAG, "DEBUG: service->lastCharacteristic: %x",
+ (uint32_t)m_pCharacteristic->getService()->getLastCreatedCharacteristic());
+ */
+ if (m_pCharacteristic != nullptr &&
+ m_bleUUID.equals(BLEUUID(param->add_char_descr.descr_uuid)) &&
+ m_pCharacteristic->getService()->getHandle() == param->add_char_descr.service_handle &&
+ m_pCharacteristic == m_pCharacteristic->getService()->getLastCreatedCharacteristic()) {
+ setHandle(param->add_char_descr.attr_handle);
+ m_semaphoreCreateEvt.give();
+ }
+ break;
+ } // ESP_GATTS_ADD_CHAR_DESCR_EVT
+
+ // ESP_GATTS_WRITE_EVT - A request to write the value of a descriptor has arrived.
+ //
+ // write:
+ // - uint16_t conn_id
+ // - uint16_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool need_rsp
+ // - bool is_prep
+ // - uint16_t len
+ // - uint8_t *value
+ case ESP_GATTS_WRITE_EVT: {
+ if (param->write.handle == m_handle) {
+ setValue(param->write.value, param->write.len); // Set the value of the descriptor.
+
+ esp_gatt_rsp_t rsp; // Build a response.
+ rsp.attr_value.len = getLength();
+ rsp.attr_value.handle = m_handle;
+ rsp.attr_value.offset = 0;
+ rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
+ memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len);
+ esp_err_t errRc = ::esp_ble_gatts_send_response(
+ gatts_if,
+ param->write.conn_id,
+ param->write.trans_id,
+ ESP_GATT_OK,
+ &rsp);
+
+ if (errRc != ESP_OK) { // Check the return code from the send of the response.
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+
+ if (m_pCallback != nullptr) { // We have completed the write, if there is a user supplied callback handler, invoke it now.
+ m_pCallback->onWrite(this); // Invoke the onWrite callback handler.
+ }
+ } // End of ... this is our handle.
+
+ break;
+ } // ESP_GATTS_WRITE_EVT
+
+ // ESP_GATTS_READ_EVT - A request to read the value of a descriptor has arrived.
+ //
+ // read:
+ // - uint16_t conn_id
+ // - uint32_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool is_long
+ // - bool need_rsp
+ //
+ case ESP_GATTS_READ_EVT: {
+ if (param->read.handle == m_handle) { // If this event is for this descriptor ... process it
+
+ if (m_pCallback != nullptr) { // If we have a user supplied callback, invoke it now.
+ m_pCallback->onRead(this); // Invoke the onRead callback method in the callback handler.
+ }
+
+ if (param->read.need_rsp) { // Do we need a response
+ ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)");
+ esp_gatt_rsp_t rsp;
+ rsp.attr_value.len = getLength();
+ rsp.attr_value.handle = param->read.handle;
+ rsp.attr_value.offset = 0;
+ rsp.attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
+ memcpy(rsp.attr_value.value, getValue(), rsp.attr_value.len);
+
+ esp_err_t errRc = ::esp_ble_gatts_send_response(
+ gatts_if,
+ param->read.conn_id,
+ param->read.trans_id,
+ ESP_GATT_OK,
+ &rsp);
+
+ if (errRc != ESP_OK) { // Check the return code from the send of the response.
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_send_response: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ } // End of need a response.
+ } // End of this is our handle
+ break;
+ } // ESP_GATTS_READ_EVT
+
+ default: {
+ break;
+ }
+ }// switch event
+} // handleGATTServerEvent
+
+
+/**
+ * @brief Set the callback handlers for this descriptor.
+ * @param [in] pCallbacks An instance of a callback structure used to define any callbacks for the descriptor.
+ */
+void BLEDescriptor::setCallbacks(BLEDescriptorCallbacks* pCallback) {
+ ESP_LOGD(LOG_TAG, ">> setCallbacks: 0x%x", (uint32_t)pCallback);
+ m_pCallback = pCallback;
+ ESP_LOGD(LOG_TAG, "<< setCallbacks");
+} // setCallbacks
+
+
+/**
+ * @brief Set the handle of this descriptor.
+ * Set the handle of this descriptor to be the supplied value.
+ * @param [in] handle The handle to be associated with this descriptor.
+ * @return N/A.
+ */
+void BLEDescriptor::setHandle(uint16_t handle) {
+ ESP_LOGD(LOG_TAG, ">> setHandle(0x%.2x): Setting descriptor handle to be 0x%.2x", handle, handle);
+ m_handle = handle;
+ ESP_LOGD(LOG_TAG, "<< setHandle()");
+} // setHandle
+
+
+/**
+ * @brief Set the value of the descriptor.
+ * @param [in] data The data to set for the descriptor.
+ * @param [in] length The length of the data in bytes.
+ */
+void BLEDescriptor::setValue(uint8_t* data, size_t length) {
+ if (length > ESP_GATT_MAX_ATTR_LEN) {
+ ESP_LOGE(LOG_TAG, "Size %d too large, must be no bigger than %d", length, ESP_GATT_MAX_ATTR_LEN);
+ return;
+ }
+ m_value.attr_len = length;
+ memcpy(m_value.attr_value, data, length);
+} // setValue
+
+
+/**
+ * @brief Set the value of the descriptor.
+ * @param [in] value The value of the descriptor in string form.
+ */
+void 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.
+ * @return A string representation of the descriptor.
+ */
+std::string BLEDescriptor::toString() {
+ std::stringstream stringstream;
+ stringstream << std::hex << std::setfill('0');
+ stringstream << "UUID: " << m_bleUUID.toString() + ", handle: 0x" << std::setw(2) << m_handle;
+ return stringstream.str();
+} // toString
+
+
+BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {}
+
+/**
+ * @brief Callback function to support a read request.
+ * @param [in] pDescriptor The descriptor that is the source of the event.
+ */
+void BLEDescriptorCallbacks::onRead(BLEDescriptor* pDescriptor) {
+ ESP_LOGD("BLEDescriptorCallbacks", ">> onRead: default");
+ ESP_LOGD("BLEDescriptorCallbacks", "<< onRead");
+} // onRead
+
+
+/**
+ * @brief Callback function to support a write request.
+ * @param [in] pDescriptor The descriptor that is the source of the event.
+ */
+void BLEDescriptorCallbacks::onWrite(BLEDescriptor* pDescriptor) {
+ ESP_LOGD("BLEDescriptorCallbacks", ">> onWrite: default");
+ ESP_LOGD("BLEDescriptorCallbacks", "<< onWrite");
+} // onWrite
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEDescriptor.h b/sensor/patchedBLE/src/BLEDescriptor.h
new file mode 100644
index 0000000..d9e0aef
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEDescriptor.h
@@ -0,0 +1,77 @@
+/*
+ * BLEDescriptor.h
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
+#define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <string>
+#include "BLEUUID.h"
+#include "BLECharacteristic.h"
+#include <esp_gatts_api.h>
+#include "FreeRTOS.h"
+
+class BLEService;
+class BLECharacteristic;
+class BLEDescriptorCallbacks;
+
+/**
+ * @brief A model of a %BLE descriptor.
+ */
+class BLEDescriptor {
+public:
+ BLEDescriptor(const char* uuid);
+ BLEDescriptor(BLEUUID uuid);
+ virtual ~BLEDescriptor();
+
+ 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 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;
+ 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);
+}; // BLEDescriptor
+
+
+/**
+ * @brief Callbacks that can be associated with a %BLE descriptors to inform of events.
+ *
+ * When a server application creates a %BLE descriptor, we may wish to be informed when there is either
+ * a read or write request to the descriptors value. An application can register a
+ * sub-classed instance of this class and will be notified when such an event happens.
+ */
+class BLEDescriptorCallbacks {
+public:
+ virtual ~BLEDescriptorCallbacks();
+ virtual void onRead(BLEDescriptor* pDescriptor);
+ virtual void onWrite(BLEDescriptor* pDescriptor);
+};
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */
diff --git a/sensor/patchedBLE/src/BLEDescriptorMap.cpp b/sensor/patchedBLE/src/BLEDescriptorMap.cpp
new file mode 100644
index 0000000..4e372e1
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEDescriptorMap.cpp
@@ -0,0 +1,152 @@
+/*
+ * BLEDescriptorMap.cpp
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include <iomanip>
+#include "BLECharacteristic.h"
+#include "BLEDescriptor.h"
+#include <esp_gatts_api.h> // ESP32 BLE
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+/**
+ * @brief Return the descriptor by UUID.
+ * @param [in] UUID The UUID to look up the descriptor.
+ * @return The descriptor. If not present, then nullptr is returned.
+ */
+BLEDescriptor* BLEDescriptorMap::getByUUID(const char* uuid) {
+ return getByUUID(BLEUUID(uuid));
+}
+
+
+/**
+ * @brief Return the descriptor by UUID.
+ * @param [in] UUID The UUID to look up the descriptor.
+ * @return The descriptor. If not present, then nullptr is returned.
+ */
+BLEDescriptor* BLEDescriptorMap::getByUUID(BLEUUID uuid) {
+ for (auto &myPair : m_uuidMap) {
+ if (myPair.second->getUUID().equals(uuid)) {
+ return myPair.second;
+ }
+ }
+ //return m_uuidMap.at(uuid.toString());
+ return nullptr;
+} // getByUUID
+
+
+/**
+ * @brief Return the descriptor by handle.
+ * @param [in] handle The handle to look up the descriptor.
+ * @return The descriptor.
+ */
+BLEDescriptor* BLEDescriptorMap::getByHandle(uint16_t handle) {
+ return m_handleMap.at(handle);
+} // getByHandle
+
+
+/**
+ * @brief Set the descriptor by UUID.
+ * @param [in] uuid The uuid of the descriptor.
+ * @param [in] characteristic The descriptor to cache.
+ * @return N/A.
+ */
+void BLEDescriptorMap::setByUUID(const char* uuid, BLEDescriptor *pDescriptor){
+ m_uuidMap.insert(std::pair<std::string, BLEDescriptor *>(uuid, pDescriptor));
+} // setByUUID
+
+
+
+/**
+ * @brief Set the descriptor by UUID.
+ * @param [in] uuid The uuid of the descriptor.
+ * @param [in] characteristic The descriptor to cache.
+ * @return N/A.
+ */
+void BLEDescriptorMap::setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor) {
+ m_uuidMap.insert(std::pair<std::string, BLEDescriptor *>(uuid.toString(), pDescriptor));
+} // setByUUID
+
+
+/**
+ * @brief Set the descriptor by handle.
+ * @param [in] handle The handle of the descriptor.
+ * @param [in] descriptor The descriptor to cache.
+ * @return N/A.
+ */
+void BLEDescriptorMap::setByHandle(uint16_t handle,
+ BLEDescriptor *pDescriptor) {
+ m_handleMap.insert(std::pair<uint16_t, BLEDescriptor *>(handle, pDescriptor));
+} // setByHandle
+
+
+/**
+ * @brief Return a string representation of the descriptor map.
+ * @return A string representation of the descriptor map.
+ */
+std::string BLEDescriptorMap::toString() {
+ std::stringstream stringStream;
+ stringStream << std::hex << std::setfill('0');
+ int count=0;
+ for (auto &myPair: m_uuidMap) {
+ if (count > 0) {
+ stringStream << "\n";
+ }
+ count++;
+ stringStream << "handle: 0x" << std::setw(2) << myPair.second->getHandle() << ", uuid: " + myPair.second->getUUID().toString();
+ }
+ return stringStream.str();
+} // toString
+
+
+/**
+ * @breif Pass the GATT server event onwards to each of the descriptors found in the mapping
+ * @param [in] event
+ * @param [in] gatts_if
+ * @param [in] param
+ */
+void BLEDescriptorMap::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t *param) {
+ // Invoke the handler for every descriptor we have.
+ for (auto &myPair : m_uuidMap) {
+ myPair.second->handleGATTServerEvent(event, gatts_if, param);
+ }
+} // handleGATTServerEvent
+
+
+/**
+ * @brief Get the first descriptor in the map.
+ * @return The first descriptor in the map.
+ */
+BLEDescriptor* BLEDescriptorMap::getFirst() {
+ m_iterator = m_uuidMap.begin();
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLEDescriptor *pRet = m_iterator->second;
+ m_iterator++;
+ return pRet;
+} // getFirst
+
+
+/**
+ * @brief Get the next descriptor in the map.
+ * @return The next descriptor in the map.
+ */
+BLEDescriptor* BLEDescriptorMap::getNext() {
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLEDescriptor *pRet = m_iterator->second;
+ m_iterator++;
+ return pRet;
+} // getNext
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEDevice.cpp b/sensor/patchedBLE/src/BLEDevice.cpp
new file mode 100644
index 0000000..a7db454
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEDevice.cpp
@@ -0,0 +1,548 @@
+/*
+ * BLE.cpp
+ *
+ * Created on: Mar 16, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <freertos/FreeRTOS.h>
+#include <freertos/event_groups.h>
+#include <freertos/task.h>
+#include <esp_err.h>
+#include <nvs_flash.h>
+#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
+#include <sstream> // Part of C++ Standard library
+#include <iomanip> // Part of C++ Standard library
+
+#include "BLEDevice.h"
+#include "BLEClient.h"
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#include "esp32-hal-bt.h"
+#endif
+
+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; // 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.
+ */
+/* 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
+
+
+/**
+ * @brief Create a new instance of a server.
+ * @return A new instance of the server.
+ */
+/* 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");
+ return m_pServer;
+} // createServer
+
+
+/**
+ * @brief Handle GATT server events.
+ *
+ * @param [in] event The event that has been newly received.
+ * @param [in] gatts_if The connection to the GATT interface.
+ * @param [in] param Parameters for the event.
+ */
+/* STATIC */ void BLEDevice::gattServerEventHandler(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param
+) {
+ ESP_LOGD(LOG_TAG, "gattServerEventHandler [esp_gatt_if: %d] ... %s",
+ gatts_if,
+ BLEUtils::gattServerEventTypeToString(event).c_str());
+
+ BLEUtils::dumpGattServerEvent(event, gatts_if, param);
+
+ switch(event) {
+ case ESP_GATTS_CONNECT_EVT: {
+ BLEDevice::m_localMTU = 23;
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ if(BLEDevice::m_securityLevel){
+ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ 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);
+ }
+} // gattServerEventHandler
+
+
+/**
+ * @brief Handle GATT client events.
+ *
+ * Handler for the GATT client events.
+ *
+ * @param [in] event
+ * @param [in] gattc_if
+ * @param [in] param
+ */
+/* STATIC */ void BLEDevice::gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* param) {
+
+ 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));
+ }
+ }
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ if(BLEDevice::m_securityLevel){
+ esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel);
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ break;
+ } // ESP_GATTC_CONNECT_EVT
+
+ default: {
+ break;
+ }
+ } // switch
+
+
+ // If we have a client registered, call it.
+ if (BLEDevice::m_pClient != nullptr) {
+ BLEDevice::m_pClient->gattClientEventHandler(event, gattc_if, param);
+ }
+
+} // gattClientEventHandler
+
+
+/**
+ * @brief Handle GAP events.
+ */
+/* 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_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");
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ 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));
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ 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));
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest());
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ 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");
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ 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);
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ 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");
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ 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);
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ break;
+ case ESP_GAP_BLE_KEY_EVT:
+ //shows the ble key type info share with peer device to the user.
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_KEY_EVT");
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ ESP_LOGI(LOG_TAG, "key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type));
+#endif // CONFIG_BLE_SMP_ENABLE
+ break;
+ case ESP_GAP_BLE_AUTH_CMPL_EVT:
+ ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_AUTH_CMPL_EVT");
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ if(BLEDevice::m_securityCallbacks!=nullptr){
+ BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl);
+ }
+#endif // CONFIG_BLE_SMP_ENABLE
+ break;
+ default: {
+ break;
+ }
+ } // switch
+
+ if (BLEDevice::m_pServer != nullptr) {
+ BLEDevice::m_pServer->handleGAPEvent(event, param);
+ }
+
+ if (BLEDevice::m_pClient != nullptr) {
+ BLEDevice::m_pClient->handleGAPEvent(event, param);
+ }
+
+ 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.
+ */
+/* STATIC */ BLEScan* BLEDevice::getScan() {
+ //ESP_LOGD(LOG_TAG, ">> getScan");
+ if (m_pScan == nullptr) {
+ m_pScan = new BLEScan();
+ //ESP_LOGD(LOG_TAG, " - creating a new scan object");
+ }
+ //ESP_LOGD(LOG_TAG, "<< getScan: Returning object at 0x%x", (uint32_t)m_pScan);
+ return m_pScan;
+} // 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.
+ */
+/* STATIC */ void BLEDevice::init(std::string deviceName) {
+ if(!initialized){
+ initialized = true; // Set the initialization flag to ensure we are only initialized once.
+
+ esp_err_t errRc = ESP_OK;
+#ifdef ARDUINO_ARCH_ESP32
+ if (!btStart()) {
+ errRc = ESP_FAIL;
+ return;
+ }
+#else
+ errRc = ::nvs_flash_init();
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+ errRc = esp_bt_controller_init(&bt_cfg);
+ if (errRc != ESP_OK) {
+ 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);
+ //errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+#else
+ errRc = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_bt_controller_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+#endif
+#endif
+
+ esp_bluedroid_status_t bt_state = esp_bluedroid_get_status();
+ if (bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){
+ errRc = esp_bluedroid_init();
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ }
+
+ if (bt_state != ESP_BLUEDROID_STATUS_ENABLED){
+ errRc = esp_bluedroid_enable();
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_bluedroid_enable: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ }
+
+ errRc = esp_ble_gap_register_callback(BLEDevice::gapEventHandler);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_register_callback: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ 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) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_set_device_name: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ };
+
+#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig
+ esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
+ errRc = ::esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_set_security_param: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ };
+#endif // CONFIG_BLE_SMP_ENABLE
+ }
+ vTaskDelay(200/portTICK_PERIOD_MS); // Delay for 200 msecs as a workaround to an apparent Arduino environment issue.
+} // init
+
+
+/**
+ * @brief Set the transmission power.
+ * The power level can be one of:
+ * * ESP_PWR_LVL_N14
+ * * ESP_PWR_LVL_N11
+ * * ESP_PWR_LVL_N8
+ * * ESP_PWR_LVL_N5
+ * * ESP_PWR_LVL_N2
+ * * ESP_PWR_LVL_P1
+ * * ESP_PWR_LVL_P4
+ * * ESP_PWR_LVL_P7
+ * @param [in] powerLevel.
+ */
+/* STATIC */ void BLEDevice::setPower(esp_power_level_t powerLevel) {
+ ESP_LOGD(LOG_TAG, ">> setPower: %d", powerLevel);
+ esp_err_t errRc = ::esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, powerLevel);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ };
+ 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;
+}
+
+bool BLEDevice::getInitialized() {
+ return initialized;
+}
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEDevice.h b/sensor/patchedBLE/src/BLEDevice.h
new file mode 100644
index 0000000..7a1b833
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEDevice.h
@@ -0,0 +1,74 @@
+/*
+ * BLEDevice.h
+ *
+ * Created on: Mar 16, 2017
+ * Author: kolban
+ */
+
+#ifndef MAIN_BLEDevice_H_
+#define MAIN_BLEDevice_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gap_ble_api.h> // ESP32 BLE
+#include <esp_gattc_api.h> // ESP32 BLE
+#include <map> // Part of C++ STL
+#include <string>
+#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(); // 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();
+ static bool getInitialized(); // Returns the state of the device, is it initialized or not?
+
+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();
+
+ static void gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* param);
+
+ static void gattServerEventHandler(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+
+ static void gapEventHandler(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param);
+
+}; // class BLE
+
+#endif // CONFIG_BT_ENABLED
+#endif /* MAIN_BLEDevice_H_ */
diff --git a/sensor/patchedBLE/src/BLEExceptions.cpp b/sensor/patchedBLE/src/BLEExceptions.cpp
new file mode 100644
index 0000000..b6adfd8
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEExceptions.cpp
@@ -0,0 +1,9 @@
+/*
+ * BLExceptions.cpp
+ *
+ * Created on: Nov 27, 2017
+ * Author: kolban
+ */
+
+#include "BLEExceptions.h"
+
diff --git a/sensor/patchedBLE/src/BLEExceptions.h b/sensor/patchedBLE/src/BLEExceptions.h
new file mode 100644
index 0000000..369fcaf
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLEHIDDevice.cpp b/sensor/patchedBLE/src/BLEHIDDevice.cpp
new file mode 100644
index 0000000..29376f3
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLEHIDDevice.h b/sensor/patchedBLE/src/BLEHIDDevice.h
new file mode 100644
index 0000000..319fd42
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLERemoteCharacteristic.cpp b/sensor/patchedBLE/src/BLERemoteCharacteristic.cpp
new file mode 100644
index 0000000..d9c64c9
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteCharacteristic.cpp
@@ -0,0 +1,606 @@
+/*
+ * BLERemoteCharacteristic.cpp
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+
+#include "BLERemoteCharacteristic.h"
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <esp_gattc_api.h>
+#include <esp_log.h>
+#include <esp_err.h>
+
+#include <sstream>
+#include "BLEExceptions.h"
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+#include "BLERemoteDescriptor.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+
+static const char* LOG_TAG = "BLERemoteCharacteristic"; // The logging tag for this class.
+
+/**
+ * @brief Constructor.
+ * @param [in] handle The BLE server side handle of this characteristic.
+ * @param [in] uuid The UUID of this characteristic.
+ * @param [in] charProp The properties of this characteristic.
+ * @param [in] pRemoteService A reference to the remote service to which this remote characteristic pertains.
+ */
+BLERemoteCharacteristic::BLERemoteCharacteristic(
+ uint16_t handle,
+ BLEUUID uuid,
+ esp_gatt_char_prop_t charProp,
+ BLERemoteService* pRemoteService) {
+ ESP_LOGD(LOG_TAG, ">> BLERemoteCharacteristic: handle: %d 0x%d, uuid: %s", handle, handle, uuid.toString().c_str());
+ m_handle = handle;
+ m_uuid = uuid;
+ m_charProp = charProp;
+ m_pRemoteService = pRemoteService;
+ m_notifyCallback = nullptr;
+
+ retrieveDescriptors(); // Get the descriptors for this characteristic
+ ESP_LOGD(LOG_TAG, "<< BLERemoteCharacteristic");
+} // BLERemoteCharacteristic
+
+
+/**
+ *@brief Destructor.
+ */
+BLERemoteCharacteristic::~BLERemoteCharacteristic() {
+ removeDescriptors(); // Release resources for any descriptor information we may have allocated.
+} // ~BLERemoteCharacteristic
+
+
+/**
+ * @brief Does the characteristic support broadcasting?
+ * @return True if the characteristic supports broadcasting.
+ */
+bool BLERemoteCharacteristic::canBroadcast() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_BROADCAST) != 0;
+} // canBroadcast
+
+
+/**
+ * @brief Does the characteristic support indications?
+ * @return True if the characteristic supports indications.
+ */
+bool BLERemoteCharacteristic::canIndicate() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_INDICATE) != 0;
+} // canIndicate
+
+
+/**
+ * @brief Does the characteristic support notifications?
+ * @return True if the characteristic supports notifications.
+ */
+bool BLERemoteCharacteristic::canNotify() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0;
+} // canNotify
+
+
+/**
+ * @brief Does the characteristic support reading?
+ * @return True if the characteristic supports reading.
+ */
+bool BLERemoteCharacteristic::canRead() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_READ) != 0;
+} // canRead
+
+
+/**
+ * @brief Does the characteristic support writing?
+ * @return True if the characteristic supports writing.
+ */
+bool BLERemoteCharacteristic::canWrite() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE) != 0;
+} // canWrite
+
+
+/**
+ * @brief Does the characteristic support writing with no response?
+ * @return True if the characteristic supports writing with no response.
+ */
+bool BLERemoteCharacteristic::canWriteNoResponse() {
+ return (m_charProp & ESP_GATT_CHAR_PROP_BIT_WRITE_NR) != 0;
+} // canWriteNoResponse
+
+
+/*
+static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
+ if (id1.id.inst_id != id2.id.inst_id) {
+ return false;
+ }
+ if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
+ return false;
+ }
+ return true;
+} // compareSrvcId
+*/
+
+/*
+static bool compareGattId(esp_gatt_id_t id1, esp_gatt_id_t id2) {
+ if (id1.inst_id != id2.inst_id) {
+ return false;
+ }
+ if (!BLEUUID(id1.uuid).equals(BLEUUID(id2.uuid))) {
+ return false;
+ }
+ return true;
+} // compareCharId
+*/
+
+
+/**
+ * @brief Handle GATT Client events.
+ * When an event arrives for a GATT client we give this characteristic the opportunity to
+ * take a look at it to see if there is interest in it.
+ * @param [in] event The type of event.
+ * @param [in] gattc_if The interface on which the event was received.
+ * @param [in] evtParam Payload data for the event.
+ * @returns N/A
+ */
+void BLERemoteCharacteristic::gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* evtParam) {
+ switch(event) {
+ //
+ // ESP_GATTC_NOTIFY_EVT
+ //
+ // notify
+ // - uint16_t conn_id - The connection identifier of the server.
+ // - esp_bd_addr_t remote_bda - The device address of the BLE server.
+ // - uint16_t handle - The handle of the characteristic for which the event is being received.
+ // - uint16_t value_len - The length of the received data.
+ // - uint8_t* value - The received data.
+ // - bool is_notify - True if this is a notify, false if it is an indicate.
+ //
+ // We have received a notification event which means that the server wishes us to know about a notification
+ // piece of data. What we must now do is find the characteristic with the associated handle and then
+ // invoke its notification callback (if it has one).
+ //
+ case ESP_GATTC_NOTIFY_EVT: {
+ if (evtParam->notify.handle != getHandle()) {
+ break;
+ }
+ if (m_notifyCallback != nullptr) {
+ ESP_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s", toString().c_str());
+ m_notifyCallback(
+ this,
+ evtParam->notify.value,
+ evtParam->notify.value_len,
+ evtParam->notify.is_notify
+ );
+ } // End we have a callback function ...
+ break;
+ } // ESP_GATTC_NOTIFY_EVT
+
+
+ //
+ // ESP_GATTC_READ_CHAR_EVT
+ // This event indicates that the server has responded to the read request.
+ //
+ // read:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - uint16_t handle
+ // - uint8_t* value
+ // - uint16_t value_len
+ //
+ case ESP_GATTC_READ_CHAR_EVT: {
+ // If this event is not for us, then nothing further to do.
+ if (evtParam->read.handle != getHandle()) {
+ break;
+ }
+
+ // At this point, we have determined that the event is for us, so now we save the value
+ // and unlock the semaphore to ensure that the requestor of the data can continue.
+ if (evtParam->read.status == ESP_GATT_OK) {
+ m_value = std::string((char*)evtParam->read.value, evtParam->read.value_len);
+ } else {
+ m_value = "";
+ }
+
+ m_semaphoreReadCharEvt.give();
+ break;
+ } // ESP_GATTC_READ_CHAR_EVT
+
+
+ //
+ // ESP_GATTC_REG_FOR_NOTIFY_EVT
+ //
+ // reg_for_notify:
+ // - esp_gatt_status_t status
+ // - uint16_t handle
+ //
+ case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
+ // If the request is not for this BLERemoteCharacteristic then move on to the next.
+ if (evtParam->reg_for_notify.handle != getHandle()) {
+ break;
+ }
+
+ // We have processed the notify registration and can unlock the semaphore.
+ m_semaphoreRegForNotifyEvt.give();
+ break;
+ } // ESP_GATTC_REG_FOR_NOTIFY_EVT
+
+
+ //
+ // ESP_GATTC_UNREG_FOR_NOTIFY_EVT
+ //
+ // unreg_for_notify:
+ // - esp_gatt_status_t status
+ // - uint16_t handle
+ //
+ case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
+ if (evtParam->unreg_for_notify.handle != getHandle()) {
+ break;
+ }
+ // We have processed the notify un-registration and can unlock the semaphore.
+ m_semaphoreRegForNotifyEvt.give();
+ break;
+ } // ESP_GATTC_UNREG_FOR_NOTIFY_EVT:
+
+
+ //
+ // ESP_GATTC_WRITE_CHAR_EVT
+ //
+ // write:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - uint16_t handle
+ //
+ case ESP_GATTC_WRITE_CHAR_EVT: {
+ // Determine if this event is for us and, if not, pass onwards.
+ if (evtParam->write.handle != getHandle()) {
+ break;
+ }
+
+ // There is nothing further we need to do here. This is merely an indication
+ // that the write has completed and we can unlock the caller.
+ m_semaphoreWriteCharEvt.give();
+ break;
+ } // ESP_GATTC_WRITE_CHAR_EVT
+
+
+ default: {
+ break;
+ }
+ } // End switch
+}; // gattClientEventHandler
+
+
+/**
+ * @brief Populate the descriptors (if any) for this characteristic.
+ */
+void BLERemoteCharacteristic::retrieveDescriptors() {
+ ESP_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
+
+ removeDescriptors(); // Remove any existing descriptors.
+
+ // Loop over each of the descriptors within the service associated with this characteristic.
+ // For each descriptor we find, create a BLERemoteDescriptor instance.
+ uint16_t offset = 0;
+ esp_gattc_descr_elem_t result;
+ while(1) {
+ uint16_t count = 1;
+ esp_gatt_status_t status = ::esp_ble_gattc_get_all_descr(
+ getRemoteService()->getClient()->getGattcIf(),
+ getRemoteService()->getClient()->getConnId(),
+ getHandle(),
+ &result,
+ &count,
+ offset
+ );
+
+ if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
+ break;
+ }
+
+ if (status != ESP_GATT_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_descr: %s", BLEUtils::gattStatusToString(status).c_str());
+ break;
+ }
+
+ if (count == 0) {
+ break;
+ }
+ ESP_LOGE(LOG_TAG, "");
+ ESP_LOGD(LOG_TAG, "Found a descriptor: Handle: %d, UUID: %s", result.handle, BLEUUID(result.uuid).toString().c_str());
+
+ // We now have a new characteristic ... let us add that to our set of known characteristics
+ BLERemoteDescriptor *pNewRemoteDescriptor = new BLERemoteDescriptor(
+ result.handle,
+ BLEUUID(result.uuid),
+ this
+ );
+
+ m_descriptorMap.insert(std::pair<std::string, BLERemoteDescriptor*>(pNewRemoteDescriptor->getUUID().toString(), pNewRemoteDescriptor));
+
+ offset++;
+ } // while true
+ //m_haveCharacteristics = true; // Remember that we have received the characteristics.
+ ESP_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", offset);
+} // getDescriptors
+
+
+/**
+ * @brief Retrieve the map of descriptors keyed by UUID.
+ */
+std::map<std::string, BLERemoteDescriptor *>* BLERemoteCharacteristic::getDescriptors() {
+ return &m_descriptorMap;
+} // getDescriptors
+
+
+/**
+ * @brief Get the handle for this characteristic.
+ * @return The handle for this characteristic.
+ */
+uint16_t BLERemoteCharacteristic::getHandle() {
+ //ESP_LOGD(LOG_TAG, ">> getHandle: Characteristic: %s", getUUID().toString().c_str());
+ //ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", m_handle, m_handle);
+ return m_handle;
+} // getHandle
+
+
+/**
+ * @brief Get the descriptor instance with the given UUID that belongs to this characteristic.
+ * @param [in] uuid The UUID of the descriptor to find.
+ * @return The Remote descriptor (if present) or null if not present.
+ */
+BLERemoteDescriptor* BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) {
+ ESP_LOGD(LOG_TAG, ">> getDescriptor: uuid: %s", uuid.toString().c_str());
+ std::string v = uuid.toString();
+ for (auto &myPair : m_descriptorMap) {
+ if (myPair.first == v) {
+ ESP_LOGD(LOG_TAG, "<< getDescriptor: found");
+ return myPair.second;
+ }
+ }
+ ESP_LOGD(LOG_TAG, "<< getDescriptor: Not found");
+ return nullptr;
+} // getDescriptor
+
+
+/**
+ * @brief Get the remote service associated with this characteristic.
+ * @return The remote service associated with this characteristic.
+ */
+BLERemoteService* BLERemoteCharacteristic::getRemoteService() {
+ return m_pRemoteService;
+} // getRemoteService
+
+
+/**
+ * @brief Get the UUID for this characteristic.
+ * @return The UUID for this characteristic.
+ */
+BLEUUID BLERemoteCharacteristic::getUUID() {
+ return m_uuid;
+} // getUUID
+
+
+/**
+ * @brief Read an unsigned 16 bit value
+ * @return The unsigned 16 bit value.
+ */
+uint16_t BLERemoteCharacteristic::readUInt16(void) {
+ std::string value = readValue();
+ if (value.length() >= 2) {
+ return *(uint16_t*)(value.data());
+ }
+ return 0;
+} // readUInt16
+
+
+/**
+ * @brief Read an unsigned 32 bit value.
+ * @return the unsigned 32 bit value.
+ */
+uint32_t BLERemoteCharacteristic::readUInt32(void) {
+ std::string value = readValue();
+ if (value.length() >= 4) {
+ return *(uint32_t*)(value.data());
+ }
+ return 0;
+} // readUInt32
+
+
+/**
+ * @brief Read a byte value
+ * @return The value as a byte
+ */
+uint8_t BLERemoteCharacteristic::readUInt8(void) {
+ std::string value = readValue();
+ if (value.length() >= 1) {
+ return (uint8_t)value[0];
+ }
+ return 0;
+} // readUInt8
+
+
+/**
+ * @brief Read the value of the remote characteristic.
+ * @return The value of the remote characteristic.
+ */
+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.
+ // This is an asynchronous request which means that we must block waiting for the response
+ // to become available.
+ esp_err_t errRc = ::esp_ble_gattc_read_char(
+ m_pRemoteService->getClient()->getGattcIf(),
+ m_pRemoteService->getClient()->getConnId(), // The connection ID to the BLE server
+ getHandle(), // The handle of this characteristic
+ ESP_GATT_AUTH_REQ_NONE); // Security
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return "";
+ }
+
+ // Block waiting for the event that indicates that the read has completed. When it has, the std::string found
+ // in m_value will contain our data.
+ m_semaphoreReadCharEvt.wait("readValue");
+
+ ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
+ return m_value;
+} // readValue
+
+
+/**
+ * @brief Register for notifications.
+ * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
+ * unregistering a notification.
+ * @return N/A.
+ */
+void BLERemoteCharacteristic::registerForNotify(
+ void (*notifyCallback)(
+ BLERemoteCharacteristic* pBLERemoteCharacteristic,
+ uint8_t* pData,
+ size_t length,
+ bool isNotify)) {
+ ESP_LOGD(LOG_TAG, ">> registerForNotify(): %s", toString().c_str());
+
+ m_notifyCallback = notifyCallback; // Save the notification callback.
+
+ m_semaphoreRegForNotifyEvt.take("registerForNotify");
+
+ if (notifyCallback != nullptr) { // If we have a callback function, then this is a registration.
+ esp_err_t errRc = ::esp_ble_gattc_register_for_notify(
+ m_pRemoteService->getClient()->getGattcIf(),
+ *m_pRemoteService->getClient()->getPeerAddress().getNative(),
+ getHandle()
+ );
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_register_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ } // End Register
+ else { // If we weren't passed a callback function, then this is an unregistration.
+ esp_err_t errRc = ::esp_ble_gattc_unregister_for_notify(
+ m_pRemoteService->getClient()->getGattcIf(),
+ *m_pRemoteService->getClient()->getPeerAddress().getNative(),
+ getHandle()
+ );
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_unregister_for_notify: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ }
+ } // End Unregister
+
+ m_semaphoreRegForNotifyEvt.wait("registerForNotify");
+
+ ESP_LOGD(LOG_TAG, "<< registerForNotify()");
+} // registerForNotify
+
+
+/**
+ * @brief Delete the descriptors in the descriptor map.
+ * We maintain a map called m_descriptorMap that contains pointers to BLERemoteDescriptors
+ * object references. Since we allocated these in this class, we are also responsible for deleteing
+ * them. This method does just that.
+ * @return N/A.
+ */
+void BLERemoteCharacteristic::removeDescriptors() {
+ // Iterate through all the descriptors releasing their storage and erasing them from the map.
+ for (auto &myPair : m_descriptorMap) {
+ m_descriptorMap.erase(myPair.first);
+ delete myPair.second;
+ }
+ m_descriptorMap.clear(); // Technically not neeeded, but just to be sure.
+} // removeCharacteristics
+
+
+/**
+ * @brief Convert a BLERemoteCharacteristic to a string representation;
+ * @return a String representation.
+ */
+std::string BLERemoteCharacteristic::toString() {
+ std::ostringstream ss;
+ ss << "Characteristic: uuid: " << m_uuid.toString() <<
+ ", handle: " << getHandle() << " 0x" << std::hex << getHandle() <<
+ ", props: " << BLEUtils::characteristicPropertiesToString(m_charProp);
+ return ss.str();
+} // toString
+
+
+/**
+ * @brief Write the new value for the characteristic.
+ * @param [in] newValue The new value to write.
+ * @param [in] response Do we expect a response?
+ * @return N/A.
+ */
+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.
+ esp_err_t errRc = ::esp_ble_gattc_write_char(
+ m_pRemoteService->getClient()->getGattcIf(),
+ m_pRemoteService->getClient()->getConnId(),
+ getHandle(),
+ newValue.length(),
+ (uint8_t*)newValue.data(),
+ response?ESP_GATT_WRITE_TYPE_RSP:ESP_GATT_WRITE_TYPE_NO_RSP,
+ ESP_GATT_AUTH_REQ_NONE
+ );
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreWriteCharEvt.wait("writeValue");
+
+ ESP_LOGD(LOG_TAG, "<< writeValue");
+} // writeValue
+
+
+/**
+ * @brief Write the new value for the characteristic.
+ *
+ * This is a convenience function. Many BLE characteristics are a single byte of data.
+ * @param [in] newValue The new byte value to write.
+ * @param [in] response Whether we require a response from the write.
+ * @return N/A.
+ */
+void BLERemoteCharacteristic::writeValue(uint8_t newValue, bool response) {
+ writeValue(std::string(reinterpret_cast<char*>(&newValue), 1), response);
+} // writeValue
+
+
+/**
+ * @brief Write the new value for the characteristic from a data buffer.
+ * @param [in] data A pointer to a data buffer.
+ * @param [in] length The length of the data in the data buffer.
+ * @param [in] response Whether we require a response from the write.
+ */
+void BLERemoteCharacteristic::writeValue(uint8_t* data, size_t length, bool response) {
+ writeValue(std::string((char *)data, length), response);
+} // writeValue
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLERemoteCharacteristic.h b/sensor/patchedBLE/src/BLERemoteCharacteristic.h
new file mode 100644
index 0000000..6f23f49
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteCharacteristic.h
@@ -0,0 +1,85 @@
+/*
+ * BLERemoteCharacteristic.h
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
+#define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <string>
+
+#include <esp_gattc_api.h>
+
+#include "BLERemoteService.h"
+#include "BLERemoteDescriptor.h"
+#include "BLEUUID.h"
+#include "FreeRTOS.h"
+
+class BLERemoteService;
+class BLERemoteDescriptor;
+
+/**
+ * @brief A model of a remote %BLE characteristic.
+ */
+class BLERemoteCharacteristic {
+public:
+ ~BLERemoteCharacteristic();
+
+ // Public member functions
+ bool canBroadcast();
+ bool canIndicate();
+ bool canNotify();
+ bool canRead();
+ bool canWrite();
+ bool canWriteNoResponse();
+ BLERemoteDescriptor* getDescriptor(BLEUUID uuid);
+ std::map<std::string, BLERemoteDescriptor *>* getDescriptors();
+ uint16_t getHandle();
+ BLEUUID getUUID();
+ std::string readValue(void);
+ uint8_t readUInt8(void);
+ uint16_t readUInt16(void);
+ uint32_t readUInt32(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);
+ std::string toString(void);
+
+private:
+ BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, esp_gatt_char_prop_t charProp, BLERemoteService* pRemoteService);
+ friend class BLEClient;
+ friend class BLERemoteService;
+ friend class BLERemoteDescriptor;
+
+ // Private member functions
+ void gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* evtParam);
+
+
+ BLERemoteService* getRemoteService();
+ void removeDescriptors();
+ void retrieveDescriptors();
+
+ // Private properties
+ BLEUUID m_uuid;
+ esp_gatt_char_prop_t m_charProp;
+ uint16_t m_handle;
+ BLERemoteService* m_pRemoteService;
+ FreeRTOS::Semaphore m_semaphoreReadCharEvt = FreeRTOS::Semaphore("ReadCharEvt");
+ FreeRTOS::Semaphore m_semaphoreRegForNotifyEvt = FreeRTOS::Semaphore("RegForNotifyEvt");
+ FreeRTOS::Semaphore m_semaphoreWriteCharEvt = FreeRTOS::Semaphore("WriteCharEvt");
+ std::string m_value;
+ void (*m_notifyCallback)(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
+
+ // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID.
+ std::map<std::string, BLERemoteDescriptor*> m_descriptorMap;
+}; // BLERemoteCharacteristic
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */
diff --git a/sensor/patchedBLE/src/BLERemoteDescriptor.cpp b/sensor/patchedBLE/src/BLERemoteDescriptor.cpp
new file mode 100644
index 0000000..4d14ba8
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteDescriptor.cpp
@@ -0,0 +1,185 @@
+/*
+ * BLERemoteDescriptor.cpp
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include "BLERemoteDescriptor.h"
+#include "GeneralUtils.h"
+#include <esp_log.h>
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLERemoteDescriptor";
+
+
+BLERemoteDescriptor::BLERemoteDescriptor(
+ uint16_t handle,
+ BLEUUID uuid,
+ BLERemoteCharacteristic* pRemoteCharacteristic) {
+
+ m_handle = handle;
+ m_uuid = uuid;
+ m_pRemoteCharacteristic = pRemoteCharacteristic;
+}
+
+
+/**
+ * @brief Retrieve the handle associated with this remote descriptor.
+ * @return The handle associated with this remote descriptor.
+ */
+uint16_t BLERemoteDescriptor::getHandle() {
+ return m_handle;
+} // 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.
+ */
+BLEUUID BLERemoteDescriptor::getUUID() {
+ return m_uuid;
+} // 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.
+ esp_err_t errRc = ::esp_ble_gattc_read_char_descr(
+ m_pRemoteCharacteristic->getRemoteService()->getClient()->getGattcIf(),
+ m_pRemoteCharacteristic->getRemoteService()->getClient()->getConnId(), // The connection ID to the BLE server
+ getHandle(), // The handle of this characteristic
+ ESP_GATT_AUTH_REQ_NONE); // Security
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_read_char: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return "";
+ }
+
+ // Block waiting for the event that indicates that the read has completed. When it has, the std::string found
+ // in m_value will contain our data.
+ m_semaphoreReadDescrEvt.wait("readValue");
+
+ ESP_LOGD(LOG_TAG, "<< readValue(): length: %d", m_value.length());
+ return m_value;
+} // readValue
+
+
+uint8_t BLERemoteDescriptor::readUInt8(void) {
+ std::string value = readValue();
+ if (value.length() >= 1) {
+ return (uint8_t)value[0];
+ }
+ return 0;
+} // readUInt8
+
+
+uint16_t BLERemoteDescriptor::readUInt16(void) {
+ std::string value = readValue();
+ if (value.length() >= 2) {
+ return *(uint16_t*)(value.data());
+ }
+ return 0;
+} // readUInt16
+
+
+uint32_t BLERemoteDescriptor::readUInt32(void) {
+ std::string value = readValue();
+ if (value.length() >= 4) {
+ return *(uint32_t*)(value.data());
+ }
+ return 0;
+} // readUInt32
+
+
+/**
+ * @brief Return a string representation of this BLE Remote Descriptor.
+ * @retun A string representation of this BLE Remote Descriptor.
+ */
+std::string BLERemoteDescriptor::toString(void) {
+ std::stringstream ss;
+ ss << "handle: " << getHandle() << ", uuid: " << getUUID().toString();
+ return ss.str();
+} // toString
+
+
+/**
+ * @brief Write data to the BLE Remote Descriptor.
+ * @param [in] data The data to send to the remote descriptor.
+ * @param [in] length The length of the data to send.
+ * @param [in] response True if we expect a response.
+ */
+void BLERemoteDescriptor::writeValue(
+ uint8_t* data,
+ 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(),
+ getHandle(),
+ length, // Data length
+ data, // Data
+ ESP_GATT_WRITE_TYPE_NO_RSP,
+ ESP_GATT_AUTH_REQ_NONE
+ );
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_write_char_descr: %d", errRc);
+ }
+ ESP_LOGD(LOG_TAG, "<< writeValue");
+} // writeValue
+
+
+/**
+ * @brief Write data represented as a string to the BLE Remote Descriptor.
+ * @param [in] newValue The data to send to the remote descriptor.
+ * @param [in] response True if we expect a response.
+ */
+void BLERemoteDescriptor::writeValue(
+ std::string newValue,
+ bool response) {
+ writeValue(newValue.data(), newValue.length());
+} // writeValue
+
+
+/**
+ * @brief Write a byte value to the Descriptor.
+ * @param [in] The single byte to write.
+ * @param [in] True if we expect a response.
+ */
+void BLERemoteDescriptor::writeValue(
+ uint8_t newValue,
+ bool response) {
+ writeValue(&newValue, 1, response);
+} // writeValue
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLERemoteDescriptor.h b/sensor/patchedBLE/src/BLERemoteDescriptor.h
new file mode 100644
index 0000000..7bbc48f
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteDescriptor.h
@@ -0,0 +1,55 @@
+/*
+ * BLERemoteDescriptor.h
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
+#define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <string>
+
+#include <esp_gattc_api.h>
+
+#include "BLERemoteCharacteristic.h"
+#include "BLEUUID.h"
+#include "FreeRTOS.h"
+
+class BLERemoteCharacteristic;
+/**
+ * @brief A model of remote %BLE descriptor.
+ */
+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 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);
+
+
+private:
+ friend class BLERemoteCharacteristic;
+ BLERemoteDescriptor(
+ uint16_t handle,
+ BLEUUID uuid,
+ BLERemoteCharacteristic* pRemoteCharacteristic
+ );
+ uint16_t m_handle; // Server handle of this descriptor.
+ BLEUUID m_uuid; // UUID of this descriptor.
+ std::string m_value; // Last received value of the descriptor.
+ BLERemoteCharacteristic* m_pRemoteCharacteristic; // Reference to the Remote characteristic of which this descriptor is associated.
+ FreeRTOS::Semaphore m_semaphoreReadDescrEvt = FreeRTOS::Semaphore("ReadDescrEvt");
+
+
+};
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */
diff --git a/sensor/patchedBLE/src/BLERemoteService.cpp b/sensor/patchedBLE/src/BLERemoteService.cpp
new file mode 100644
index 0000000..78ff991
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteService.cpp
@@ -0,0 +1,328 @@
+/*
+ * BLERemoteService.cpp
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <sstream>
+#include "BLERemoteService.h"
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+#include <esp_log.h>
+#include <esp_err.h>
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLERemoteService";
+
+BLERemoteService::BLERemoteService(
+ esp_gatt_id_t srvcId,
+ BLEClient* pClient,
+ uint16_t startHandle,
+ uint16_t endHandle
+ ) {
+
+ ESP_LOGD(LOG_TAG, ">> BLERemoteService()");
+ m_srvcId = srvcId;
+ m_pClient = pClient;
+ m_uuid = BLEUUID(m_srvcId);
+ m_haveCharacteristics = false;
+ m_startHandle = startHandle;
+ m_endHandle = endHandle;
+
+ ESP_LOGD(LOG_TAG, "<< BLERemoteService()");
+}
+
+
+BLERemoteService::~BLERemoteService() {
+ removeCharacteristics();
+}
+
+/*
+static bool compareSrvcId(esp_gatt_srvc_id_t id1, esp_gatt_srvc_id_t id2) {
+ if (id1.id.inst_id != id2.id.inst_id) {
+ return false;
+ }
+ if (!BLEUUID(id1.id.uuid).equals(BLEUUID(id2.id.uuid))) {
+ return false;
+ }
+ return true;
+} // compareSrvcId
+*/
+
+/**
+ * @brief Handle GATT Client events
+ */
+void BLERemoteService::gattClientEventHandler(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t *evtParam) {
+ switch(event) {
+ //
+ // ESP_GATTC_GET_CHAR_EVT
+ //
+ // get_char:
+ // - esp_gatt_status_t status
+ // - uin1t6_t conn_id
+ // - esp_gatt_srvc_id_t srvc_id
+ // - esp_gatt_id_t char_id
+ // - esp_gatt_char_prop_t char_prop
+ //
+ /*
+ case ESP_GATTC_GET_CHAR_EVT: {
+ // Is this event for this service? If yes, then the local srvc_id and the event srvc_id will be
+ // the same.
+ if (compareSrvcId(m_srvcId, evtParam->get_char.srvc_id) == false) {
+ break;
+ }
+
+ // If the status is NOT OK then we have a problem and continue.
+ if (evtParam->get_char.status != ESP_GATT_OK) {
+ m_semaphoreGetCharEvt.give();
+ break;
+ }
+
+ // This is an indication that we now have the characteristic details for a characteristic owned
+ // by this service so remember it.
+ m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(
+ BLEUUID(evtParam->get_char.char_id.uuid).toString(),
+ new BLERemoteCharacteristic(evtParam->get_char.char_id, evtParam->get_char.char_prop, this) ));
+
+
+ // Now that we have received a characteristic, lets ask for the next one.
+ esp_err_t errRc = ::esp_ble_gattc_get_characteristic(
+ m_pClient->getGattcIf(),
+ m_pClient->getConnId(),
+ &m_srvcId,
+ &evtParam->get_char.char_id);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ break;
+ }
+
+ //m_semaphoreGetCharEvt.give();
+ break;
+ } // ESP_GATTC_GET_CHAR_EVT
+*/
+ default: {
+ break;
+ }
+ } // switch
+
+ // Send the event to each of the characteristics owned by this service.
+ for (auto &myPair : m_characteristicMap) {
+ myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
+ }
+} // 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));
+} // getCharacteristic
+
+
+/**
+ * @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
+// ------
+// We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the
+// device what characteristics it has in which case we have nothing to match against. If we have not
+// asked the device about its characteristics, then we do that now. Once we get the results we can then
+// examine the characteristics map to see if it has the characteristic we are looking for.
+ if (!m_haveCharacteristics) {
+ retrieveCharacteristics();
+ }
+ std::string v = uuid.toString();
+ for (auto &myPair : m_characteristicMap) {
+ if (myPair.first == v) {
+ return myPair.second;
+ }
+ }
+ throw new BLEUuidNotFoundException();
+} // getCharacteristic
+
+
+/**
+ * @brief Retrieve all the characteristics for this service.
+ * This function will not return until we have all the characteristics.
+ * @return N/A
+ */
+void BLERemoteService::retrieveCharacteristics() {
+
+ ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
+
+ removeCharacteristics(); // Forget any previous characteristics.
+
+ uint16_t offset = 0;
+ esp_gattc_char_elem_t result;
+ while(1) {
+ uint16_t count = 1;
+ esp_gatt_status_t status = ::esp_ble_gattc_get_all_char(
+ getClient()->getGattcIf(),
+ getClient()->getConnId(),
+ m_startHandle,
+ m_endHandle,
+ &result,
+ &count,
+ offset
+ );
+
+ if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
+ break;
+ }
+
+ if (status != ESP_GATT_OK) { // If we got an error, end.
+ ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
+ break;
+ }
+
+ if (count == 0) { // If we failed to get any new records, end.
+ break;
+ }
+
+ ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
+
+ // We now have a new characteristic ... let us add that to our set of known characteristics
+ BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(
+ result.char_handle,
+ BLEUUID(result.uuid),
+ result.properties,
+ this
+ );
+
+ 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).
+
+ m_haveCharacteristics = true; // Remember that we have received the characteristics.
+ ESP_LOGD(LOG_TAG, "<< getCharacteristics()");
+} // getCharacteristics
+
+
+/**
+ * @brief Retrieve a map of all the characteristics of this service.
+ * @return A map of all the characteristics of this service.
+ */
+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
+ // call and does not return until all the characteristics are available.
+ if (!m_haveCharacteristics) {
+ retrieveCharacteristics();
+ }
+ ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str());
+ return &m_characteristicMap;
+} // 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_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.
+ * We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic
+ * object references. Since we allocated these in this class, we are also responsible for deleteing
+ * them. This method does just that.
+ * @return N/A.
+ */
+void BLERemoteService::removeCharacteristics() {
+ for (auto &myPair : m_characteristicMap) {
+ 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.
+ * @return A string representation of this remote service.
+ */
+std::string BLERemoteService::toString() {
+ std::ostringstream ss;
+ ss << "Service: uuid: " + m_uuid.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.second->toString();
+ // myPair.second is the value
+ }
+ return ss.str();
+} // toString
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLERemoteService.h b/sensor/patchedBLE/src/BLERemoteService.h
new file mode 100644
index 0000000..222c6e4
--- /dev/null
+++ b/sensor/patchedBLE/src/BLERemoteService.h
@@ -0,0 +1,85 @@
+/*
+ * BLERemoteService.h
+ *
+ * Created on: Jul 8, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
+#define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <map>
+
+#include "BLEClient.h"
+#include "BLERemoteCharacteristic.h"
+#include "BLEUUID.h"
+#include "FreeRTOS.h"
+
+class BLEClient;
+class BLERemoteCharacteristic;
+
+
+/**
+ * @brief A model of a remote %BLE service.
+ */
+class BLERemoteService {
+public:
+
+ virtual ~BLERemoteService();
+
+ // Public methods
+ 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:
+ // Private constructor ... never meant to be created by a user application.
+ BLERemoteService(esp_gatt_id_t srvcId, BLEClient* pClient, uint16_t startHandle, uint16_t endHandle);
+
+ // Friends
+ friend class BLEClient;
+ friend class BLERemoteCharacteristic;
+
+ // Private methods
+ void retrieveCharacteristics(void); // Retrieve the characteristics from the BLE Server.
+ esp_gatt_id_t* getSrvcId(void);
+ 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<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;
+
+ bool m_haveCharacteristics; // Have we previously obtained the characteristics.
+ BLEClient* m_pClient;
+ FreeRTOS::Semaphore m_semaphoreGetCharEvt = FreeRTOS::Semaphore("GetCharEvt");
+ esp_gatt_id_t m_srvcId;
+ 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 */
+#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */
diff --git a/sensor/patchedBLE/src/BLEScan.cpp b/sensor/patchedBLE/src/BLEScan.cpp
new file mode 100644
index 0000000..3046b7c
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEScan.cpp
@@ -0,0 +1,292 @@
+/*
+ * BLEScan.cpp
+ *
+ * Created on: Jul 1, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+
+#include <esp_log.h>
+#include <esp_err.h>
+
+#include <map>
+
+#include "BLEAdvertisedDevice.h"
+#include "BLEScan.h"
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLEScan";
+
+
+/**
+ * Constructor
+ */
+BLEScan::BLEScan() {
+ m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE; // Default is a passive scan.
+ m_scan_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
+ 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
+
+
+/**
+ * @brief Handle GAP events related to scans.
+ * @param [in] event The event type for this event.
+ * @param [in] param Parameter data for this event.
+ */
+void BLEScan::handleGAPEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param) {
+
+ switch(event) {
+
+ // ESP_GAP_BLE_SCAN_RESULT_EVT
+ // ---------------------------
+ // scan_rst:
+ // esp_gap_search_evt_t search_evt
+ // esp_bd_addr_t bda
+ // esp_bt_dev_type_t dev_type
+ // esp_ble_addr_type_t ble_addr_type
+ // esp_ble_evt_type_t ble_evt_type
+ // int rssi
+ // uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX]
+ // int flag
+ // int num_resps
+ // uint8_t adv_data_len
+ // uint8_t scan_rsp_len
+ case ESP_GAP_BLE_SCAN_RESULT_EVT: {
+
+ switch(param->scan_rst.search_evt) {
+ //
+ // ESP_GAP_SEARCH_INQ_CMPL_EVT
+ //
+ // Event that indicates that the duration allowed for the search has completed or that we have been
+ // asked to stop.
+ case ESP_GAP_SEARCH_INQ_CMPL_EVT: {
+ m_stopped = true;
+ if (m_scanCompleteCB != nullptr) {
+ m_scanCompleteCB(m_scanResults);
+ }
+ m_semaphoreScanEnd.give();
+ break;
+ } // ESP_GAP_SEARCH_INQ_CMPL_EVT
+
+ //
+ // ESP_GAP_SEARCH_INQ_RES_EVT
+ //
+ // Result that has arrived back from a Scan inquiry.
+ case ESP_GAP_SEARCH_INQ_RES_EVT: {
+ if (m_stopped) { // If we are not scanning, nothing to do with the extra results.
+ break;
+ }
+
+// Examine our list of previously scanned addresses and, if we found this one already,
+// ignore it.
+ BLEAddress advertisedAddress(param->scan_rst.bda);
+ bool found = false;
+
+ for (int i=0; i<m_scanResults.getCount(); i++) {
+ if (m_scanResults.getDevice(i).getAddress().equals(advertisedAddress)) {
+ found = true;
+ break;
+ }
+ }
+ 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;
+ }
+
+ // We now construct a model of the advertised device that we have just found for the first
+ // time.
+ BLEAdvertisedDevice advertisedDevice;
+ advertisedDevice.setAddress(advertisedAddress);
+ advertisedDevice.setRSSI(param->scan_rst.rssi);
+ advertisedDevice.setAdFlag(param->scan_rst.flag);
+ advertisedDevice.parseAdvertisement((uint8_t*)param->scan_rst.ble_adv);
+ advertisedDevice.setScan(this);
+
+ if (m_pAdvertisedDeviceCallbacks) {
+ m_pAdvertisedDeviceCallbacks->onResult(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
+
+ default: {
+ break;
+ }
+ } // switch - search_evt
+
+
+ break;
+ } // ESP_GAP_BLE_SCAN_RESULT_EVT
+
+ default: {
+ break;
+ } // default
+ } // End switch
+} // gapEventHandler
+
+
+/**
+ * @brief Should we perform an active or passive scan?
+ * The default is a passive scan. An active scan means that we will wish a scan response.
+ * @param [in] active If true, we perform an active scan otherwise a passive scan.
+ * @return N/A.
+ */
+void BLEScan::setActiveScan(bool active) {
+ if (active) {
+ m_scan_params.scan_type = BLE_SCAN_TYPE_ACTIVE;
+ } else {
+ m_scan_params.scan_type = BLE_SCAN_TYPE_PASSIVE;
+ }
+} // setActiveScan
+
+
+/**
+ * @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, bool wantDuplicates) {
+ m_wantDuplicates = wantDuplicates;
+ m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks;
+} // setAdvertisedDeviceCallbacks
+
+
+/**
+ * @brief Set the interval to scan.
+ * @param [in] The interval in msecs.
+ */
+void BLEScan::setInterval(uint16_t intervalMSecs) {
+ m_scan_params.scan_interval = intervalMSecs / 0.625;
+} // setInterval
+
+
+/**
+ * @brief Set the window to actively scan.
+ * @param [in] windowMSecs How long to actively scan.
+ */
+void BLEScan::setWindow(uint16_t windowMSecs) {
+ m_scan_params.scan_window = windowMSecs / 0.625;
+} // setWindow
+
+
+/**
+ * @brief Start scanning.
+ * @param [in] duration The duration in seconds for which to scan.
+ * @param [in] scanCompleteCB A function to be called when scanning has completed.
+ * @return True if scan started or false if there was an error.
+ */
+bool BLEScan::start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults)) {
+ ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
+
+ m_semaphoreScanEnd.take(std::string("start"));
+ m_scanCompleteCB = scanCompleteCB; // Save the callback to be invoked when the scan completes.
+
+ m_scanResults.m_vectorAdvertisedDevices.clear();
+
+ esp_err_t errRc = ::esp_ble_gap_set_scan_params(&m_scan_params);
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_set_scan_params: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
+ m_semaphoreScanEnd.give();
+ return false;
+ }
+
+ errRc = ::esp_ble_gap_start_scanning(duration);
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_start_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
+ m_semaphoreScanEnd.give();
+ return false;
+ }
+
+ m_stopped = false;
+
+ ESP_LOGD(LOG_TAG, "<< start()");
+ return true;
+} // start
+
+
+/**
+ * @brief Start scanning and block until scanning has been completed.
+ * @param [in] duration The duration in seconds for which to scan.
+ * @return The BLEScanResults.
+ */
+BLEScanResults BLEScan::start(uint32_t duration) {
+ if(start(duration, nullptr)) {
+ m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release.
+ }
+ return m_scanResults;
+} // start
+
+
+/**
+ * @brief Stop an in progress scan.
+ * @return N/A.
+ */
+void BLEScan::stop() {
+ ESP_LOGD(LOG_TAG, ">> stop()");
+
+ esp_err_t errRc = ::esp_ble_gap_stop_scanning();
+
+ m_stopped = true;
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gap_stop_scanning: err: %d, text: %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreScanEnd.give();
+
+ ESP_LOGD(LOG_TAG, "<< stop()");
+} // stop
+
+
+/**
+ * @brief Dump the scan results to the log.
+ */
+void BLEScanResults::dump() {
+ ESP_LOGD(LOG_TAG, ">> Dump scan results:");
+ for (int i=0; i<getCount(); i++) {
+ ESP_LOGD(LOG_TAG, "- %s", getDevice(i).toString().c_str());
+ }
+} // dump
+
+
+/**
+ * @brief Return the count of devices found in the last scan.
+ * @return The number of devices found in the last scan.
+ */
+int BLEScanResults::getCount() {
+ return m_vectorAdvertisedDevices.size();
+} // getCount
+
+
+/**
+ * @brief Return the specified device at the given index.
+ * The index should be between 0 and getCount()-1.
+ * @param [in] i The index of the device.
+ * @return The device at the specified index.
+ */
+BLEAdvertisedDevice BLEScanResults::getDevice(uint32_t i) {
+ return m_vectorAdvertisedDevices.at(i);
+}
+
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEScan.h b/sensor/patchedBLE/src/BLEScan.h
new file mode 100644
index 0000000..76c7c7c
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEScan.h
@@ -0,0 +1,79 @@
+/*
+ * BLEScan.h
+ *
+ * Created on: Jul 1, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_
+#define COMPONENTS_CPP_UTILS_BLESCAN_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gap_ble_api.h>
+
+#include <vector>
+#include "BLEAdvertisedDevice.h"
+#include "BLEClient.h"
+#include "FreeRTOS.h"
+
+class BLEAdvertisedDevice;
+class BLEAdvertisedDeviceCallbacks;
+class BLEClient;
+class BLEScan;
+
+
+/**
+ * @brief The result of having performed a scan.
+ * When a scan completes, we have a set of found devices. Each device is described
+ * by a BLEAdvertisedDevice object. The number of items in the set is given by
+ * getCount(). We can retrieve a device by calling getDevice() passing in the
+ * index (starting at 0) of the desired device.
+ */
+class BLEScanResults {
+public:
+ void dump();
+ int getCount();
+ BLEAdvertisedDevice getDevice(uint32_t i);
+
+private:
+ friend BLEScan;
+ std::vector<BLEAdvertisedDevice> m_vectorAdvertisedDevices;
+};
+
+/**
+ * @brief Perform and manage %BLE scans.
+ *
+ * Scanning is associated with a %BLE client that is attempting to locate BLE servers.
+ */
+class BLEScan {
+public:
+ void setActiveScan(bool active);
+ void setAdvertisedDeviceCallbacks(
+ BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks,
+ bool wantDuplicates = false);
+ void setInterval(uint16_t intervalMSecs);
+ void setWindow(uint16_t windowMSecs);
+ bool start(uint32_t duration, void (*scanCompleteCB)(BLEScanResults));
+ BLEScanResults start(uint32_t duration);
+ void stop();
+
+private:
+ BLEScan(); // One doesn't create a new instance instead one asks the BLEDevice for the singleton.
+ friend class BLEDevice;
+ void handleGAPEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param);
+ void parseAdvertisement(BLEClient* pRemoteDevice, uint8_t *payload);
+
+
+ esp_ble_scan_params_t m_scan_params;
+ BLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks;
+ bool m_stopped;
+ FreeRTOS::Semaphore m_semaphoreScanEnd = FreeRTOS::Semaphore("ScanEnd");
+ BLEScanResults m_scanResults;
+ bool m_wantDuplicates;
+ void (*m_scanCompleteCB)(BLEScanResults scanResults);
+}; // BLEScan
+
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */
diff --git a/sensor/patchedBLE/src/BLESecurity.cpp b/sensor/patchedBLE/src/BLESecurity.cpp
new file mode 100644
index 0000000..4cf964a
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLESecurity.h b/sensor/patchedBLE/src/BLESecurity.h
new file mode 100644
index 0000000..67c41ef
--- /dev/null
+++ b/sensor/patchedBLE/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/sensor/patchedBLE/src/BLEServer.cpp b/sensor/patchedBLE/src/BLEServer.cpp
new file mode 100644
index 0000000..d5c9de6
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEServer.cpp
@@ -0,0 +1,361 @@
+/*
+ * BLEServer.cpp
+ *
+ * Created on: Apr 16, 2017
+ * Author: kolban
+ */
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_log.h>
+#include <esp_bt.h>
+#include <esp_bt_main.h>
+#include <esp_gap_ble_api.h>
+//#include <esp_gatts_api.h>
+#include "BLEDevice.h"
+#include "BLEServer.h"
+#include "BLEService.h"
+#include "BLEUtils.h"
+#include <string.h>
+#include <string>
+#include <unordered_set>
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLEServer";
+
+
+/**
+ * @brief Construct a %BLE Server
+ *
+ * This class is not designed to be individually instantiated. Instead one should create a server by asking
+ * the BLEDevice class.
+ */
+BLEServer::BLEServer() {
+ m_appId = -1;
+ m_gatts_if = -1;
+ m_connectedCount = 0;
+ m_connId = -1;
+ m_pServerCallbacks = nullptr;
+
+ //createApp(0);
+} // BLEServer
+
+
+void BLEServer::createApp(uint16_t appId) {
+ m_appId = appId;
+ registerApp();
+} // createApp
+
+
+/**
+ * @brief Create a %BLE Service.
+ *
+ * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
+ * of a new service. Every service must have a unique UUID.
+ * @param [in] uuid The UUID of the new service.
+ * @return A reference to the new service object.
+ */
+BLEService* BLEServer::createService(const char* uuid) {
+ return createService(BLEUUID(uuid));
+}
+
+
+/**
+ * @brief Create a %BLE Service.
+ *
+ * With a %BLE server, we can host one or more services. Invoking this function causes the creation of a definition
+ * of a new service. Every service must have a unique UUID.
+ * @param [in] uuid The UUID of the new service.
+ * @param [in] numHandles The maximum number of handles associated with this service.
+ * @param [in] inst_id With multiple services with the same UUID we need to provide inst_id value different for each service.
+ * @return A reference to the new service object.
+ */
+BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles, uint8_t inst_id) {
+ ESP_LOGD(LOG_TAG, ">> createService - %s", uuid.toString().c_str());
+ m_semaphoreCreateEvt.take("createService");
+
+ // Check that a service with the supplied UUID does not already exist.
+ if (m_serviceMap.getByUUID(uuid) != nullptr) {
+ ESP_LOGW(LOG_TAG, "<< Attempt to create a new service with uuid %s but a service with that UUID already exists.",
+ uuid.toString().c_str());
+ //m_semaphoreCreateEvt.give();
+ //return nullptr;
+ }
+
+ BLEService* pService = new BLEService(uuid, numHandles);
+ pService->m_id = inst_id;
+ m_serviceMap.setByUUID(uuid, pService); // Save a reference to this service being on this server.
+ pService->executeCreate(this); // Perform the API calls to actually create the service.
+
+ m_semaphoreCreateEvt.wait("createService");
+
+ ESP_LOGD(LOG_TAG, "<< createService");
+ return pService;
+} // createService
+
+
+/**
+ * @brief Retrieve the advertising object that can be used to advertise the existence of the server.
+ *
+ * @return An advertising object.
+ */
+BLEAdvertising* BLEServer::getAdvertising() {
+ return &m_bleAdvertising;
+}
+
+uint16_t BLEServer::getConnId() {
+ return m_connId;
+}
+
+
+/**
+ * @brief Return the number of connected clients.
+ * @return The number of connected clients.
+ */
+uint32_t BLEServer::getConnectedCount() {
+ return m_connectedCount;
+} // getConnectedCount
+
+
+uint16_t BLEServer::getGattsIf() {
+ return m_gatts_if;
+}
+
+/**
+ * @brief Handle a received GAP event.
+ *
+ * @param [in] event
+ * @param [in] param
+ */
+void BLEServer::handleGAPEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param) {
+ ESP_LOGD(LOG_TAG, "BLEServer ... handling GAP event!");
+ switch(event) {
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: {
+ /*
+ esp_ble_adv_params_t adv_params;
+ adv_params.adv_int_min = 0x20;
+ adv_params.adv_int_max = 0x40;
+ adv_params.adv_type = ADV_TYPE_IND;
+ adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
+ adv_params.channel_map = ADV_CHNL_ALL;
+ adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
+ ESP_LOGD(tag, "Starting advertising");
+ esp_err_t errRc = ::esp_ble_gap_start_advertising(&adv_params);
+ if (errRc != ESP_OK) {
+ ESP_LOGE(tag, "esp_ble_gap_start_advertising: rc=%d %s", errRc, espToString(errRc));
+ return;
+ }
+ */
+ break;
+ }
+
+ default:
+ break;
+ }
+} // handleGAPEvent
+
+
+
+/**
+ * @brief Handle a GATT Server Event.
+ *
+ * @param [in] event
+ * @param [in] gatts_if
+ * @param [in] param
+ *
+ */
+void BLEServer::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());
+
+ // Invoke the handler for every Service we have.
+ 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
+ // - esp_bd_addr_t remote_bda
+ // - 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++; // Increment the number of connected devices count.
+ break;
+ } // ESP_GATTS_CONNECT_EVT
+
+
+ // ESP_GATTS_CREATE_EVT
+ // Called when a new service is registered as having been created.
+ //
+ // create:
+ // * esp_gatt_status_t status
+ // * uint16_t service_handle
+ // * esp_gatt_srvc_id_t service_id
+ //
+ case ESP_GATTS_CREATE_EVT: {
+ BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid);
+ m_serviceMap.setByHandle(param->create.service_handle, pService);
+ m_semaphoreCreateEvt.give();
+ break;
+ } // ESP_GATTS_CREATE_EVT
+
+
+ // ESP_GATTS_DISCONNECT_EVT
+ //
+ // disconnect
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ // - 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
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool is_long
+ // - bool need_rsp
+ //
+ case ESP_GATTS_READ_EVT: {
+ break;
+ } // ESP_GATTS_READ_EVT
+
+
+ // ESP_GATTS_REG_EVT
+ // reg:
+ // - esp_gatt_status_t status
+ // - uint16_t app_id
+ //
+ case ESP_GATTS_REG_EVT: {
+ m_gatts_if = gatts_if;
+ m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app.
+ break;
+ } // ESP_GATTS_REG_EVT
+
+
+ // ESP_GATTS_WRITE_EVT - A request to write the value of a characteristic has arrived.
+ //
+ // write:
+ // - uint16_t conn_id
+ // - uint16_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint16_t handle
+ // - uint16_t offset
+ // - bool need_rsp
+ // - bool is_prep
+ // - uint16_t len
+ // - uint8_t* value
+ //
+ case ESP_GATTS_WRITE_EVT: {
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ ESP_LOGD(LOG_TAG, "<< handleGATTServerEvent");
+} // handleGATTServerEvent
+
+
+/**
+ * @brief Register the app.
+ *
+ * @return N/A
+ */
+void BLEServer::registerApp() {
+ ESP_LOGD(LOG_TAG, ">> registerApp - %d", m_appId);
+ m_semaphoreRegisterAppEvt.take("registerApp"); // Take the mutex, will be released by ESP_GATTS_REG_EVT event.
+ ::esp_ble_gatts_app_register(m_appId);
+ m_semaphoreRegisterAppEvt.wait("registerApp");
+ ESP_LOGD(LOG_TAG, "<< registerApp");
+} // registerApp
+
+
+/**
+ * @brief Set the server callbacks.
+ *
+ * As a %BLE server operates, it will generate server level events such as a new client connecting or a previous client
+ * disconnecting. This function can be called to register a callback handler that will be invoked when these
+ * events are detected.
+ *
+ * @param [in] pCallbacks The callbacks to be invoked.
+ */
+void BLEServer::setCallbacks(BLEServerCallbacks* pCallbacks) {
+ m_pServerCallbacks = pCallbacks;
+} // setCallbacks
+
+/*
+ * Remove service
+ */
+void BLEServer::removeService(BLEService *service) {
+ service->stop();
+ service->executeDelete();
+ m_serviceMap.removeService(service);
+}
+
+/**
+ * @brief Start advertising.
+ *
+ * Start the server advertising its existence. This is a convenience function and is equivalent to
+ * retrieving the advertising object and invoking start upon it.
+ */
+void BLEServer::startAdvertising() {
+ ESP_LOGD(LOG_TAG, ">> startAdvertising");
+ m_bleAdvertising.start();
+ ESP_LOGD(LOG_TAG, "<< startAdvertising");
+} // startAdvertising
+
+
+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
+
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEServer.h b/sensor/patchedBLE/src/BLEServer.h
new file mode 100644
index 0000000..95c55d5
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEServer.h
@@ -0,0 +1,121 @@
+/*
+ * BLEServer.h
+ *
+ * Created on: Apr 16, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_
+#define COMPONENTS_CPP_UTILS_BLESERVER_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gatts_api.h>
+
+#include <string>
+#include <string.h>
+
+#include "BLEUUID.h"
+#include "BLEAdvertising.h"
+#include "BLECharacteristic.h"
+#include "BLEService.h"
+#include "BLESecurity.h"
+#include "FreeRTOS.h"
+
+class BLEServerCallbacks;
+
+
+/**
+ * @brief A data structure that manages the %BLE servers owned by a BLE server.
+ */
+class BLEServiceMap {
+public:
+ BLEService* getByHandle(uint16_t handle);
+ BLEService* getByUUID(const char* uuid);
+ BLEService* getByUUID(BLEUUID uuid);
+ void handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+ void setByHandle(uint16_t handle, BLEService* service);
+ void setByUUID(const char* uuid, BLEService* service);
+ void setByUUID(BLEUUID uuid, BLEService* service);
+ std::string toString();
+ BLEService* getFirst();
+ BLEService* getNext();
+ void removeService(BLEService *service);
+
+private:
+ std::map<uint16_t, BLEService*> m_handleMap;
+ std::map<BLEService*, std::string> m_uuidMap;
+ std::map<BLEService*, std::string>::iterator m_iterator;
+};
+
+
+/**
+ * @brief The model of a %BLE server.
+ */
+class BLEServer {
+public:
+ uint32_t getConnectedCount();
+ BLEService* createService(const char* uuid);
+ BLEService* createService(BLEUUID uuid, uint32_t numHandles=15, uint8_t inst_id=0);
+ BLEAdvertising* getAdvertising();
+ void setCallbacks(BLEServerCallbacks* pCallbacks);
+ void startAdvertising();
+ void removeService(BLEService *service);
+
+
+private:
+ BLEServer();
+ friend class BLEService;
+ friend class BLECharacteristic;
+ friend class BLEDevice;
+ esp_ble_adv_data_t m_adv_data;
+ uint16_t m_appId;
+ BLEAdvertising m_bleAdvertising;
+ uint16_t m_connId;
+ uint32_t m_connectedCount;
+ uint16_t m_gatts_if;
+ FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt");
+ FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
+ BLEServiceMap m_serviceMap;
+ BLEServerCallbacks* m_pServerCallbacks;
+
+ void createApp(uint16_t appId);
+ uint16_t getConnId();
+ uint16_t getGattsIf();
+ void handleGAPEvent(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
+ void handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
+ void registerApp();
+}; // BLEServer
+
+
+/**
+ * @brief Callbacks associated with the operation of a %BLE server.
+ */
+class BLEServerCallbacks {
+public:
+ virtual ~BLEServerCallbacks() {};
+ /**
+ * @brief Handle a new client connection.
+ *
+ * When a new client connects, we are invoked.
+ *
+ * @param [in] pServer A reference to the %BLE server that received the client connection.
+ */
+ virtual void onConnect(BLEServer* pServer);
+
+ /**
+ * @brief Handle an existing client disconnection.
+ *
+ * When an existing client disconnects, we are invoked.
+ *
+ * @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
+ */
+ virtual void onDisconnect(BLEServer* pServer);
+}; // BLEServerCallbacks
+
+
+
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */
diff --git a/sensor/patchedBLE/src/BLEService.cpp b/sensor/patchedBLE/src/BLEService.cpp
new file mode 100644
index 0000000..340ea56
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEService.cpp
@@ -0,0 +1,435 @@
+/*
+ * BLEService.cpp
+ *
+ * Created on: Mar 25, 2017
+ * Author: kolban
+ */
+
+// A service is identified by a UUID. A service is also the container for one or more characteristics.
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_err.h>
+#include <esp_gatts_api.h>
+#include <esp_log.h>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include "BLEServer.h"
+#include "BLEService.h"
+#include "BLEUtils.h"
+#include "GeneralUtils.h"
+
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+#define NULL_HANDLE (0xffff)
+
+static const char* LOG_TAG = "BLEService"; // Tag for logging.
+
+/**
+ * @brief Construct an instance of the BLEService
+ * @param [in] uuid The UUID of the service.
+ * @param [in] numHandles The maximum number of handles associated with the service.
+ */
+BLEService::BLEService(const char* uuid, uint32_t numHandles) : BLEService(BLEUUID(uuid), numHandles) {
+}
+
+
+/**
+ * @brief Construct an instance of the BLEService
+ * @param [in] uuid The UUID of the service.
+ * @param [in] numHandles The maximum number of handles associated with the service.
+ */
+BLEService::BLEService(BLEUUID uuid, uint32_t numHandles) {
+ m_uuid = uuid;
+ m_handle = NULL_HANDLE;
+ m_pServer = nullptr;
+ //m_serializeMutex.setName("BLEService");
+ m_lastCreatedCharacteristic = nullptr;
+ m_numHandles = numHandles;
+} // BLEService
+
+
+/**
+ * @brief Create the service.
+ * Create the service.
+ * @param [in] gatts_if The handle of the GATT server interface.
+ * @return N/A.
+ */
+
+void BLEService::executeCreate(BLEServer *pServer) {
+ //ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str());
+ //getUUID(); // Needed for a weird bug fix
+ //char x[1000];
+ //memcpy(x, &m_uuid, sizeof(m_uuid));
+ //char x[10];
+ //memcpy(x, &deleteMe, 10);
+ m_pServer = pServer;
+ m_semaphoreCreateEvt.take("executeCreate"); // Take the mutex and release at event ESP_GATTS_CREATE_EVT
+
+ esp_gatt_srvc_id_t srvc_id;
+ srvc_id.is_primary = true;
+ srvc_id.id.inst_id = m_id;
+ srvc_id.id.uuid = *m_uuid.getNative();
+ esp_err_t errRc = ::esp_ble_gatts_create_service(
+ getServer()->getGattsIf(),
+ &srvc_id,
+ m_numHandles // The maximum number of handles associated with the service.
+ );
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_create_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreCreateEvt.wait("executeCreate");
+ ESP_LOGD(LOG_TAG, "<< executeCreate");
+} // executeCreate
+
+
+/**
+ * @brief Delete the service.
+ * Delete the service.
+ * @return N/A.
+ */
+
+void BLEService::executeDelete() {
+ ESP_LOGD(LOG_TAG, ">> executeDelete()");
+ m_semaphoreDeleteEvt.take("executeDelete"); // Take the mutex and release at event ESP_GATTS_DELETE_EVT
+
+ esp_err_t errRc = ::esp_ble_gatts_delete_service( getHandle() );
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "esp_ble_gatts_delete_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+
+ m_semaphoreDeleteEvt.wait("executeDelete");
+ ESP_LOGD(LOG_TAG, "<< executeDelete");
+} // executeDelete
+
+
+/**
+ * @brief Dump details of this BLE GATT service.
+ * @return N/A.
+ */
+void BLEService::dump() {
+ ESP_LOGD(LOG_TAG, "Service: uuid:%s, handle: 0x%.2x",
+ m_uuid.toString().c_str(),
+ m_handle);
+ ESP_LOGD(LOG_TAG, "Characteristics:\n%s", m_characteristicMap.toString().c_str());
+} // dump
+
+
+/**
+ * @brief Get the UUID of the service.
+ * @return the UUID of the service.
+ */
+BLEUUID BLEService::getUUID() {
+ return m_uuid;
+} // getUUID
+
+
+/**
+ * @brief Start the service.
+ * Here we wish to start the service which means that we will respond to partner requests about it.
+ * Starting a service also means that we can create the corresponding characteristics.
+ * @return Start the service.
+ */
+void BLEService::start() {
+// We ask the BLE runtime to start the service and then create each of the characteristics.
+// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event
+// obtained as a result of calling esp_ble_gatts_create_service().
+//
+ ESP_LOGD(LOG_TAG, ">> start(): Starting service (esp_ble_gatts_start_service): %s", toString().c_str());
+ if (m_handle == NULL_HANDLE) {
+ ESP_LOGE(LOG_TAG, "<< !!! We attempted to start a service but don't know its handle!");
+ return;
+ }
+
+
+ BLECharacteristic *pCharacteristic = m_characteristicMap.getFirst();
+
+ while(pCharacteristic != nullptr) {
+ m_lastCreatedCharacteristic = pCharacteristic;
+ pCharacteristic->executeCreate(this);
+
+ pCharacteristic = m_characteristicMap.getNext();
+ }
+ // Start each of the characteristics ... these are found in the m_characteristicMap.
+
+ m_semaphoreStartEvt.take("start");
+ esp_err_t errRc = ::esp_ble_gatts_start_service(m_handle);
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_start_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ m_semaphoreStartEvt.wait("start");
+
+ ESP_LOGD(LOG_TAG, "<< start()");
+} // start
+
+
+/**
+ * @brief Stop the service.
+ * @return Stop the service.
+ */
+void BLEService::stop() {
+// We ask the BLE runtime to start the service and then create each of the characteristics.
+// We start the service through its local handle which was returned in the ESP_GATTS_CREATE_EVT event
+// obtained as a result of calling esp_ble_gatts_create_service().
+//
+ ESP_LOGD(LOG_TAG, ">> stop(): Stopping service (esp_ble_gatts_stop_service): %s", toString().c_str());
+ if (m_handle == NULL_HANDLE) {
+ ESP_LOGE(LOG_TAG, "<< !!! We attempted to stop a service but don't know its handle!");
+ return;
+ }
+
+ m_semaphoreStopEvt.take("stop");
+ esp_err_t errRc = ::esp_ble_gatts_stop_service(m_handle);
+
+ if (errRc != ESP_OK) {
+ ESP_LOGE(LOG_TAG, "<< esp_ble_gatts_stop_service: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
+ return;
+ }
+ m_semaphoreStopEvt.wait("stop");
+
+ ESP_LOGD(LOG_TAG, "<< stop()");
+} // start
+
+
+/**
+ * @brief Set the handle associated with this service.
+ * @param [in] handle The handle associated with the service.
+ */
+void BLEService::setHandle(uint16_t handle) {
+ ESP_LOGD(LOG_TAG, ">> setHandle - Handle=0x%.2x, service UUID=%s)", handle, getUUID().toString().c_str());
+ if (m_handle != NULL_HANDLE) {
+ ESP_LOGE(LOG_TAG, "!!! Handle is already set %.2x", m_handle);
+ return;
+ }
+ m_handle = handle;
+ ESP_LOGD(LOG_TAG, "<< setHandle");
+} // setHandle
+
+
+/**
+ * @brief Get the handle associated with this service.
+ * @return The handle associated with this service.
+ */
+uint16_t BLEService::getHandle() {
+ return m_handle;
+} // getHandle
+
+
+/**
+ * @brief Add a characteristic to the service.
+ * @param [in] pCharacteristic A pointer to the characteristic to be added.
+ */
+void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) {
+// We maintain a mapping of characteristics owned by this service. These are managed by the
+// BLECharacteristicMap class instance found in m_characteristicMap. We add the characteristic
+// to the map and then ask the service to add the characteristic at the BLE level (ESP-IDF).
+//
+ ESP_LOGD(LOG_TAG, ">> addCharacteristic()");
+ ESP_LOGD(LOG_TAG, "Adding characteristic: uuid=%s to service: %s",
+ pCharacteristic->getUUID().toString().c_str(),
+ toString().c_str());
+
+ // Check that we don't add the same characteristic twice.
+ if (m_characteristicMap.getByUUID(pCharacteristic->getUUID()) != nullptr) {
+ ESP_LOGW(LOG_TAG, "<< Adding a new characteristic with the same UUID as a previous one");
+ //return;
+ }
+
+ // Remember this characteristic in our map of characteristics. At this point, we can lookup by UUID
+ // but not by handle. The handle is allocated to us on the ESP_GATTS_ADD_CHAR_EVT.
+ m_characteristicMap.setByUUID(pCharacteristic, pCharacteristic->getUUID());
+
+ ESP_LOGD(LOG_TAG, "<< addCharacteristic()");
+} // addCharacteristic
+
+
+/**
+ * @brief Create a new BLE Characteristic associated with this service.
+ * @param [in] uuid - The UUID of the characteristic.
+ * @param [in] properties - The properties of the characteristic.
+ * @return The new BLE characteristic.
+ */
+BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) {
+ return createCharacteristic(BLEUUID(uuid), properties);
+}
+
+
+/**
+ * @brief Create a new BLE Characteristic associated with this service.
+ * @param [in] uuid - The UUID of the characteristic.
+ * @param [in] properties - The properties of the characteristic.
+ * @return The new BLE characteristic.
+ */
+BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) {
+ BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties);
+ addCharacteristic(pCharacteristic);
+ return pCharacteristic;
+} // createCharacteristic
+
+
+/**
+ * @brief Handle a GATTS server event.
+ */
+void BLEService::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t *param) {
+
+
+ switch(event) {
+ // ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
+ // add_char:
+ // - esp_gatt_status_t status
+ // - uint16_t attr_handle
+ // - uint16_t service_handle
+ // - esp_bt_uuid_t char_uuid
+
+ // If we have reached the correct service, then locate the characteristic and remember the handle
+ // for that characteristic.
+ case ESP_GATTS_ADD_CHAR_EVT: {
+ if (m_handle == param->add_char.service_handle) {
+ BLECharacteristic *pCharacteristic = getLastCreatedCharacteristic();
+ if (pCharacteristic == nullptr) {
+ ESP_LOGE(LOG_TAG, "Expected to find characteristic with UUID: %s, but didnt!",
+ BLEUUID(param->add_char.char_uuid).toString().c_str());
+ dump();
+ break;
+ }
+ pCharacteristic->setHandle(param->add_char.attr_handle);
+ m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic);
+ //ESP_LOGD(tag, "Characteristic map: %s", m_characteristicMap.toString().c_str());
+ break;
+ } // Reached the correct service.
+ break;
+ } // ESP_GATTS_ADD_CHAR_EVT
+
+
+ // ESP_GATTS_START_EVT
+ //
+ // start:
+ // esp_gatt_status_t status
+ // uint16_t service_handle
+ case ESP_GATTS_START_EVT: {
+ if (param->start.service_handle == getHandle()) {
+ m_semaphoreStartEvt.give();
+ }
+ break;
+ } // ESP_GATTS_START_EVT
+
+ // ESP_GATTS_STOP_EVT
+ //
+ // stop:
+ // esp_gatt_status_t status
+ // uint16_t service_handle
+ //
+ case ESP_GATTS_STOP_EVT: {
+ if (param->stop.service_handle == getHandle()) {
+ m_semaphoreStopEvt.give();
+ }
+ break;
+ } // ESP_GATTS_STOP_EVT
+
+
+ // ESP_GATTS_CREATE_EVT
+ // Called when a new service is registered as having been created.
+ //
+ // create:
+ // * esp_gatt_status_t status
+ // * uint16_t service_handle
+ // * esp_gatt_srvc_id_t service_id
+ // * - esp_gatt_id id
+ // * - esp_bt_uuid uuid
+ // * - uint8_t inst_id
+ // * - bool is_primary
+ //
+ case ESP_GATTS_CREATE_EVT: {
+ if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid)) && m_id == param->create.service_id.id.inst_id) {
+ setHandle(param->create.service_handle);
+ m_semaphoreCreateEvt.give();
+ }
+ break;
+ } // ESP_GATTS_CREATE_EVT
+
+
+ // ESP_GATTS_DELETE_EVT
+ // Called when a service is deleted.
+ //
+ // delete:
+ // * esp_gatt_status_t status
+ // * uint16_t service_handle
+ //
+ case ESP_GATTS_DELETE_EVT: {
+ if (param->del.service_handle == getHandle()) {
+ m_semaphoreDeleteEvt.give();
+ }
+ break;
+ } // ESP_GATTS_DELETE_EVT
+
+ default: {
+ break;
+ } // Default
+ } // Switch
+
+ // Invoke the GATTS handler in each of the associated characteristics.
+ m_characteristicMap.handleGATTServerEvent(event, gatts_if, param);
+} // handleGATTServerEvent
+
+
+BLECharacteristic* BLEService::getCharacteristic(const char* uuid) {
+ return getCharacteristic(BLEUUID(uuid));
+}
+
+
+BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) {
+ return m_characteristicMap.getByUUID(uuid);
+}
+
+
+/**
+ * @brief Return a string representation of this service.
+ * A service is defined by:
+ * * Its UUID
+ * * Its handle
+ * @return A string representation of this service.
+ */
+std::string BLEService::toString() {
+ std::stringstream stringStream;
+ stringStream << "UUID: " << getUUID().toString() <<
+ ", handle: 0x" << std::hex << std::setfill('0') << std::setw(2) << getHandle();
+ return stringStream.str();
+} // toString
+
+
+/**
+ * @brief Get the last created characteristic.
+ * It is lamentable that this function has to exist. It returns the last created characteristic.
+ * We need this because the descriptor API is built around the notion that a new descriptor, when created,
+ * is associated with the last characteristics created and we need that information.
+ * @return The last created characteristic.
+ */
+BLECharacteristic* BLEService::getLastCreatedCharacteristic() {
+ return m_lastCreatedCharacteristic;
+} // getLastCreatedCharacteristic
+
+
+/**
+ * @brief Get the BLE server associated with this service.
+ * @return The BLEServer associated with this service.
+ */
+BLEServer* BLEService::getServer() {
+ return m_pServer;
+} // getServer
+
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEService.h b/sensor/patchedBLE/src/BLEService.h
new file mode 100644
index 0000000..93b4b2c
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEService.h
@@ -0,0 +1,104 @@
+/*
+ * BLEService.h
+ *
+ * Created on: Mar 25, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_
+#define COMPONENTS_CPP_UTILS_BLESERVICE_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <esp_gatts_api.h>
+
+#include "BLECharacteristic.h"
+#include "BLEServer.h"
+#include "BLEUUID.h"
+#include "FreeRTOS.h"
+
+class BLEServer;
+
+/**
+ * @brief A data mapping used to manage the set of %BLE characteristics known to the server.
+ */
+class BLECharacteristicMap {
+public:
+ void setByUUID(BLECharacteristic* pCharacteristic, const char* uuid);
+ void setByUUID(BLECharacteristic* pCharacteristic, BLEUUID uuid);
+ void setByHandle(uint16_t handle, BLECharacteristic* pCharacteristic);
+ BLECharacteristic* getByUUID(const char* uuid);
+ BLECharacteristic* getByUUID(BLEUUID uuid);
+ BLECharacteristic* getByHandle(uint16_t handle);
+ BLECharacteristic* getFirst();
+ BLECharacteristic* getNext();
+ std::string toString();
+ void handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+
+
+private:
+ std::map<BLECharacteristic*, std::string> m_uuidMap;
+ std::map<uint16_t, BLECharacteristic*> m_handleMap;
+ std::map<BLECharacteristic*, std::string>::iterator m_iterator;
+};
+
+
+/**
+ * @brief The model of a %BLE service.
+ *
+ */
+class BLEService {
+public:
+ void addCharacteristic(BLECharacteristic* pCharacteristic);
+ BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties);
+ BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties);
+ void dump();
+ void executeCreate(BLEServer* pServer);
+ void executeDelete();
+ BLECharacteristic* getCharacteristic(const char* uuid);
+ BLECharacteristic* getCharacteristic(BLEUUID uuid);
+ BLEUUID getUUID();
+ BLEServer* getServer();
+ void start();
+ void stop();
+ std::string toString();
+ uint16_t getHandle();
+ uint8_t m_id = 0;
+
+private:
+ BLEService(const char* uuid, uint32_t numHandles);
+ BLEService(BLEUUID uuid, uint32_t numHandles);
+ friend class BLEServer;
+ friend class BLEServiceMap;
+ friend class BLEDescriptor;
+ friend class BLECharacteristic;
+ friend class BLEDevice;
+
+ BLECharacteristicMap m_characteristicMap;
+ uint16_t m_handle;
+ BLECharacteristic* m_lastCreatedCharacteristic;
+ BLEServer* m_pServer;
+ BLEUUID m_uuid;
+
+ FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
+ FreeRTOS::Semaphore m_semaphoreDeleteEvt = FreeRTOS::Semaphore("DeleteEvt");
+ FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt");
+ FreeRTOS::Semaphore m_semaphoreStopEvt = FreeRTOS::Semaphore("StopEvt");
+
+ uint32_t m_numHandles;
+
+ BLECharacteristic* getLastCreatedCharacteristic();
+ void handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* param);
+ void setHandle(uint16_t handle);
+ //void setService(esp_gatt_srvc_id_t srvc_id);
+}; // BLEService
+
+
+#endif // CONFIG_BT_ENABLED
+#endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */
diff --git a/sensor/patchedBLE/src/BLEServiceMap.cpp b/sensor/patchedBLE/src/BLEServiceMap.cpp
new file mode 100644
index 0000000..dd828fa
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEServiceMap.cpp
@@ -0,0 +1,132 @@
+/*
+ * BLEServiceMap.cpp
+ *
+ * Created on: Jun 22, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <sstream>
+#include <iomanip>
+#include "BLEService.h"
+
+
+/**
+ * @brief Return the service by UUID.
+ * @param [in] UUID The UUID to look up the service.
+ * @return The characteristic.
+ */
+BLEService* BLEServiceMap::getByUUID(const char* uuid) {
+ return getByUUID(BLEUUID(uuid));
+}
+
+/**
+ * @brief Return the service by UUID.
+ * @param [in] UUID The UUID to look up the service.
+ * @return The characteristic.
+ */
+BLEService* BLEServiceMap::getByUUID(BLEUUID uuid) {
+ for (auto &myPair : m_uuidMap) {
+ if (myPair.first->getUUID().equals(uuid)) {
+ return myPair.first;
+ }
+ }
+ //return m_uuidMap.at(uuid.toString());
+ return nullptr;
+} // getByUUID
+
+
+/**
+ * @brief Return the service by handle.
+ * @param [in] handle The handle to look up the service.
+ * @return The service.
+ */
+BLEService* BLEServiceMap::getByHandle(uint16_t handle) {
+ return m_handleMap.at(handle);
+} // getByHandle
+
+
+/**
+ * @brief Set the service by UUID.
+ * @param [in] uuid The uuid of the service.
+ * @param [in] characteristic The service to cache.
+ * @return N/A.
+ */
+void BLEServiceMap::setByUUID(BLEUUID uuid,
+ BLEService *service) {
+ m_uuidMap.insert(std::pair<BLEService *, std::string>(service, uuid.toString()));
+} // setByUUID
+
+
+/**
+ * @brief Set the service by handle.
+ * @param [in] handle The handle of the service.
+ * @param [in] service The service to cache.
+ * @return N/A.
+ */
+void BLEServiceMap::setByHandle(uint16_t handle,
+ BLEService* service) {
+ m_handleMap.insert(std::pair<uint16_t, BLEService *>(handle, service));
+} // setByHandle
+
+
+/**
+ * @brief Return a string representation of the service map.
+ * @return A string representation of the service map.
+ */
+std::string BLEServiceMap::toString() {
+ std::stringstream stringStream;
+ stringStream << std::hex << std::setfill('0');
+ for (auto &myPair: m_handleMap) {
+ stringStream << "handle: 0x" << std::setw(2) << myPair.first << ", uuid: " + myPair.second->getUUID().toString() << "\n";
+ }
+ return stringStream.str();
+} // toString
+
+void BLEServiceMap::handleGATTServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t *param) {
+ // Invoke the handler for every Service we have.
+ for (auto &myPair : m_uuidMap) {
+ myPair.first->handleGATTServerEvent(event, gatts_if, param);
+ }
+}
+
+/**
+ * @brief Get the first service in the map.
+ * @return The first service in the map.
+ */
+BLEService* BLEServiceMap::getFirst() {
+ m_iterator = m_uuidMap.begin();
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLEService* pRet = m_iterator->first;
+ m_iterator++;
+ return pRet;
+} // getFirst
+
+/**
+ * @brief Get the next service in the map.
+ * @return The next service in the map.
+ */
+BLEService* BLEServiceMap::getNext() {
+ if (m_iterator == m_uuidMap.end()) {
+ return nullptr;
+ }
+ BLEService* pRet = m_iterator->first;
+ m_iterator++;
+ return pRet;
+} // getNext
+
+/**
+ * @brief Removes service from maps.
+ * @return N/A.
+ */
+void BLEServiceMap::removeService(BLEService *service){
+ m_handleMap.erase(service->getHandle());
+ m_uuidMap.erase(service);
+} // removeService
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEUUID.cpp b/sensor/patchedBLE/src/BLEUUID.cpp
new file mode 100644
index 0000000..9ca7cdd
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEUUID.cpp
@@ -0,0 +1,410 @@
+/*
+ * BLEUUID.cpp
+ *
+ * Created on: Jun 21, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_log.h>
+#include <string.h>
+#include <sstream>
+#include <iomanip>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "BLEUUID.h"
+static const char* LOG_TAG = "BLEUUID";
+
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+/**
+ * @brief Copy memory from source to target but in reverse order.
+ *
+ * When we move memory from one location it is normally:
+ *
+ * ```
+ * [0][1][2]...[n] -> [0][1][2]...[n]
+ * ```
+ *
+ * with this function, it is:
+ *
+ * ```
+ * [0][1][2]...[n] -> [n][n-1][n-2]...[0]
+ * ```
+ *
+ * @param [in] target The target of the copy
+ * @param [in] source The source of the copy
+ * @param [in] size The number of bytes to copy
+ */
+static void memrcpy(uint8_t* target, uint8_t* source, uint32_t size) {
+ assert(size > 0);
+ target+=(size-1); // Point target to the last byte of the target data
+ while (size > 0) {
+ *target = *source;
+ target--;
+ source++;
+ size--;
+ }
+} // memrcpy
+
+
+/**
+ * @brief Create a UUID from a string.
+ *
+ * Create a UUID from a string. There will be two possible stories here. Either the string represents
+ * a binary data field or the string represents a hex encoding of a UUID.
+ * For the hex encoding, here is an example:
+ *
+ * ```
+ * "beb5483e-36e1-4688-b7f5-ea07361b26a8"
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * 12345678-90ab-cdef-1234-567890abcdef
+ * ```
+ *
+ * This has a length of 36 characters. We need to parse this into 16 bytes.
+ *
+ * @param [in] value The string to build a UUID from.
+ */
+BLEUUID::BLEUUID(std::string value) {
+ m_valueSet = true;
+ if (value.length() == 2) {
+ m_uuid.len = ESP_UUID_LEN_16;
+ m_uuid.uuid.uuid16 = value[0] | (value[1] << 8);
+ }
+ else if (value.length() == 4) {
+ m_uuid.len = ESP_UUID_LEN_32;
+ m_uuid.uuid.uuid32 = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+ }
+ else if (value.length() == 16) {
+ m_uuid.len = ESP_UUID_LEN_128;
+ memrcpy(m_uuid.uuid.uuid128, (uint8_t*)value.data(), 16);
+ }
+ else if (value.length() == 36) {
+// If the length of the string is 36 bytes then we will assume it is a long hex string in
+// UUID format.
+ m_uuid.len = ESP_UUID_LEN_128;
+ int vals[16];
+ sscanf(value.c_str(), "%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x",
+ &vals[15],
+ &vals[14],
+ &vals[13],
+ &vals[12],
+ &vals[11],
+ &vals[10],
+ &vals[9],
+ &vals[8],
+ &vals[7],
+ &vals[6],
+ &vals[5],
+ &vals[4],
+ &vals[3],
+ &vals[2],
+ &vals[1],
+ &vals[0]
+ );
+
+ int i;
+ for (i=0; i<16; i++) {
+ m_uuid.uuid.uuid128[i] = vals[i];
+ }
+ }
+ else {
+ ESP_LOGE(LOG_TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes");
+ m_valueSet = false;
+ }
+} //BLEUUID(std::string)
+
+
+/**
+ * @brief Create a UUID from 16 bytes of memory.
+ *
+ * @param [in] pData The pointer to the start of the UUID.
+ * @param [in] size The size of the data.
+ * @param [in] msbFirst Is the MSB first in pData memory?
+ */
+BLEUUID::BLEUUID(uint8_t* pData, size_t size, bool msbFirst) {
+ if (size != 16) {
+ ESP_LOGE(LOG_TAG, "ERROR: UUID length not 16 bytes");
+ return;
+ }
+ m_uuid.len = ESP_UUID_LEN_128;
+ if (msbFirst) {
+ memrcpy(m_uuid.uuid.uuid128, pData, 16);
+ } else {
+ memcpy(m_uuid.uuid.uuid128, pData, 16);
+ }
+ m_valueSet = true;
+} // BLEUUID
+
+
+/**
+ * @brief Create a UUID from the 16bit value.
+ *
+ * @param [in] uuid The 16bit short form UUID.
+ */
+BLEUUID::BLEUUID(uint16_t uuid) {
+ m_uuid.len = ESP_UUID_LEN_16;
+ m_uuid.uuid.uuid16 = uuid;
+ m_valueSet = true;
+
+} // BLEUUID
+
+
+/**
+ * @brief Create a UUID from the 32bit value.
+ *
+ * @param [in] uuid The 32bit short form UUID.
+ */
+BLEUUID::BLEUUID(uint32_t uuid) {
+ m_uuid.len = ESP_UUID_LEN_32;
+ m_uuid.uuid.uuid32 = uuid;
+ m_valueSet = true;
+} // BLEUUID
+
+
+/**
+ * @brief Create a UUID from the native UUID.
+ *
+ * @param [in] uuid The native UUID.
+ */
+BLEUUID::BLEUUID(esp_bt_uuid_t uuid) {
+ m_uuid = uuid;
+ m_valueSet = true;
+} // BLEUUID
+
+
+/**
+ * @brief Create a UUID from the ESP32 esp_gat_id_t.
+ *
+ * @param [in] gattId The data to create the UUID from.
+ */
+BLEUUID::BLEUUID(esp_gatt_id_t gattId) : BLEUUID(gattId.uuid) {
+} // BLEUUID
+
+
+BLEUUID::BLEUUID() {
+ m_valueSet = false;
+} // 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.
+ * @return True if the UUIDs are equal and false otherwise.
+ */
+bool BLEUUID::equals(BLEUUID uuid) {
+ //ESP_LOGD(TAG, "Comparing: %s to %s", toString().c_str(), uuid.toString().c_str());
+ if (m_valueSet == false || uuid.m_valueSet == false) {
+ return false;
+ }
+
+ if (uuid.m_uuid.len != m_uuid.len) {
+ return uuid.toString() == toString();
+ }
+
+ if (uuid.m_uuid.len == ESP_UUID_LEN_16) {
+ return uuid.m_uuid.uuid.uuid16 == m_uuid.uuid.uuid16;
+ }
+
+ if (uuid.m_uuid.len == ESP_UUID_LEN_32) {
+ return uuid.m_uuid.uuid.uuid32 == m_uuid.uuid.uuid32;
+ }
+
+ return memcmp(uuid.m_uuid.uuid.uuid128, m_uuid.uuid.uuid128, 16) == 0;
+} // equals
+
+
+/**
+ * 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.
+ */
+esp_bt_uuid_t* BLEUUID::getNative() {
+ //ESP_LOGD(TAG, ">> getNative()")
+ if (m_valueSet == false) {
+ ESP_LOGD(LOG_TAG, "<< Return of un-initialized UUID!");
+ return nullptr;
+ }
+ //ESP_LOGD(TAG, "<< getNative()");
+ return &m_uuid;
+} // getNative
+
+
+/**
+ * @brief Convert a UUID to its 128 bit representation.
+ *
+ * A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
+ * will convert 16 or 32 bit representations to the full 128bit.
+ */
+BLEUUID BLEUUID::to128() {
+ //ESP_LOGD(LOG_TAG, ">> toFull() - %s", toString().c_str());
+
+ // If we either don't have a value or are already a 128 bit UUID, nothing further to do.
+ if (m_valueSet == false || m_uuid.len == ESP_UUID_LEN_128) {
+ return *this;
+ }
+
+ // If we are 16 bit or 32 bit, then set the 4 bytes of the variable part of the UUID.
+ if (m_uuid.len == ESP_UUID_LEN_16) {
+ uint16_t temp = m_uuid.uuid.uuid16;
+ m_uuid.uuid.uuid128[15] = 0;
+ m_uuid.uuid.uuid128[14] = 0;
+ m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff;
+ m_uuid.uuid.uuid128[12] = temp & 0xff;
+
+ }
+ else if (m_uuid.len == ESP_UUID_LEN_32) {
+ uint32_t temp = m_uuid.uuid.uuid32;
+ m_uuid.uuid.uuid128[15] = (temp >> 24) & 0xff;
+ m_uuid.uuid.uuid128[14] = (temp >> 16) & 0xff;
+ m_uuid.uuid.uuid128[13] = (temp >> 8) & 0xff;
+ m_uuid.uuid.uuid128[12] = temp & 0xff;
+ }
+
+ // Set the fixed parts of the UUID.
+ m_uuid.uuid.uuid128[11] = 0x00;
+ m_uuid.uuid.uuid128[10] = 0x00;
+
+ m_uuid.uuid.uuid128[9] = 0x10;
+ m_uuid.uuid.uuid128[8] = 0x00;
+
+ m_uuid.uuid.uuid128[7] = 0x80;
+ m_uuid.uuid.uuid128[6] = 0x00;
+
+ m_uuid.uuid.uuid128[5] = 0x00;
+ m_uuid.uuid.uuid128[4] = 0x80;
+ m_uuid.uuid.uuid128[3] = 0x5f;
+ m_uuid.uuid.uuid128[2] = 0x9b;
+ m_uuid.uuid.uuid128[1] = 0x34;
+ m_uuid.uuid.uuid128[0] = 0xfb;
+
+ m_uuid.len = ESP_UUID_LEN_128;
+ //ESP_LOGD(TAG, "<< toFull <- %s", toString().c_str());
+ return *this;
+} // to128
+
+
+
+
+/**
+ * @brief Get a string representation of the UUID.
+ *
+ * The format of a string is:
+ * 01234567 8901 2345 6789 012345678901
+ * 0000180d-0000-1000-8000-00805f9b34fb
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ *
+ * @return A string representation of the UUID.
+ */
+std::string BLEUUID::toString() {
+ if (m_valueSet == false) { // If we have no value, nothing to format.
+ return "<NULL>";
+ }
+
+ // If the UUIDs are 16 or 32 bit, pad correctly.
+ std::stringstream ss;
+
+ if (m_uuid.len == ESP_UUID_LEN_16) { // If the UUID is 16bit, pad correctly.
+ ss << "0000" <<
+ std::hex <<
+ std::setfill('0') <<
+ std::setw(4) <<
+ m_uuid.uuid.uuid16 <<
+ "-0000-1000-8000-00805f9b34fb";
+ return ss.str(); // Return the string
+ } // End 16bit UUID
+
+ if (m_uuid.len == ESP_UUID_LEN_32) { // If the UUID is 32bit, pad correctly.
+ ss << std::hex <<
+ std::setfill('0') <<
+ std::setw(8) <<
+ m_uuid.uuid.uuid32 <<
+ "-0000-1000-8000-00805f9b34fb";
+ return ss.str(); // return the string
+ } // End 32bit UUID
+
+ // The UUID is not 16bit or 32bit which means that it is 128bit.
+ //
+ // UUID string format:
+ // AABBCCDD-EEFF-GGHH-IIJJ-KKLLMMNNOOPP
+ //
+ ss << std::hex << std::setfill('0') <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[15] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[14] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[13] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[12] << "-" <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[11] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[10] << "-" <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[9] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[8] << "-" <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[7] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[6] << "-" <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[5] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[4] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[3] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[2] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[1] <<
+ std::setw(2) << (int)m_uuid.uuid.uuid128[0];
+ return ss.str();
+} // toString
+
+#endif /* CONFIG_BT_ENABLED */
diff --git a/sensor/patchedBLE/src/BLEUUID.h b/sensor/patchedBLE/src/BLEUUID.h
new file mode 100644
index 0000000..5fb7795
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEUUID.h
@@ -0,0 +1,39 @@
+/*
+ * BLEUUID.h
+ *
+ * Created on: Jun 21, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_
+#define COMPONENTS_CPP_UTILS_BLEUUID_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gatt_defs.h>
+#include <string>
+
+/**
+ * @brief A model of a %BLE UUID.
+ */
+class BLEUUID {
+public:
+ BLEUUID(std::string uuid);
+ BLEUUID(uint16_t uuid);
+ BLEUUID(uint32_t uuid);
+ BLEUUID(esp_bt_uuid_t uuid);
+ 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); // Create a BLEUUID from a string
+
+private:
+ esp_bt_uuid_t m_uuid; // The underlying UUID structure that this class wraps.
+ bool m_valueSet; // Is there a value set for this instance.
+}; // BLEUUID
+#endif /* CONFIG_BT_ENABLED */
+#endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */
diff --git a/sensor/patchedBLE/src/BLEUtils.cpp b/sensor/patchedBLE/src/BLEUtils.cpp
new file mode 100644
index 0000000..a33ee27
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEUtils.cpp
@@ -0,0 +1,2172 @@
+/*
+ * BLEUtils.cpp
+ *
+ * Created on: Mar 25, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include "BLEAddress.h"
+#include "BLEClient.h"
+#include "BLEUtils.h"
+#include "BLEUUID.h"
+#include "GeneralUtils.h"
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/event_groups.h>
+#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
+#include <esp_err.h> // ESP32 ESP-IDF
+#include <esp_log.h> // ESP32 ESP-IDF
+#include <map> // Part of C++ STL
+#include <sstream>
+#include <iomanip>
+
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG = "BLEUtils"; // Tag for logging.
+
+/*
+static std::map<std::string, BLEClient *> g_addressMap;
+static std::map<uint16_t, BLEClient *> g_connIdMap;
+*/
+
+typedef struct {
+ uint32_t assignedNumber;
+ const char* name;
+} member_t;
+
+static const member_t members_ids[] = {
+ {0xFE08, "Microsoft"},
+ {0xFE09, "Pillsy, Inc."},
+ {0xFE0A, "ruwido austria gmbh"},
+ {0xFE0B, "ruwido austria gmbh"},
+ {0xFE0C, "Procter & Gamble"},
+ {0xFE0D, "Procter & Gamble"},
+ {0xFE0E, "Setec Pty Ltd"},
+ {0xFE0F, "Philips Lighting B.V."},
+ {0xFE10, "Lapis Semiconductor Co., Ltd."},
+ {0xFE11, "GMC-I Messtechnik GmbH"},
+ {0xFE12, "M-Way Solutions GmbH"},
+ {0xFE13, "Apple Inc."},
+ {0xFE14, "Flextronics International USA Inc."},
+ {0xFE15, "Amazon Fulfillment Services, Inc."},
+ {0xFE16, "Footmarks, Inc."},
+ {0xFE17, "Telit Wireless Solutions GmbH"},
+ {0xFE18, "Runtime, Inc."},
+ {0xFE19, "Google Inc."},
+ {0xFE1A, "Tyto Life LLC"},
+ {0xFE1B, "Tyto Life LLC"},
+ {0xFE1C, "NetMedia, Inc."},
+ {0xFE1D, "Illuminati Instrument Corporation"},
+ {0xFE1E, "Smart Innovations Co., Ltd"},
+ {0xFE1F, "Garmin International, Inc."},
+ {0xFE20, "Emerson"},
+ {0xFE21, "Bose Corporation"},
+ {0xFE22, "Zoll Medical Corporation"},
+ {0xFE23, "Zoll Medical Corporation"},
+ {0xFE24, "August Home Inc"},
+ {0xFE25, "Apple, Inc. "},
+ {0xFE26, "Google Inc."},
+ {0xFE27, "Google Inc."},
+ {0xFE28, "Ayla Networks"},
+ {0xFE29, "Gibson Innovations"},
+ {0xFE2A, "DaisyWorks, Inc."},
+ {0xFE2B, "ITT Industries"},
+ {0xFE2C, "Google Inc."},
+ {0xFE2D, "SMART INNOVATION Co.,Ltd"},
+ {0xFE2E, "ERi,Inc."},
+ {0xFE2F, "CRESCO Wireless, Inc"},
+ {0xFE30, "Volkswagen AG"},
+ {0xFE31, "Volkswagen AG"},
+ {0xFE32, "Pro-Mark, Inc."},
+ {0xFE33, "CHIPOLO d.o.o."},
+ {0xFE34, "SmallLoop LLC"},
+ {0xFE35, "HUAWEI Technologies Co., Ltd"},
+ {0xFE36, "HUAWEI Technologies Co., Ltd"},
+ {0xFE37, "Spaceek LTD"},
+ {0xFE38, "Spaceek LTD"},
+ {0xFE39, "TTS Tooltechnic Systems AG & Co. KG"},
+ {0xFE3A, "TTS Tooltechnic Systems AG & Co. KG"},
+ {0xFE3B, "Dolby Laboratories"},
+ {0xFE3C, "Alibaba"},
+ {0xFE3D, "BD Medical"},
+ {0xFE3E, "BD Medical"},
+ {0xFE3F, "Friday Labs Limited"},
+ {0xFE40, "Inugo Systems Limited"},
+ {0xFE41, "Inugo Systems Limited"},
+ {0xFE42, "Nets A/S "},
+ {0xFE43, "Andreas Stihl AG & Co. KG"},
+ {0xFE44, "SK Telecom "},
+ {0xFE45, "Snapchat Inc"},
+ {0xFE46, "B&O Play A/S "},
+ {0xFE47, "General Motors"},
+ {0xFE48, "General Motors"},
+ {0xFE49, "SenionLab AB"},
+ {0xFE4A, "OMRON HEALTHCARE Co., Ltd."},
+ {0xFE4B, "Philips Lighting B.V."},
+ {0xFE4C, "Volkswagen AG"},
+ {0xFE4D, "Casambi Technologies Oy"},
+ {0xFE4E, "NTT docomo"},
+ {0xFE4F, "Molekule, Inc."},
+ {0xFE50, "Google Inc."},
+ {0xFE51, "SRAM"},
+ {0xFE52, "SetPoint Medical"},
+ {0xFE53, "3M"},
+ {0xFE54, "Motiv, Inc."},
+ {0xFE55, "Google Inc."},
+ {0xFE56, "Google Inc."},
+ {0xFE57, "Dotted Labs"},
+ {0xFE58, "Nordic Semiconductor ASA"},
+ {0xFE59, "Nordic Semiconductor ASA"},
+ {0xFE5A, "Chronologics Corporation"},
+ {0xFE5B, "GT-tronics HK Ltd"},
+ {0xFE5C, "million hunters GmbH"},
+ {0xFE5D, "Grundfos A/S"},
+ {0xFE5E, "Plastc Corporation"},
+ {0xFE5F, "Eyefi, Inc."},
+ {0xFE60, "Lierda Science & Technology Group Co., Ltd."},
+ {0xFE61, "Logitech International SA"},
+ {0xFE62, "Indagem Tech LLC"},
+ {0xFE63, "Connected Yard, Inc."},
+ {0xFE64, "Siemens AG"},
+ {0xFE65, "CHIPOLO d.o.o."},
+ {0xFE66, "Intel Corporation"},
+ {0xFE67, "Lab Sensor Solutions"},
+ {0xFE68, "Qualcomm Life Inc"},
+ {0xFE69, "Qualcomm Life Inc"},
+ {0xFE6A, "Kontakt Micro-Location Sp. z o.o."},
+ {0xFE6B, "TASER International, Inc."},
+ {0xFE6C, "TASER International, Inc."},
+ {0xFE6D, "The University of Tokyo"},
+ {0xFE6E, "The University of Tokyo"},
+ {0xFE6F, "LINE Corporation"},
+ {0xFE70, "Beijing Jingdong Century Trading Co., Ltd."},
+ {0xFE71, "Plume Design Inc"},
+ {0xFE72, "St. Jude Medical, Inc."},
+ {0xFE73, "St. Jude Medical, Inc."},
+ {0xFE74, "unwire"},
+ {0xFE75, "TangoMe"},
+ {0xFE76, "TangoMe"},
+ {0xFE77, "Hewlett-Packard Company"},
+ {0xFE78, "Hewlett-Packard Company"},
+ {0xFE79, "Zebra Technologies"},
+ {0xFE7A, "Bragi GmbH"},
+ {0xFE7B, "Orion Labs, Inc."},
+ {0xFE7C, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"},
+ {0xFE7D, "Aterica Health Inc."},
+ {0xFE7E, "Awear Solutions Ltd"},
+ {0xFE7F, "Doppler Lab"},
+ {0xFE80, "Doppler Lab"},
+ {0xFE81, "Medtronic Inc."},
+ {0xFE82, "Medtronic Inc."},
+ {0xFE83, "Blue Bite"},
+ {0xFE84, "RF Digital Corp"},
+ {0xFE85, "RF Digital Corp"},
+ {0xFE86, "HUAWEI Technologies Co., Ltd. ( )"},
+ {0xFE87, "Qingdao Yeelink Information Technology Co., Ltd. ( )"},
+ {0xFE88, "SALTO SYSTEMS S.L."},
+ {0xFE89, "B&O Play A/S"},
+ {0xFE8A, "Apple, Inc."},
+ {0xFE8B, "Apple, Inc."},
+ {0xFE8C, "TRON Forum"},
+ {0xFE8D, "Interaxon Inc."},
+ {0xFE8E, "ARM Ltd"},
+ {0xFE8F, "CSR"},
+ {0xFE90, "JUMA"},
+ {0xFE91, "Shanghai Imilab Technology Co.,Ltd"},
+ {0xFE92, "Jarden Safety & Security"},
+ {0xFE93, "OttoQ Inc."},
+ {0xFE94, "OttoQ Inc."},
+ {0xFE95, "Xiaomi Inc."},
+ {0xFE96, "Tesla Motor Inc."},
+ {0xFE97, "Tesla Motor Inc."},
+ {0xFE98, "Currant, Inc."},
+ {0xFE99, "Currant, Inc."},
+ {0xFE9A, "Estimote"},
+ {0xFE9B, "Samsara Networks, Inc"},
+ {0xFE9C, "GSI Laboratories, Inc."},
+ {0xFE9D, "Mobiquity Networks Inc"},
+ {0xFE9E, "Dialog Semiconductor B.V."},
+ {0xFE9F, "Google Inc."},
+ {0xFEA0, "Google Inc."},
+ {0xFEA1, "Intrepid Control Systems, Inc."},
+ {0xFEA2, "Intrepid Control Systems, Inc."},
+ {0xFEA3, "ITT Industries"},
+ {0xFEA4, "Paxton Access Ltd"},
+ {0xFEA5, "GoPro, Inc."},
+ {0xFEA6, "GoPro, Inc."},
+ {0xFEA7, "UTC Fire and Security"},
+ {0xFEA8, "Savant Systems LLC"},
+ {0xFEA9, "Savant Systems LLC"},
+ {0xFEAA, "Google Inc."},
+ {0xFEAB, "Nokia Corporation"},
+ {0xFEAC, "Nokia Corporation"},
+ {0xFEAD, "Nokia Corporation"},
+ {0xFEAE, "Nokia Corporation"},
+ {0xFEAF, "Nest Labs Inc."},
+ {0xFEB0, "Nest Labs Inc."},
+ {0xFEB1, "Electronics Tomorrow Limited"},
+ {0xFEB2, "Microsoft Corporation"},
+ {0xFEB3, "Taobao"},
+ {0xFEB4, "WiSilica Inc."},
+ {0xFEB5, "WiSilica Inc."},
+ {0xFEB6, "Vencer Co, Ltd"},
+ {0xFEB7, "Facebook, Inc."},
+ {0xFEB8, "Facebook, Inc."},
+ {0xFEB9, "LG Electronics"},
+ {0xFEBA, "Tencent Holdings Limited"},
+ {0xFEBB, "adafruit industries"},
+ {0xFEBC, "Dexcom, Inc. "},
+ {0xFEBD, "Clover Network, Inc."},
+ {0xFEBE, "Bose Corporation"},
+ {0xFEBF, "Nod, Inc."},
+ {0xFEC0, "KDDI Corporation"},
+ {0xFEC1, "KDDI Corporation"},
+ {0xFEC2, "Blue Spark Technologies, Inc."},
+ {0xFEC3, "360fly, Inc."},
+ {0xFEC4, "PLUS Location Systems"},
+ {0xFEC5, "Realtek Semiconductor Corp."},
+ {0xFEC6, "Kocomojo, LLC"},
+ {0xFEC7, "Apple, Inc."},
+ {0xFEC8, "Apple, Inc."},
+ {0xFEC9, "Apple, Inc."},
+ {0xFECA, "Apple, Inc."},
+ {0xFECB, "Apple, Inc."},
+ {0xFECC, "Apple, Inc."},
+ {0xFECD, "Apple, Inc."},
+ {0xFECE, "Apple, Inc."},
+ {0xFECF, "Apple, Inc."},
+ {0xFED0, "Apple, Inc."},
+ {0xFED1, "Apple, Inc."},
+ {0xFED2, "Apple, Inc."},
+ {0xFED3, "Apple, Inc."},
+ {0xFED4, "Apple, Inc."},
+ {0xFED5, "Plantronics Inc."},
+ {0xFED6, "Broadcom Corporation"},
+ {0xFED7, "Broadcom Corporation"},
+ {0xFED8, "Google Inc."},
+ {0xFED9, "Pebble Technology Corporation"},
+ {0xFEDA, "ISSC Technologies Corporation"},
+ {0xFEDB, "Perka, Inc."},
+ {0xFEDC, "Jawbone"},
+ {0xFEDD, "Jawbone"},
+ {0xFEDE, "Coin, Inc."},
+ {0xFEDF, "Design SHIFT"},
+ {0xFEE0, "Anhui Huami Information Technology Co."},
+ {0xFEE1, "Anhui Huami Information Technology Co."},
+ {0xFEE2, "Anki, Inc."},
+ {0xFEE3, "Anki, Inc."},
+ {0xFEE4, "Nordic Semiconductor ASA"},
+ {0xFEE5, "Nordic Semiconductor ASA"},
+ {0xFEE6, "Silvair, Inc."},
+ {0xFEE7, "Tencent Holdings Limited"},
+ {0xFEE8, "Quintic Corp."},
+ {0xFEE9, "Quintic Corp."},
+ {0xFEEA, "Swirl Networks, Inc."},
+ {0xFEEB, "Swirl Networks, Inc."},
+ {0xFEEC, "Tile, Inc."},
+ {0xFEED, "Tile, Inc."},
+ {0xFEEE, "Polar Electro Oy"},
+ {0xFEEF, "Polar Electro Oy"},
+ {0xFEF0, "Intel"},
+ {0xFEF1, "CSR"},
+ {0xFEF2, "CSR"},
+ {0xFEF3, "Google Inc."},
+ {0xFEF4, "Google Inc."},
+ {0xFEF5, "Dialog Semiconductor GmbH"},
+ {0xFEF6, "Wicentric, Inc."},
+ {0xFEF7, "Aplix Corporation"},
+ {0xFEF8, "Aplix Corporation"},
+ {0xFEF9, "PayPal, Inc."},
+ {0xFEFA, "PayPal, Inc."},
+ {0xFEFB, "Telit Wireless Solutions (Formerly Stollmann E+V GmbH)"},
+ {0xFEFC, "Gimbal, Inc."},
+ {0xFEFD, "Gimbal, Inc."},
+ {0xFEFE, "GN ReSound A/S"},
+ {0xFEFF, "GN Netcom"},
+ {0xFFFF, "Reserved"}, /*for testing purposes only*/
+ {0, "" }
+};
+
+typedef struct {
+ uint32_t assignedNumber;
+ const char* name;
+} gattdescriptor_t;
+
+static const gattdescriptor_t g_descriptor_ids[] = {
+ {0x2905,"Characteristic Aggregate Format"},
+ {0x2900,"Characteristic Extended Properties"},
+ {0x2904,"Characteristic Presentation Format"},
+ {0x2901,"Characteristic User Description"},
+ {0x2902,"Client Characteristic Configuration"},
+ {0x290B,"Environmental Sensing Configuration"},
+ {0x290C,"Environmental Sensing Measurement"},
+ {0x290D,"Environmental Sensing Trigger Setting"},
+ {0x2907,"External Report Reference"},
+ {0x2909,"Number of Digitals"},
+ {0x2908,"Report Reference"},
+ {0x2903,"Server Characteristic Configuration"},
+ {0x290E,"Time Trigger Setting"},
+ {0x2906,"Valid Range"},
+ {0x290A,"Value Trigger Setting"},
+ { 0, "" }
+};
+
+typedef struct {
+ uint32_t assignedNumber;
+ const char* name;
+} characteristicMap_t;
+
+static const characteristicMap_t g_characteristicsMappings[] = {
+ {0x2A7E,"Aerobic Heart Rate Lower Limit"},
+ {0x2A84,"Aerobic Heart Rate Upper Limit"},
+ {0x2A7F,"Aerobic Threshold"},
+ {0x2A80,"Age"},
+ {0x2A5A,"Aggregate"},
+ {0x2A43,"Alert Category ID"},
+ {0x2A42,"Alert Category ID Bit Mask"},
+ {0x2A06,"Alert Level"},
+ {0x2A44,"Alert Notification Control Point"},
+ {0x2A3F,"Alert Status"},
+ {0x2AB3,"Altitude"},
+ {0x2A81,"Anaerobic Heart Rate Lower Limit"},
+ {0x2A82,"Anaerobic Heart Rate Upper Limit"},
+ {0x2A83,"Anaerobic Threshold"},
+ {0x2A58,"Analog"},
+ {0x2A59,"Analog Output"},
+ {0x2A73,"Apparent Wind Direction"},
+ {0x2A72,"Apparent Wind Speed"},
+ {0x2A01,"Appearance"},
+ {0x2AA3,"Barometric Pressure Trend"},
+ {0x2A19,"Battery Level"},
+ {0x2A1B,"Battery Level State"},
+ {0x2A1A,"Battery Power State"},
+ {0x2A49,"Blood Pressure Feature"},
+ {0x2A35,"Blood Pressure Measurement"},
+ {0x2A9B,"Body Composition Feature"},
+ {0x2A9C,"Body Composition Measurement"},
+ {0x2A38,"Body Sensor Location"},
+ {0x2AA4,"Bond Management Control Point"},
+ {0x2AA5,"Bond Management Features"},
+ {0x2A22,"Boot Keyboard Input Report"},
+ {0x2A32,"Boot Keyboard Output Report"},
+ {0x2A33,"Boot Mouse Input Report"},
+ {0x2AA6,"Central Address Resolution"},
+ {0x2AA8,"CGM Feature"},
+ {0x2AA7,"CGM Measurement"},
+ {0x2AAB,"CGM Session Run Time"},
+ {0x2AAA,"CGM Session Start Time"},
+ {0x2AAC,"CGM Specific Ops Control Point"},
+ {0x2AA9,"CGM Status"},
+ {0x2ACE,"Cross Trainer Data"},
+ {0x2A5C,"CSC Feature"},
+ {0x2A5B,"CSC Measurement"},
+ {0x2A2B,"Current Time"},
+ {0x2A66,"Cycling Power Control Point"},
+ {0x2A66,"Cycling Power Control Point"},
+ {0x2A65,"Cycling Power Feature"},
+ {0x2A65,"Cycling Power Feature"},
+ {0x2A63,"Cycling Power Measurement"},
+ {0x2A64,"Cycling Power Vector"},
+ {0x2A99,"Database Change Increment"},
+ {0x2A85,"Date of Birth"},
+ {0x2A86,"Date of Threshold Assessment"},
+ {0x2A08,"Date Time"},
+ {0x2A0A,"Day Date Time"},
+ {0x2A09,"Day of Week"},
+ {0x2A7D,"Descriptor Value Changed"},
+ {0x2A00,"Device Name"},
+ {0x2A7B,"Dew Point"},
+ {0x2A56,"Digital"},
+ {0x2A57,"Digital Output"},
+ {0x2A0D,"DST Offset"},
+ {0x2A6C,"Elevation"},
+ {0x2A87,"Email Address"},
+ {0x2A0B,"Exact Time 100"},
+ {0x2A0C,"Exact Time 256"},
+ {0x2A88,"Fat Burn Heart Rate Lower Limit"},
+ {0x2A89,"Fat Burn Heart Rate Upper Limit"},
+ {0x2A26,"Firmware Revision String"},
+ {0x2A8A,"First Name"},
+ {0x2AD9,"Fitness Machine Control Point"},
+ {0x2ACC,"Fitness Machine Feature"},
+ {0x2ADA,"Fitness Machine Status"},
+ {0x2A8B,"Five Zone Heart Rate Limits"},
+ {0x2AB2,"Floor Number"},
+ {0x2A8C,"Gender"},
+ {0x2A51,"Glucose Feature"},
+ {0x2A18,"Glucose Measurement"},
+ {0x2A34,"Glucose Measurement Context"},
+ {0x2A74,"Gust Factor"},
+ {0x2A27,"Hardware Revision String"},
+ {0x2A39,"Heart Rate Control Point"},
+ {0x2A8D,"Heart Rate Max"},
+ {0x2A37,"Heart Rate Measurement"},
+ {0x2A7A,"Heat Index"},
+ {0x2A8E,"Height"},
+ {0x2A4C,"HID Control Point"},
+ {0x2A4A,"HID Information"},
+ {0x2A8F,"Hip Circumference"},
+ {0x2ABA,"HTTP Control Point"},
+ {0x2AB9,"HTTP Entity Body"},
+ {0x2AB7,"HTTP Headers"},
+ {0x2AB8,"HTTP Status Code"},
+ {0x2ABB,"HTTPS Security"},
+ {0x2A6F,"Humidity"},
+ {0x2A2A,"IEEE 11073-20601 Regulatory Certification Data List"},
+ {0x2AD2,"Indoor Bike Data"},
+ {0x2AAD,"Indoor Positioning Configuration"},
+ {0x2A36,"Intermediate Cuff Pressure"},
+ {0x2A1E,"Intermediate Temperature"},
+ {0x2A77,"Irradiance"},
+ {0x2AA2,"Language"},
+ {0x2A90,"Last Name"},
+ {0x2AAE,"Latitude"},
+ {0x2A6B,"LN Control Point"},
+ {0x2A6A,"LN Feature"},
+ {0x2AB1,"Local East Coordinate"},
+ {0x2AB0,"Local North Coordinate"},
+ {0x2A0F,"Local Time Information"},
+ {0x2A67,"Location and Speed Characteristic"},
+ {0x2AB5,"Location Name"},
+ {0x2AAF,"Longitude"},
+ {0x2A2C,"Magnetic Declination"},
+ {0x2AA0,"Magnetic Flux Density - 2D"},
+ {0x2AA1,"Magnetic Flux Density - 3D"},
+ {0x2A29,"Manufacturer Name String"},
+ {0x2A91,"Maximum Recommended Heart Rate"},
+ {0x2A21,"Measurement Interval"},
+ {0x2A24,"Model Number String"},
+ {0x2A68,"Navigation"},
+ {0x2A3E,"Network Availability"},
+ {0x2A46,"New Alert"},
+ {0x2AC5,"Object Action Control Point"},
+ {0x2AC8,"Object Changed"},
+ {0x2AC1,"Object First-Created"},
+ {0x2AC3,"Object ID"},
+ {0x2AC2,"Object Last-Modified"},
+ {0x2AC6,"Object List Control Point"},
+ {0x2AC7,"Object List Filter"},
+ {0x2ABE,"Object Name"},
+ {0x2AC4,"Object Properties"},
+ {0x2AC0,"Object Size"},
+ {0x2ABF,"Object Type"},
+ {0x2ABD,"OTS Feature"},
+ {0x2A04,"Peripheral Preferred Connection Parameters"},
+ {0x2A02,"Peripheral Privacy Flag"},
+ {0x2A5F,"PLX Continuous Measurement Characteristic"},
+ {0x2A60,"PLX Features"},
+ {0x2A5E,"PLX Spot-Check Measurement"},
+ {0x2A50,"PnP ID"},
+ {0x2A75,"Pollen Concentration"},
+ {0x2A2F,"Position 2D"},
+ {0x2A30,"Position 3D"},
+ {0x2A69,"Position Quality"},
+ {0x2A6D,"Pressure"},
+ {0x2A4E,"Protocol Mode"},
+ {0x2A62,"Pulse Oximetry Control Point"},
+ {0x2A60,"Pulse Oximetry Pulsatile Event Characteristic"},
+ {0x2A78,"Rainfall"},
+ {0x2A03,"Reconnection Address"},
+ {0x2A52,"Record Access Control Point"},
+ {0x2A14,"Reference Time Information"},
+ {0x2A3A,"Removable"},
+ {0x2A4D,"Report"},
+ {0x2A4B,"Report Map"},
+ {0x2AC9,"Resolvable Private Address Only"},
+ {0x2A92,"Resting Heart Rate"},
+ {0x2A40,"Ringer Control point"},
+ {0x2A41,"Ringer Setting"},
+ {0x2AD1,"Rower Data"},
+ {0x2A54,"RSC Feature"},
+ {0x2A53,"RSC Measurement"},
+ {0x2A55,"SC Control Point"},
+ {0x2A4F,"Scan Interval Window"},
+ {0x2A31,"Scan Refresh"},
+ {0x2A3C,"Scientific Temperature Celsius"},
+ {0x2A10,"Secondary Time Zone"},
+ {0x2A5D,"Sensor Location"},
+ {0x2A25,"Serial Number String"},
+ {0x2A05,"Service Changed"},
+ {0x2A3B,"Service Required"},
+ {0x2A28,"Software Revision String"},
+ {0x2A93,"Sport Type for Aerobic and Anaerobic Thresholds"},
+ {0x2AD0,"Stair Climber Data"},
+ {0x2ACF,"Step Climber Data"},
+ {0x2A3D,"String"},
+ {0x2AD7,"Supported Heart Rate Range"},
+ {0x2AD5,"Supported Inclination Range"},
+ {0x2A47,"Supported New Alert Category"},
+ {0x2AD8,"Supported Power Range"},
+ {0x2AD6,"Supported Resistance Level Range"},
+ {0x2AD4,"Supported Speed Range"},
+ {0x2A48,"Supported Unread Alert Category"},
+ {0x2A23,"System ID"},
+ {0x2ABC,"TDS Control Point"},
+ {0x2A6E,"Temperature"},
+ {0x2A1F,"Temperature Celsius"},
+ {0x2A20,"Temperature Fahrenheit"},
+ {0x2A1C,"Temperature Measurement"},
+ {0x2A1D,"Temperature Type"},
+ {0x2A94,"Three Zone Heart Rate Limits"},
+ {0x2A12,"Time Accuracy"},
+ {0x2A15,"Time Broadcast"},
+ {0x2A13,"Time Source"},
+ {0x2A16,"Time Update Control Point"},
+ {0x2A17,"Time Update State"},
+ {0x2A11,"Time with DST"},
+ {0x2A0E,"Time Zone"},
+ {0x2AD3,"Training Status"},
+ {0x2ACD,"Treadmill Data"},
+ {0x2A71,"True Wind Direction"},
+ {0x2A70,"True Wind Speed"},
+ {0x2A95,"Two Zone Heart Rate Limit"},
+ {0x2A07,"Tx Power Level"},
+ {0x2AB4,"Uncertainty"},
+ {0x2A45,"Unread Alert Status"},
+ {0x2AB6,"URI"},
+ {0x2A9F,"User Control Point"},
+ {0x2A9A,"User Index"},
+ {0x2A76,"UV Index"},
+ {0x2A96,"VO2 Max"},
+ {0x2A97,"Waist Circumference"},
+ {0x2A98,"Weight"},
+ {0x2A9D,"Weight Measurement"},
+ {0x2A9E,"Weight Scale Feature"},
+ {0x2A79,"Wind Chill"},
+ {0, ""}
+};
+
+/**
+ * @brief Mapping from service ids to names
+ */
+typedef struct {
+ const char* name;
+ const char* type;
+ uint32_t assignedNumber;
+} gattService_t;
+
+
+/**
+ * Definition of the service ids to names that we know about.
+ */
+static const gattService_t g_gattServices[] = {
+ {"Alert Notification Service", "org.bluetooth.service.alert_notification", 0x1811},
+ {"Automation IO", "org.bluetooth.service.automation_io", 0x1815 },
+ {"Battery Service","org.bluetooth.service.battery_service", 0x180F},
+ {"Blood Pressure", "org.bluetooth.service.blood_pressure", 0x1810},
+ {"Body Composition", "org.bluetooth.service.body_composition", 0x181B},
+ {"Bond Management", "org.bluetooth.service.bond_management", 0x181E},
+ {"Continuous Glucose Monitoring", "org.bluetooth.service.continuous_glucose_monitoring", 0x181F},
+ {"Current Time Service", "org.bluetooth.service.current_time", 0x1805},
+ {"Cycling Power", "org.bluetooth.service.cycling_power", 0x1818},
+ {"Cycling Speed and Cadence", "org.bluetooth.service.cycling_speed_and_cadence", 0x1816},
+ {"Device Information", "org.bluetooth.service.device_information", 0x180A},
+ {"Environmental Sensing", "org.bluetooth.service.environmental_sensing", 0x181A},
+ {"Generic Access", "org.bluetooth.service.generic_access", 0x1800},
+ {"Generic Attribute", "org.bluetooth.service.generic_attribute", 0x1801},
+ {"Glucose", "org.bluetooth.service.glucose", 0x1808},
+ {"Health Thermometer", "org.bluetooth.service.health_thermometer", 0x1809},
+ {"Heart Rate", "org.bluetooth.service.heart_rate", 0x180D},
+ {"HTTP Proxy", "org.bluetooth.service.http_proxy", 0x1823},
+ {"Human Interface Device", "org.bluetooth.service.human_interface_device", 0x1812},
+ {"Immediate Alert", "org.bluetooth.service.immediate_alert", 0x1802},
+ {"Indoor Positioning", "org.bluetooth.service.indoor_positioning", 0x1821},
+ {"Internet Protocol Support", "org.bluetooth.service.internet_protocol_support", 0x1820},
+ {"Link Loss", "org.bluetooth.service.link_loss", 0x1803},
+ {"Location and Navigation", "org.bluetooth.service.location_and_navigation", 0x1819},
+ {"Next DST Change Service", "org.bluetooth.service.next_dst_change", 0x1807},
+ {"Object Transfer", "org.bluetooth.service.object_transfer", 0x1825},
+ {"Phone Alert Status Service", "org.bluetooth.service.phone_alert_status", 0x180E},
+ {"Pulse Oximeter", "org.bluetooth.service.pulse_oximeter", 0x1822},
+ {"Reference Time Update Service", "org.bluetooth.service.reference_time_update", 0x1806},
+ {"Running Speed and Cadence", "org.bluetooth.service.running_speed_and_cadence", 0x1814},
+ {"Scan Parameters", "org.bluetooth.service.scan_parameters", 0x1813},
+ {"Transport Discovery", "org.bluetooth.service.transport_discovery", 0x1824},
+ {"Tx Power", "org.bluetooth.service.tx_power", 0x1804},
+ {"User Data", "org.bluetooth.service.user_data", 0x181C},
+ {"Weight Scale", "org.bluetooth.service.weight_scale", 0x181D},
+ {"", "", 0 }
+};
+
+
+/**
+ * @brief Convert characteristic properties into a string representation.
+ * @param [in] prop Characteristic properties.
+ * @return A string representation of characteristic properties.
+ */
+std::string BLEUtils::characteristicPropertiesToString(esp_gatt_char_prop_t prop) {
+ std::stringstream stream;
+ stream <<
+ "broadcast: " << ((prop & ESP_GATT_CHAR_PROP_BIT_BROADCAST)?"1":"0") <<
+ ", read: " << ((prop & ESP_GATT_CHAR_PROP_BIT_READ)?"1":"0") <<
+ ", write_nr: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)?"1":"0") <<
+ ", write: " << ((prop & ESP_GATT_CHAR_PROP_BIT_WRITE)?"1":"0") <<
+ ", notify: " << ((prop & ESP_GATT_CHAR_PROP_BIT_NOTIFY)?"1":"0") <<
+ ", indicate: " << ((prop & ESP_GATT_CHAR_PROP_BIT_INDICATE)?"1":"0") <<
+ ", auth: " << ((prop & ESP_GATT_CHAR_PROP_BIT_AUTH)?"1":"0");
+ return stream.str();
+} // characteristicPropertiesToString
+
+/**
+ * @brief Convert an esp_gatt_id_t to a string.
+ */
+static std::string gattIdToString(esp_gatt_id_t gattId) {
+ std::stringstream stream;
+ stream << "uuid: " << BLEUUID(gattId.uuid).toString() << ", inst_id: " << (int)gattId.inst_id;
+ //sprintf(buffer, "uuid: %s, inst_id: %d", uuidToString(gattId.uuid).c_str(), gattId.inst_id);
+ return stream.str();
+} // gattIdToString
+
+
+/**
+ * @brief Convert an esp_ble_addr_type_t to a string representation.
+ */
+const char* BLEUtils::addressTypeToString(esp_ble_addr_type_t type) {
+ switch(type) {
+ case BLE_ADDR_TYPE_PUBLIC:
+ return "BLE_ADDR_TYPE_PUBLIC";
+ case BLE_ADDR_TYPE_RANDOM:
+ return "BLE_ADDR_TYPE_RANDOM";
+ case BLE_ADDR_TYPE_RPA_PUBLIC:
+ return "BLE_ADDR_TYPE_RPA_PUBLIC";
+ case BLE_ADDR_TYPE_RPA_RANDOM:
+ return "BLE_ADDR_TYPE_RPA_RANDOM";
+ default:
+ return " esp_ble_addr_type_t";
+ }
+} // addressTypeToString
+
+
+/**
+ * @brief Convert the BLE Advertising Data flags to a string.
+ * @param adFlags The flags to convert
+ * @return std::string A string representation of the advertising flags.
+ */
+std::string BLEUtils::adFlagsToString(uint8_t adFlags) {
+ std::stringstream ss;
+ if (adFlags & (1<<0)) {
+ ss << "[LE Limited Discoverable Mode] ";
+ }
+ if (adFlags & (1<<1)) {
+ ss << "[LE General Discoverable Mode] ";
+ }
+ if (adFlags & (1<<2)) {
+ ss << "[BR/EDR Not Supported] ";
+ }
+ if (adFlags & (1<<3)) {
+ ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Controller)] ";
+ }
+ if (adFlags & (1<<4)) {
+ ss << "[Simultaneous LE and BR/EDR to Same Device Capable (Host)] ";
+ }
+ return ss.str();
+} // adFlagsToString
+
+
+/**
+ * @brief Given an advertising type, return a string representation of the type.
+ *
+ * For details see ...
+ * https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+ *
+ * @return A string representation of the type.
+ */
+const char* BLEUtils::advTypeToString(uint8_t advType) {
+ switch(advType) {
+ case ESP_BLE_AD_TYPE_FLAG: // 0x01
+ return "ESP_BLE_AD_TYPE_FLAG";
+
+ case ESP_BLE_AD_TYPE_16SRV_PART: // 0x02
+ return "ESP_BLE_AD_TYPE_16SRV_PART";
+
+ case ESP_BLE_AD_TYPE_16SRV_CMPL: // 0x03
+ return "ESP_BLE_AD_TYPE_16SRV_CMPL";
+
+ case ESP_BLE_AD_TYPE_32SRV_PART: // 0x04
+ return "ESP_BLE_AD_TYPE_32SRV_PART";
+
+ case ESP_BLE_AD_TYPE_32SRV_CMPL: // 0x05
+ return "ESP_BLE_AD_TYPE_32SRV_CMPL";
+
+ case ESP_BLE_AD_TYPE_128SRV_PART: // 0x06
+ return "ESP_BLE_AD_TYPE_128SRV_PART";
+
+ case ESP_BLE_AD_TYPE_128SRV_CMPL: // 0x07
+ return "ESP_BLE_AD_TYPE_128SRV_CMPL";
+
+ case ESP_BLE_AD_TYPE_NAME_SHORT: // 0x08
+ return "ESP_BLE_AD_TYPE_NAME_SHORT";
+
+ case ESP_BLE_AD_TYPE_NAME_CMPL: // 0x09
+ return "ESP_BLE_AD_TYPE_NAME_CMPL";
+
+ case ESP_BLE_AD_TYPE_TX_PWR: // 0x0a
+ return "ESP_BLE_AD_TYPE_TX_PWR";
+
+ case ESP_BLE_AD_TYPE_DEV_CLASS: // 0x0b
+ return "ESP_BLE_AD_TYPE_DEV_CLASS";
+
+ case ESP_BLE_AD_TYPE_SM_TK: // 0x10
+ return "ESP_BLE_AD_TYPE_SM_TK";
+
+ case ESP_BLE_AD_TYPE_SM_OOB_FLAG: // 0x11
+ return "ESP_BLE_AD_TYPE_SM_OOB_FLAG";
+
+ case ESP_BLE_AD_TYPE_INT_RANGE: // 0x12
+ return "ESP_BLE_AD_TYPE_INT_RANGE";
+
+ case ESP_BLE_AD_TYPE_SOL_SRV_UUID: // 0x14
+ return "ESP_BLE_AD_TYPE_SOL_SRV_UUID";
+
+ case ESP_BLE_AD_TYPE_128SOL_SRV_UUID: // 0x15
+ return "ESP_BLE_AD_TYPE_128SOL_SRV_UUID";
+
+ case ESP_BLE_AD_TYPE_SERVICE_DATA: // 0x16
+ return "ESP_BLE_AD_TYPE_SERVICE_DATA";
+
+ case ESP_BLE_AD_TYPE_PUBLIC_TARGET: // 0x17
+ return "ESP_BLE_AD_TYPE_PUBLIC_TARGET";
+
+ case ESP_BLE_AD_TYPE_RANDOM_TARGET: // 0x18
+ return "ESP_BLE_AD_TYPE_RANDOM_TARGET";
+
+ case ESP_BLE_AD_TYPE_APPEARANCE: // 0x19
+ return "ESP_BLE_AD_TYPE_APPEARANCE";
+
+ case ESP_BLE_AD_TYPE_ADV_INT: // 0x1a
+ return "ESP_BLE_AD_TYPE_ADV_INT";
+
+ case ESP_BLE_AD_TYPE_32SOL_SRV_UUID:
+ return "ESP_BLE_AD_TYPE_32SOL_SRV_UUID";
+
+ case ESP_BLE_AD_TYPE_32SERVICE_DATA: // 0x20
+ return "ESP_BLE_AD_TYPE_32SERVICE_DATA";
+
+ case ESP_BLE_AD_TYPE_128SERVICE_DATA: // 0x21
+ return "ESP_BLE_AD_TYPE_128SERVICE_DATA";
+
+ case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: // 0xff
+ return "ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE";
+
+ default:
+ ESP_LOGD(LOG_TAG, " adv data type: 0x%x", advType);
+ return "";
+ } // End switch
+} // advTypeToString
+
+
+esp_gatt_id_t BLEUtils::buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id) {
+ esp_gatt_id_t retGattId;
+ retGattId.uuid = uuid;
+ retGattId.inst_id = inst_id;
+ return retGattId;
+}
+
+esp_gatt_srvc_id_t BLEUtils::buildGattSrvcId(esp_gatt_id_t gattId,
+ bool is_primary) {
+ esp_gatt_srvc_id_t retSrvcId;
+ retSrvcId.id = gattId;
+ retSrvcId.is_primary = is_primary;
+ return retSrvcId;
+}
+
+/**
+ * @brief Create a hex representation of data.
+ *
+ * @param [in] target Where to write the hex string. If this is null, we malloc storage.
+ * @param [in] source The start of the binary data.
+ * @param [in] length The length of the data to convert.
+ * @return A pointer to the formatted buffer.
+ */
+char* BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) {
+// Guard against too much data.
+ if (length > 100) {
+ length = 100;
+ }
+
+ if (target == nullptr) {
+ target = (uint8_t *)malloc(length * 2 + 1);
+ if (target == nullptr) {
+ ESP_LOGE(LOG_TAG, "buildHexData: malloc failed");
+ return nullptr;
+ }
+ }
+ char *startOfData = (char *)target;
+
+ int i;
+ for (i=0; i<length; i++) {
+ sprintf((char *)target, "%.2x", (char)*source);
+ source++;
+ target +=2;
+ }
+
+// Handle the special case where there was no data.
+ if (length == 0) {
+ *startOfData = 0;
+ }
+
+ return startOfData;
+} // buildHexData
+
+
+/**
+ * @brief Build a printable string of memory range.
+ * Create a string representation of a piece of memory. Only printable characters will be included
+ * while those that are not printable will be replaced with '.'.
+ * @param [in] source Start of memory.
+ * @param [in] length Length of memory.
+ * @return A string representation of a piece of memory.
+ */
+std::string BLEUtils::buildPrintData(uint8_t* source, size_t length) {
+ std::ostringstream ss;
+ for (int i=0; i<length; i++) {
+ char c = *source;
+ if (isprint(c)) {
+ ss << c;
+ } else {
+ ss << '.';
+ }
+ source++;
+ }
+ return ss.str();
+} // 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: {
+ return "ESP_GATT_CONN_UNKNOWN";
+ }
+
+ case ESP_GATT_CONN_L2C_FAILURE: {
+ return "ESP_GATT_CONN_L2C_FAILURE";
+ }
+
+ case ESP_GATT_CONN_TIMEOUT: {
+ return "ESP_GATT_CONN_TIMEOUT";
+ }
+
+ case ESP_GATT_CONN_TERMINATE_PEER_USER: {
+ return "ESP_GATT_CONN_TERMINATE_PEER_USER";
+ }
+
+ case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: {
+ return "ESP_GATT_CONN_TERMINATE_LOCAL_HOST";
+ }
+
+ case ESP_GATT_CONN_FAIL_ESTABLISH: {
+ return "ESP_GATT_CONN_FAIL_ESTABLISH";
+ }
+
+ case ESP_GATT_CONN_LMP_TIMEOUT: {
+ return "ESP_GATT_CONN_LMP_TIMEOUT";
+ }
+
+ case ESP_GATT_CONN_CONN_CANCEL: {
+ return "ESP_GATT_CONN_CONN_CANCEL";
+ }
+
+ case ESP_GATT_CONN_NONE: {
+ return "ESP_GATT_CONN_NONE";
+ }
+
+ default: {
+ return "Unknown";
+ }
+ }
+} // gattCloseReasonToString
+
+
+std::string BLEUtils::gattClientEventTypeToString(esp_gattc_cb_event_t eventType) {
+ switch(eventType) {
+ case ESP_GATTC_ACL_EVT:
+ return "ESP_GATTC_ACL_EVT";
+ case ESP_GATTC_ADV_DATA_EVT:
+ return "ESP_GATTC_ADV_DATA_EVT";
+ case ESP_GATTC_ADV_VSC_EVT:
+ return "ESP_GATTC_ADV_VSC_EVT";
+ case ESP_GATTC_BTH_SCAN_CFG_EVT:
+ return "ESP_GATTC_BTH_SCAN_CFG_EVT";
+ case ESP_GATTC_BTH_SCAN_DIS_EVT:
+ return "ESP_GATTC_BTH_SCAN_DIS_EVT";
+ case ESP_GATTC_BTH_SCAN_ENB_EVT:
+ return "ESP_GATTC_BTH_SCAN_ENB_EVT";
+ case ESP_GATTC_BTH_SCAN_PARAM_EVT:
+ return "ESP_GATTC_BTH_SCAN_PARAM_EVT";
+ case ESP_GATTC_BTH_SCAN_RD_EVT:
+ return "ESP_GATTC_BTH_SCAN_RD_EVT";
+ case ESP_GATTC_BTH_SCAN_THR_EVT:
+ return "ESP_GATTC_BTH_SCAN_THR_EVT";
+ case ESP_GATTC_CANCEL_OPEN_EVT:
+ return "ESP_GATTC_CANCEL_OPEN_EVT";
+ case ESP_GATTC_CFG_MTU_EVT:
+ return "ESP_GATTC_CFG_MTU_EVT";
+ case ESP_GATTC_CLOSE_EVT:
+ return "ESP_GATTC_CLOSE_EVT";
+ case ESP_GATTC_CONGEST_EVT:
+ return "ESP_GATTC_CONGEST_EVT";
+ case ESP_GATTC_CONNECT_EVT:
+ return "ESP_GATTC_CONNECT_EVT";
+ case ESP_GATTC_DISCONNECT_EVT:
+ return "ESP_GATTC_DISCONNECT_EVT";
+ case ESP_GATTC_ENC_CMPL_CB_EVT:
+ return "ESP_GATTC_ENC_CMPL_CB_EVT";
+ case ESP_GATTC_EXEC_EVT:
+ return "ESP_GATTC_EXEC_EVT";
+ //case ESP_GATTC_GET_CHAR_EVT:
+// return "ESP_GATTC_GET_CHAR_EVT";
+ //case ESP_GATTC_GET_DESCR_EVT:
+// return "ESP_GATTC_GET_DESCR_EVT";
+ //case ESP_GATTC_GET_INCL_SRVC_EVT:
+// return "ESP_GATTC_GET_INCL_SRVC_EVT";
+ case ESP_GATTC_MULT_ADV_DATA_EVT:
+ return "ESP_GATTC_MULT_ADV_DATA_EVT";
+ case ESP_GATTC_MULT_ADV_DIS_EVT:
+ return "ESP_GATTC_MULT_ADV_DIS_EVT";
+ case ESP_GATTC_MULT_ADV_ENB_EVT:
+ return "ESP_GATTC_MULT_ADV_ENB_EVT";
+ case ESP_GATTC_MULT_ADV_UPD_EVT:
+ return "ESP_GATTC_MULT_ADV_UPD_EVT";
+ case ESP_GATTC_NOTIFY_EVT:
+ return "ESP_GATTC_NOTIFY_EVT";
+ case ESP_GATTC_OPEN_EVT:
+ return "ESP_GATTC_OPEN_EVT";
+ case ESP_GATTC_PREP_WRITE_EVT:
+ return "ESP_GATTC_PREP_WRITE_EVT";
+ case ESP_GATTC_READ_CHAR_EVT:
+ return "ESP_GATTC_READ_CHAR_EVT";
+ case ESP_GATTC_REG_EVT:
+ return "ESP_GATTC_REG_EVT";
+ case ESP_GATTC_REG_FOR_NOTIFY_EVT:
+ return "ESP_GATTC_REG_FOR_NOTIFY_EVT";
+ case ESP_GATTC_SCAN_FLT_CFG_EVT:
+ return "ESP_GATTC_SCAN_FLT_CFG_EVT";
+ case ESP_GATTC_SCAN_FLT_PARAM_EVT:
+ return "ESP_GATTC_SCAN_FLT_PARAM_EVT";
+ case ESP_GATTC_SCAN_FLT_STATUS_EVT:
+ return "ESP_GATTC_SCAN_FLT_STATUS_EVT";
+ case ESP_GATTC_SEARCH_CMPL_EVT:
+ return "ESP_GATTC_SEARCH_CMPL_EVT";
+ case ESP_GATTC_SEARCH_RES_EVT:
+ return "ESP_GATTC_SEARCH_RES_EVT";
+ case ESP_GATTC_SRVC_CHG_EVT:
+ return "ESP_GATTC_SRVC_CHG_EVT";
+ case ESP_GATTC_READ_DESCR_EVT:
+ return "ESP_GATTC_READ_DESCR_EVT";
+ case ESP_GATTC_UNREG_EVT:
+ return "ESP_GATTC_UNREG_EVT";
+ case ESP_GATTC_UNREG_FOR_NOTIFY_EVT:
+ return "ESP_GATTC_UNREG_FOR_NOTIFY_EVT";
+ case ESP_GATTC_WRITE_CHAR_EVT:
+ return "ESP_GATTC_WRITE_CHAR_EVT";
+ case ESP_GATTC_WRITE_DESCR_EVT:
+ return "ESP_GATTC_WRITE_DESCR_EVT";
+ default:
+ ESP_LOGW(LOG_TAG, "Unknown GATT Client event type: %d", eventType);
+ return "Unknown";
+ }
+} // gattClientEventTypeToString
+
+
+/**
+ * @brief Return a string representation of a GATT server event code.
+ * @param [in] eventType A GATT server event code.
+ * @return A string representation of the GATT server event code.
+ */
+std::string BLEUtils::gattServerEventTypeToString(esp_gatts_cb_event_t eventType) {
+ switch(eventType) {
+ case ESP_GATTS_REG_EVT:
+ return "ESP_GATTS_REG_EVT";
+
+ case ESP_GATTS_READ_EVT:
+ return "ESP_GATTS_READ_EVT";
+
+ case ESP_GATTS_WRITE_EVT:
+ return "ESP_GATTS_WRITE_EVT";
+
+ case ESP_GATTS_EXEC_WRITE_EVT:
+ return "ESP_GATTS_EXEC_WRITE_EVT";
+
+ case ESP_GATTS_MTU_EVT:
+ return "ESP_GATTS_MTU_EVT";
+
+ case ESP_GATTS_CONF_EVT:
+ return "ESP_GATTS_CONF_EVT";
+
+ case ESP_GATTS_UNREG_EVT:
+ return "ESP_GATTS_UNREG_EVT";
+
+ case ESP_GATTS_CREATE_EVT:
+ return "ESP_GATTS_CREATE_EVT";
+
+ case ESP_GATTS_ADD_INCL_SRVC_EVT:
+ return "ESP_GATTS_ADD_INCL_SRVC_EVT";
+
+ case ESP_GATTS_ADD_CHAR_EVT:
+ return "ESP_GATTS_ADD_CHAR_EVT";
+
+ case ESP_GATTS_ADD_CHAR_DESCR_EVT:
+ return "ESP_GATTS_ADD_CHAR_DESCR_EVT";
+
+ case ESP_GATTS_DELETE_EVT:
+ return "ESP_GATTS_DELETE_EVT";
+
+ case ESP_GATTS_START_EVT:
+ return "ESP_GATTS_START_EVT";
+
+ case ESP_GATTS_STOP_EVT:
+ return "ESP_GATTS_STOP_EVT";
+
+ case ESP_GATTS_CONNECT_EVT:
+ return "ESP_GATTS_CONNECT_EVT";
+
+ case ESP_GATTS_DISCONNECT_EVT:
+ return "ESP_GATTS_DISCONNECT_EVT";
+
+ case ESP_GATTS_OPEN_EVT:
+ return "ESP_GATTS_OPEN_EVT";
+
+ case ESP_GATTS_CANCEL_OPEN_EVT:
+ return "ESP_GATTS_CANCEL_OPEN_EVT";
+
+ case ESP_GATTS_CLOSE_EVT:
+ return "ESP_GATTS_CLOSE_EVT";
+
+ case ESP_GATTS_LISTEN_EVT:
+ return "ESP_GATTS_LISTEN_EVT";
+
+ case ESP_GATTS_CONGEST_EVT:
+ return "ESP_GATTS_CONGEST_EVT";
+
+ case ESP_GATTS_RESPONSE_EVT:
+ return "ESP_GATTS_RESPONSE_EVT";
+
+ case ESP_GATTS_CREAT_ATTR_TAB_EVT:
+ return "ESP_GATTS_CREAT_ATTR_TAB_EVT";
+
+ case ESP_GATTS_SET_ATTR_VAL_EVT:
+ return "ESP_GATTS_SET_ATTR_VAL_EVT";
+
+ }
+ return "Unknown";
+} // gattServerEventTypeToString
+
+
+
+/**
+ * @brief Convert a BLE device type to a string.
+ * @param [in] type The device type.
+ */
+const char* BLEUtils::devTypeToString(esp_bt_dev_type_t type) {
+ switch(type) {
+ case ESP_BT_DEVICE_TYPE_BREDR:
+ return "ESP_BT_DEVICE_TYPE_BREDR";
+ case ESP_BT_DEVICE_TYPE_BLE:
+ return "ESP_BT_DEVICE_TYPE_BLE";
+ case ESP_BT_DEVICE_TYPE_DUMO:
+ return "ESP_BT_DEVICE_TYPE_DUMO";
+ default:
+ return "Unknown";
+ }
+} // devTypeToString
+
+
+/**
+ * @brief Dump the GAP event to the log.
+ */
+void BLEUtils::dumpGapEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param) {
+ ESP_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event));
+ switch(event) {
+ //
+ // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT
+ // adv_data_cmpl
+ // - esp_bt_status_t
+ //
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT
+ //
+ // adv_data_raw_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_data_raw_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_ADV_START_COMPLETE_EVT
+ //
+ // adv_start_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_start_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_ADV_START_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
+ //
+ // adv_stop_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->adv_stop_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_AUTH_CMPL_EVT
+ //
+ // auth_cmpl
+ // - esp_bd_addr_t bd_addr
+ // - bool key_present
+ // - esp_link_key key
+ // - bool success
+ // - uint8_t fail_reason
+ // - esp_bd_addr_type_t addr_type
+ // - esp_bt_dev_type_t dev_type
+ //
+ case ESP_GAP_BLE_AUTH_CMPL_EVT: {
+ ESP_LOGD(LOG_TAG, "[bd_addr: %s, key_present: %d, key: ***, key_type: %d, success: %d, fail_reason: %d, addr_type: ***, dev_type: %s]",
+ BLEAddress(param->ble_security.auth_cmpl.bd_addr).toString().c_str(),
+ param->ble_security.auth_cmpl.key_present,
+ param->ble_security.auth_cmpl.key_type,
+ param->ble_security.auth_cmpl.success,
+ param->ble_security.auth_cmpl.fail_reason,
+ BLEUtils::devTypeToString(param->ble_security.auth_cmpl.dev_type)
+ );
+ break;
+ } // ESP_GAP_BLE_AUTH_CMPL_EVT
+
+
+ //
+ // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT
+ //
+ // clear_bond_dev_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->clear_bond_dev_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_LOCAL_IR_EVT
+ //
+ case ESP_GAP_BLE_LOCAL_IR_EVT: {
+ break;
+ } // ESP_GAP_BLE_LOCAL_IR_EVT
+
+
+ //
+ // ESP_GAP_BLE_LOCAL_ER_EVT
+ //
+ case ESP_GAP_BLE_LOCAL_ER_EVT: {
+ break;
+ } // ESP_GAP_BLE_LOCAL_ER_EVT
+
+
+ //
+ // ESP_GAP_BLE_NC_REQ_EVT
+ //
+ case ESP_GAP_BLE_NC_REQ_EVT: {
+ ESP_LOGD(LOG_TAG, "[bd_addr: %s, passkey: %d]",
+ BLEAddress(param->ble_security.key_notif.bd_addr).toString().c_str(),
+ param->ble_security.key_notif.passkey);
+ break;
+ } // ESP_GAP_BLE_NC_REQ_EVT
+
+
+ //
+ // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
+ //
+ // read_rssi_cmpl
+ // - esp_bt_status_t status
+ // - int8_t rssi
+ // - esp_bd_addr_t remote_addr
+ //
+ case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d, rssi: %d, remote_addr: %s]",
+ param->read_rssi_cmpl.status,
+ param->read_rssi_cmpl.rssi,
+ BLEAddress(param->read_rssi_cmpl.remote_addr).toString().c_str()
+ );
+ break;
+ } // ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT
+ //
+ // scan_param_cmpl.
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_param_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_SCAN_RESULT_EVT
+ //
+ // scan_rst:
+ // - search_evt
+ // - bda
+ // - dev_type
+ // - ble_addr_type
+ // - ble_evt_type
+ // - rssi
+ // - ble_adv
+ // - flag
+ // - num_resps
+ // - adv_data_len
+ // - scan_rsp_len
+ //
+ case ESP_GAP_BLE_SCAN_RESULT_EVT: {
+ switch(param->scan_rst.search_evt) {
+ case ESP_GAP_SEARCH_INQ_RES_EVT: {
+ ESP_LOGD(LOG_TAG, "search_evt: %s, bda: %s, dev_type: %s, ble_addr_type: %s, ble_evt_type: %s, rssi: %d, ble_adv: ??, flag: %d (%s), num_resps: %d, adv_data_len: %d, scan_rsp_len: %d",
+ searchEventTypeToString(param->scan_rst.search_evt),
+ BLEAddress(param->scan_rst.bda).toString().c_str(),
+ devTypeToString(param->scan_rst.dev_type),
+ addressTypeToString(param->scan_rst.ble_addr_type),
+ eventTypeToString(param->scan_rst.ble_evt_type),
+ param->scan_rst.rssi,
+ param->scan_rst.flag,
+ adFlagsToString(param->scan_rst.flag).c_str(),
+ param->scan_rst.num_resps,
+ param->scan_rst.adv_data_len,
+ param->scan_rst.scan_rsp_len
+ );
+ break;
+ } // ESP_GAP_SEARCH_INQ_RES_EVT
+
+ default: {
+ ESP_LOGD(LOG_TAG, "search_evt: %s",searchEventTypeToString(param->scan_rst.search_evt));
+ break;
+ }
+ }
+ break;
+ } // ESP_GAP_BLE_SCAN_RESULT_EVT
+
+
+ //
+ // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT
+ //
+ // scan_rsp_data_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_rsp_data_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT
+
+
+ //
+ // 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
+ // - esp_bt_status_t status
+ case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_start_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_SCAN_START_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT
+ //
+ // scan_stop_cmpl
+ // - esp_bt_status_t status
+ //
+ case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d]", param->scan_stop_cmpl.status);
+ break;
+ } // ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT
+
+
+ //
+ // ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT
+ //
+ // update_conn_params
+ // - esp_bt_status_t status
+ // - esp_bd_addr_t bda
+ // - uint16_t min_int
+ // - uint16_t max_int
+ // - uint16_t latency
+ // - uint16_t conn_int
+ // - uint16_t timeout
+ //
+ case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %d, bd_addr: %s, min_int: %d, max_int: %d, latency: %d, conn_int: %d, timeout: %d]",
+ param->update_conn_params.status,
+ BLEAddress(param->update_conn_params.bda).toString().c_str(),
+ param->update_conn_params.min_int,
+ param->update_conn_params.max_int,
+ param->update_conn_params.latency,
+ param->update_conn_params.conn_int,
+ param->update_conn_params.timeout
+ );
+ break;
+ } // ESP_GAP_BLE_SCAN_UPDATE_CONN_PARAMS_EVT
+
+
+ //
+ // ESP_GAP_BLE_SEC_REQ_EVT
+ //
+ case ESP_GAP_BLE_SEC_REQ_EVT: {
+ ESP_LOGD(LOG_TAG, "[bd_addr: %s]", BLEAddress(param->ble_security.ble_req.bd_addr).toString().c_str());
+ break;
+ } // ESP_GAP_BLE_SEC_REQ_EVT
+
+
+ default: {
+ ESP_LOGD(LOG_TAG, "*** dumpGapEvent: Logger not coded ***");
+ break;
+ } // default
+ } // switch
+} // dumpGapEvent
+
+
+/**
+ * @brief Decode and dump a GATT client event
+ *
+ * @param [in] event The type of event received.
+ * @param [in] evtParam The data associated with the event.
+ */
+void BLEUtils::dumpGattClientEvent(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* evtParam) {
+
+ //esp_ble_gattc_cb_param_t *evtParam = (esp_ble_gattc_cb_param_t *)param;
+ ESP_LOGD(LOG_TAG, "GATT Event: %s", BLEUtils::gattClientEventTypeToString(event).c_str());
+ switch(event) {
+ //
+ // ESP_GATTC_CLOSE_EVT
+ //
+ // close:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ // - esp_gatt_conn_reason_t reason
+ //
+ case ESP_GATTC_CLOSE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, reason:%s, conn_id: %d]",
+ BLEUtils::gattStatusToString(evtParam->close.status).c_str(),
+ BLEUtils::gattCloseReasonToString(evtParam->close.reason).c_str(),
+ evtParam->close.conn_id);
+ break;
+ }
+
+ //
+ // ESP_GATTC_CONNECT_EVT
+ //
+ // connect:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ case ESP_GATTC_CONNECT_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]",
+ evtParam->connect.conn_id,
+ BLEAddress(evtParam->connect.remote_bda).toString().c_str()
+ );
+ break;
+ }
+
+ //
+ // ESP_GATTC_DISCONNECT_EVT
+ //
+ // disconnect:
+ // - 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, "[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()
+ );
+ break;
+ } // ESP_GATTC_DISCONNECT_EVT
+
+ //
+ // ESP_GATTC_GET_CHAR_EVT
+ //
+ // get_char:
+ // - esp_gatt_status_t status
+ // - uin1t6_t conn_id
+ // - esp_gatt_srvc_id_t srvc_id
+ // - esp_gatt_id_t char_id
+ // - esp_gatt_char_prop_t char_prop
+ //
+ /*
+ case ESP_GATTC_GET_CHAR_EVT: {
+
+ // If the status of the event shows that we have a value other than ESP_GATT_OK then the
+ // characteristic fields are not set to a usable value .. so don't try and log them.
+ if (evtParam->get_char.status == ESP_GATT_OK) {
+ std::string description = "Unknown";
+ if (evtParam->get_char.char_id.uuid.len == ESP_UUID_LEN_16) {
+ description = BLEUtils::gattCharacteristicUUIDToString(evtParam->get_char.char_id.uuid.uuid.uuid16);
+ }
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s, char_id: %s [description: %s]\nchar_prop: %s]",
+ BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(),
+ evtParam->get_char.conn_id,
+ BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str(),
+ gattIdToString(evtParam->get_char.char_id).c_str(),
+ description.c_str(),
+ BLEUtils::characteristicPropertiesToString(evtParam->get_char.char_prop).c_str()
+ );
+ } else {
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, srvc_id: %s]",
+ BLEUtils::gattStatusToString(evtParam->get_char.status).c_str(),
+ evtParam->get_char.conn_id,
+ BLEUtils::gattServiceIdToString(evtParam->get_char.srvc_id).c_str()
+ );
+ }
+ break;
+ } // ESP_GATTC_GET_CHAR_EVT
+ */
+
+ //
+ // ESP_GATTC_NOTIFY_EVT
+ //
+ // notify
+ // uint16_t conn_id
+ // esp_bd_addr_t remote_bda
+ // handle handle
+ // uint16_t value_len
+ // uint8_t* value
+ // bool is_notify
+ //
+ case ESP_GATTC_NOTIFY_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s, handle: %d 0x%.2x, value_len: %d, is_notify: %d]",
+ evtParam->notify.conn_id,
+ BLEAddress(evtParam->notify.remote_bda).toString().c_str(),
+ evtParam->notify.handle,
+ evtParam->notify.handle,
+ evtParam->notify.value_len,
+ evtParam->notify.is_notify
+ );
+ break;
+ }
+
+ //
+ // ESP_GATTC_OPEN_EVT
+ //
+ // open:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ // - esp_bd_addr_t remote_bda
+ // - uint16_t mtu
+ //
+ case ESP_GATTC_OPEN_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, remote_bda: %s, mtu: %d]",
+ BLEUtils::gattStatusToString(evtParam->open.status).c_str(),
+ evtParam->open.conn_id,
+ BLEAddress(evtParam->open.remote_bda).toString().c_str(),
+ evtParam->open.mtu);
+ break;
+ } // ESP_GATTC_OPEN_EVT
+
+
+ //
+ // ESP_GATTC_READ_CHAR_EVT
+ //
+ // Callback to indicate that requested data that we wanted to read is now available.
+ //
+ // read:
+ // esp_gatt_status_t status
+ // uint16_t conn_id
+ // uint16_t handle
+ // uint8_t* value
+ // uint16_t value_type
+ // uint16_t value_len
+ case ESP_GATTC_READ_CHAR_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d, handle: %d 0x%.2x, value_len: %d]",
+ BLEUtils::gattStatusToString(evtParam->read.status).c_str(),
+ evtParam->read.conn_id,
+ evtParam->read.handle,
+ evtParam->read.handle,
+ evtParam->read.value_len
+ );
+ if (evtParam->read.status == ESP_GATT_OK) {
+ GeneralUtils::hexDump(evtParam->read.value, evtParam->read.value_len);
+ /*
+ char *pHexData = BLEUtils::buildHexData(nullptr, evtParam->read.value, evtParam->read.value_len);
+ ESP_LOGD(LOG_TAG, "value: %s \"%s\"", pHexData, BLEUtils::buildPrintData(evtParam->read.value, evtParam->read.value_len).c_str());
+ free(pHexData);
+ */
+ }
+ break;
+ } // ESP_GATTC_READ_CHAR_EVT
+
+
+ //
+ // ESP_GATTC_REG_EVT
+ //
+ // reg:
+ // - esp_gatt_status_t status
+ // - uint16_t app_id
+ //
+ case ESP_GATTC_REG_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, app_id: 0x%x]",
+ BLEUtils::gattStatusToString(evtParam->reg.status).c_str(),
+ evtParam->reg.app_id);
+ break;
+ } // ESP_GATTC_REG_EVT
+
+
+ //
+ // ESP_GATTC_REG_FOR_NOTIFY_EVT
+ //
+ // reg_for_notify:
+ // - esp_gatt_status_t status
+ // - uint16_t handle
+ case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, handle: %d 0x%.2x]",
+ BLEUtils::gattStatusToString(evtParam->reg_for_notify.status).c_str(),
+ evtParam->reg_for_notify.handle,
+ evtParam->reg_for_notify.handle
+ );
+ break;
+ } // ESP_GATTC_REG_FOR_NOTIFY_EVT
+
+
+ //
+ // ESP_GATTC_SEARCH_CMPL_EVT
+ //
+ // search_cmpl:
+ // - esp_gatt_status_t status
+ // - uint16_t conn_id
+ //
+ case ESP_GATTC_SEARCH_CMPL_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: %d]",
+ BLEUtils::gattStatusToString(evtParam->search_cmpl.status).c_str(),
+ evtParam->search_cmpl.conn_id);
+ break;
+ } // ESP_GATTC_SEARCH_CMPL_EVT
+
+
+ //
+ // ESP_GATTC_SEARCH_RES_EVT
+ //
+ // search_res:
+ // - uint16_t conn_id
+ // - uint16_t start_handle
+ // - uint16_t end_handle
+ // - esp_gatt_id_t srvc_id
+ //
+ case ESP_GATTC_SEARCH_RES_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, start_handle: %d 0x%.2x, end_handle: %d 0x%.2x, srvc_id: %s",
+ evtParam->search_res.conn_id,
+ evtParam->search_res.start_handle,
+ evtParam->search_res.start_handle,
+ evtParam->search_res.end_handle,
+ evtParam->search_res.end_handle,
+ gattIdToString(evtParam->search_res.srvc_id).c_str());
+ break;
+ } // ESP_GATTC_SEARCH_RES_EVT
+
+
+ //
+ // ESP_GATTC_WRITE_CHAR_EVT
+ //
+ // write:
+ // - 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, offset: %d]",
+ BLEUtils::gattStatusToString(evtParam->write.status).c_str(),
+ evtParam->write.conn_id,
+ evtParam->write.handle,
+ evtParam->write.handle,
+ evtParam->write.offset
+ );
+ break;
+ } // ESP_GATTC_WRITE_CHAR_EVT
+
+ default:
+ break;
+ }
+} // dumpGattClientEvent
+
+
+/**
+ * @brief Dump the details of a GATT server event.
+ * A GATT Server event is a callback received from the BLE subsystem when we are acting as a BLE
+ * server. The callback indicates the type of event in the `event` field. The `evtParam` is a
+ * union of structures where we can use the `event` to indicate which of the structures has been
+ * populated and hence is valid.
+ *
+ * @param [in] event The event type that was posted.
+ * @param [in] evtParam A union of structures only one of which is populated.
+ */
+void BLEUtils::dumpGattServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* evtParam) {
+ ESP_LOGD(LOG_TAG, "GATT ServerEvent: %s", BLEUtils::gattServerEventTypeToString(event).c_str());
+ switch(event) {
+
+ case ESP_GATTS_ADD_CHAR_DESCR_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]",
+ gattStatusToString(evtParam->add_char_descr.status).c_str(),
+ evtParam->add_char_descr.attr_handle,
+ evtParam->add_char_descr.attr_handle,
+ evtParam->add_char_descr.service_handle,
+ evtParam->add_char_descr.service_handle,
+ BLEUUID(evtParam->add_char_descr.descr_uuid).toString().c_str());
+ break;
+ } // ESP_GATTS_ADD_CHAR_DESCR_EVT
+
+ case ESP_GATTS_ADD_CHAR_EVT: {
+ if (evtParam->add_char.status == ESP_GATT_OK) {
+ ESP_LOGD(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]",
+ gattStatusToString(evtParam->add_char.status).c_str(),
+ evtParam->add_char.attr_handle,
+ evtParam->add_char.attr_handle,
+ evtParam->add_char.service_handle,
+ evtParam->add_char.service_handle,
+ BLEUUID(evtParam->add_char.char_uuid).toString().c_str());
+ } else {
+ ESP_LOGE(LOG_TAG, "[status: %s, attr_handle: %d 0x%.2x, service_handle: %d 0x%.2x, char_uuid: %s]",
+ gattStatusToString(evtParam->add_char.status).c_str(),
+ evtParam->add_char.attr_handle,
+ evtParam->add_char.attr_handle,
+ evtParam->add_char.service_handle,
+ evtParam->add_char.service_handle,
+ BLEUUID(evtParam->add_char.char_uuid).toString().c_str());
+ }
+ break;
+ } // ESP_GATTS_ADD_CHAR_EVT
+
+
+ // ESP_GATTS_CONF_EVT
+ //
+ // conf:
+ // - esp_gatt_status_t status – The status code.
+ // - uint16_t conn_id – The connection used.
+ //
+ case ESP_GATTS_CONF_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, conn_id: 0x%.2x]",
+ gattStatusToString(evtParam->conf.status).c_str(),
+ evtParam->conf.conn_id);
+ break;
+ } // ESP_GATTS_CONF_EVT
+
+
+ case ESP_GATTS_CONGEST_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, congested: %d]",
+ evtParam->congest.conn_id,
+ evtParam->congest.congested);
+ break;
+ } // ESP_GATTS_CONGEST_EVT
+
+ case ESP_GATTS_CONNECT_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]",
+ evtParam->connect.conn_id,
+ BLEAddress(evtParam->connect.remote_bda).toString().c_str());
+ break;
+ } // ESP_GATTS_CONNECT_EVT
+
+ case ESP_GATTS_CREATE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, service_handle: %d 0x%.2x, service_id: [%s]]",
+ gattStatusToString(evtParam->create.status).c_str(),
+ evtParam->create.service_handle,
+ evtParam->create.service_handle,
+ gattServiceIdToString(evtParam->create.service_id).c_str());
+ break;
+ } // ESP_GATTS_CREATE_EVT
+
+ case ESP_GATTS_DISCONNECT_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, remote_bda: %s]",
+ evtParam->connect.conn_id,
+ BLEAddress(evtParam->connect.remote_bda).toString().c_str());
+ break;
+ } // ESP_GATTS_DISCONNECT_EVT
+
+
+ // ESP_GATTS_EXEC_WRITE_EVT
+ // exec_write:
+ // - uint16_t conn_id
+ // - uint32_t trans_id
+ // - esp_bd_addr_t bda
+ // - uint8_t exec_write_flag
+ //
+ case ESP_GATTS_EXEC_WRITE_EVT: {
+ char* pWriteFlagText;
+ switch(evtParam->exec_write.exec_write_flag) {
+ case ESP_GATT_PREP_WRITE_EXEC: {
+ pWriteFlagText = (char*)"WRITE";
+ break;
+ }
+
+ case ESP_GATT_PREP_WRITE_CANCEL: {
+ pWriteFlagText = (char*)"CANCEL";
+ break;
+ }
+
+ default:
+ pWriteFlagText = (char*)"<Unknown>";
+ break;
+ }
+
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, exec_write_flag: 0x%.2x=%s]",
+ evtParam->exec_write.conn_id,
+ evtParam->exec_write.trans_id,
+ BLEAddress(evtParam->exec_write.bda).toString().c_str(),
+ evtParam->exec_write.exec_write_flag,
+ pWriteFlagText);
+ break;
+ } // ESP_GATTS_DISCONNECT_EVT
+
+
+ case ESP_GATTS_MTU_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, mtu: %d]",
+ evtParam->mtu.conn_id,
+ evtParam->mtu.mtu);
+ break;
+ } // ESP_GATTS_MTU_EVT
+
+ case ESP_GATTS_READ_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, is_long: %d, need_rsp:%d]",
+ evtParam->read.conn_id,
+ evtParam->read.trans_id,
+ BLEAddress(evtParam->read.bda).toString().c_str(),
+ evtParam->read.handle,
+ evtParam->read.is_long,
+ evtParam->read.need_rsp);
+ break;
+ } // ESP_GATTS_READ_EVT
+
+ case ESP_GATTS_RESPONSE_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, handle: 0x%.2x]",
+ gattStatusToString(evtParam->rsp.status).c_str(),
+ evtParam->rsp.handle);
+ break;
+ } // ESP_GATTS_RESPONSE_EVT
+
+ case ESP_GATTS_REG_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, app_id: %d]",
+ gattStatusToString(evtParam->reg.status).c_str(),
+ evtParam->reg.app_id);
+ break;
+ } // ESP_GATTS_REG_EVT
+
+
+ // ESP_GATTS_START_EVT
+ //
+ // start:
+ // - esp_gatt_status_t status
+ // - uint16_t service_handle
+ //
+ case ESP_GATTS_START_EVT: {
+ ESP_LOGD(LOG_TAG, "[status: %s, service_handle: 0x%.2x]",
+ gattStatusToString(evtParam->start.status).c_str(),
+ evtParam->start.service_handle);
+ break;
+ } // ESP_GATTS_START_EVT
+
+
+ // ESP_GATTS_WRITE_EVT
+ //
+ // write:
+ // - uint16_t conn_id – The connection id.
+ // - uint16_t trans_id – The transfer id.
+ // - esp_bd_addr_t bda – The address of the partner.
+ // - uint16_t handle – The attribute handle.
+ // - uint16_t offset – The offset of the currently received within the whole value.
+ // - bool need_rsp – Do we need a response?
+ // - bool is_prep – Is this a write prepare? If set, then this is to be considered part of the received value and not the whole value. A subsequent ESP_GATTS_EXEC_WRITE will mark the total.
+ // - uint16_t len – The length of the incoming value part.
+ // - uint8_t* value – The data for this value part.
+ //
+ case ESP_GATTS_WRITE_EVT: {
+ ESP_LOGD(LOG_TAG, "[conn_id: %d, trans_id: %d, bda: %s, handle: 0x%.2x, offset: %d, need_rsp: %d, is_prep: %d, len: %d]",
+ evtParam->write.conn_id,
+ evtParam->write.trans_id,
+ BLEAddress(evtParam->write.bda).toString().c_str(),
+ evtParam->write.handle,
+ evtParam->write.offset,
+ evtParam->write.need_rsp,
+ evtParam->write.is_prep,
+ evtParam->write.len);
+ char* pHex = buildHexData(nullptr, evtParam->write.value, evtParam->write.len);
+ ESP_LOGD(LOG_TAG, "[Data: %s]", pHex);
+ free(pHex);
+ break;
+ } // ESP_GATTS_WRITE_EVT
+
+ default:
+ ESP_LOGD(LOG_TAG, "dumpGattServerEvent: *** NOT CODED ***");
+ break;
+ }
+} // dumpGattServerEvent
+
+
+/**
+ * @brief Convert a BLE event type to a string.
+ * @param [in] eventType The event type.
+ * @return The event type as a string.
+ */
+const char* BLEUtils::eventTypeToString(esp_ble_evt_type_t eventType) {
+ switch(eventType) {
+ case ESP_BLE_EVT_CONN_ADV:
+ return "ESP_BLE_EVT_CONN_ADV";
+ case ESP_BLE_EVT_CONN_DIR_ADV:
+ return "ESP_BLE_EVT_CONN_DIR_ADV";
+ case ESP_BLE_EVT_DISC_ADV:
+ return "ESP_BLE_EVT_DISC_ADV";
+ case ESP_BLE_EVT_NON_CONN_ADV:
+ return "ESP_BLE_EVT_NON_CONN_ADV";
+ case ESP_BLE_EVT_SCAN_RSP:
+ return "ESP_BLE_EVT_SCAN_RSP";
+ default:
+ ESP_LOGD(LOG_TAG, "Unknown esp_ble_evt_type_t: %d (0x%.2x)", eventType, eventType);
+ return "*** Unknown ***";
+ }
+} // eventTypeToString
+
+
+
+/**
+ * @brief Convert a BT GAP event type to a string representation.
+ * @param [in] eventType The type of event.
+ * @return A string representation of the event type.
+ */
+const char* BLEUtils::gapEventToString(uint32_t eventType) {
+ switch(eventType) {
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
+ return "ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
+ return "ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
+ return "ESP_GAP_BLE_ADV_START_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: /*!< When stop adv complete, the event comes */
+ return "ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_AUTH_CMPL_EVT: /* Authentication complete indication. */
+ return "ESP_GAP_BLE_AUTH_CMPL_EVT";
+
+ case ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT:
+ return "ESP_GAP_BLE_CLEAR_BOND_DEV_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT:
+ return "ESP_GAP_BLE_GET_BOND_DEV_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_KEY_EVT: /* BLE key event for peer device keys */
+ return "ESP_GAP_BLE_KEY_EVT";
+
+ case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */
+ return "ESP_GAP_BLE_LOCAL_IR_EVT";
+
+ case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */
+ return "ESP_GAP_BLE_LOCAL_ER_EVT";
+
+ case ESP_GAP_BLE_NC_REQ_EVT: /* Numeric Comparison request event */
+ return "ESP_GAP_BLE_NC_REQ_EVT";
+
+ case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */
+ return "ESP_GAP_BLE_OOB_REQ_EVT";
+
+ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: /* passkey notification event */
+ return "ESP_GAP_BLE_PASSKEY_NOTIF_EVT";
+
+ case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */
+ return "ESP_GAP_BLE_PASSKEY_REQ_EVT";
+
+ case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
+ return "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT:
+ return "ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SCAN_RESULT_EVT:
+ return "ESP_GAP_BLE_SCAN_RESULT_EVT";
+
+ case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SCAN_START_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SEC_REQ_EVT: /* BLE security request */
+ return "ESP_GAP_BLE_SEC_REQ_EVT";
+
+ case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
+ return "ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT";
+
+ case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT:
+ return "ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT";
+
+ case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
+ return "ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT";
+
+
+ default:
+ ESP_LOGD(LOG_TAG, "gapEventToString: Unknown event type %d 0x%.2x", eventType, eventType);
+ return "Unknown event type";
+ }
+} // gapEventToString
+
+
+std::string BLEUtils::gattCharacteristicUUIDToString(uint32_t characteristicUUID) {
+ const characteristicMap_t *p = g_characteristicsMappings;
+ while (strlen(p->name) > 0) {
+ if (p->assignedNumber == characteristicUUID) {
+ return std::string(p->name);
+ }
+ p++;
+ }
+ return "Unknown";
+} // gattCharacteristicUUIDToString
+
+
+/**
+ * @brief Given the UUID for a BLE defined descriptor, return its string representation.
+ * @param [in] descriptorUUID UUID of the descriptor to be returned as a string.
+ * @return The string representation of a descriptor UUID.
+ */
+std::string BLEUtils::gattDescriptorUUIDToString(uint32_t descriptorUUID) {
+ gattdescriptor_t* p = (gattdescriptor_t *)g_descriptor_ids;
+ while (strlen(p->name) > 0) {
+ if (p->assignedNumber == descriptorUUID) {
+ return std::string(p->name);
+ }
+ p++;
+ }
+ return "";
+} // gattDescriptorUUIDToString
+
+
+/**
+ * @brief Return a string representation of an esp_gattc_service_elem_t.
+ * @return A string representation of an esp_gattc_service_elem_t.
+ */
+std::string BLEUtils::gattcServiceElementToString(esp_gattc_service_elem_t* pGATTCServiceElement) {
+ std::stringstream ss;
+
+ ss << "[uuid: " << BLEUUID(pGATTCServiceElement->uuid).toString() <<
+ ", start_handle: " << pGATTCServiceElement->start_handle <<
+ " 0x" << std::hex << pGATTCServiceElement->start_handle <<
+ ", end_handle: " << std::dec << pGATTCServiceElement->end_handle <<
+ " 0x" << std::hex << pGATTCServiceElement->end_handle << "]";
+ return ss.str();
+} // gattcServiceElementToString
+
+
+/**
+ * @brief Convert an esp_gatt_srvc_id_t to a string.
+ */
+std::string BLEUtils::gattServiceIdToString(esp_gatt_srvc_id_t srvcId) {
+ return gattIdToString(srvcId.id);
+} // gattServiceIdToString
+
+
+std::string BLEUtils::gattServiceToString(uint32_t serviceId) {
+ gattService_t* p = (gattService_t *)g_gattServices;
+ while (strlen(p->name) > 0) {
+ if (p->assignedNumber == serviceId) {
+ return std::string(p->name);
+ }
+ p++;
+ }
+ return "Unknown";
+} // gattServiceToString
+
+
+/**
+ * @brief Convert a GATT status to a string.
+ *
+ * @param [in] status The status to convert.
+ * @return A string representation of the status.
+ */
+std::string BLEUtils::gattStatusToString(esp_gatt_status_t status) {
+ switch(status) {
+ case ESP_GATT_OK:
+ return "ESP_GATT_OK";
+ case ESP_GATT_INVALID_HANDLE:
+ return "ESP_GATT_INVALID_HANDLE";
+ case ESP_GATT_READ_NOT_PERMIT:
+ return "ESP_GATT_READ_NOT_PERMIT";
+ case ESP_GATT_WRITE_NOT_PERMIT:
+ return "ESP_GATT_WRITE_NOT_PERMIT";
+ case ESP_GATT_INVALID_PDU:
+ return "ESP_GATT_INVALID_PDU";
+ case ESP_GATT_INSUF_AUTHENTICATION:
+ return "ESP_GATT_INSUF_AUTHENTICATION";
+ case ESP_GATT_REQ_NOT_SUPPORTED:
+ return "ESP_GATT_REQ_NOT_SUPPORTED";
+ case ESP_GATT_INVALID_OFFSET:
+ return "ESP_GATT_INVALID_OFFSET";
+ case ESP_GATT_INSUF_AUTHORIZATION:
+ return "ESP_GATT_INSUF_AUTHORIZATION";
+ case ESP_GATT_PREPARE_Q_FULL:
+ return "ESP_GATT_PREPARE_Q_FULL";
+ case ESP_GATT_NOT_FOUND:
+ return "ESP_GATT_NOT_FOUND";
+ case ESP_GATT_NOT_LONG:
+ return "ESP_GATT_NOT_LONG";
+ case ESP_GATT_INSUF_KEY_SIZE:
+ return "ESP_GATT_INSUF_KEY_SIZE";
+ case ESP_GATT_INVALID_ATTR_LEN:
+ return "ESP_GATT_INVALID_ATTR_LEN";
+ case ESP_GATT_ERR_UNLIKELY:
+ return "ESP_GATT_ERR_UNLIKELY";
+ case ESP_GATT_INSUF_ENCRYPTION:
+ return "ESP_GATT_INSUF_ENCRYPTION";
+ case ESP_GATT_UNSUPPORT_GRP_TYPE:
+ return "ESP_GATT_UNSUPPORT_GRP_TYPE";
+ case ESP_GATT_INSUF_RESOURCE:
+ return "ESP_GATT_INSUF_RESOURCE";
+ case ESP_GATT_NO_RESOURCES:
+ return "ESP_GATT_NO_RESOURCES";
+ case ESP_GATT_INTERNAL_ERROR:
+ return "ESP_GATT_INTERNAL_ERROR";
+ case ESP_GATT_WRONG_STATE:
+ return "ESP_GATT_WRONG_STATE";
+ case ESP_GATT_DB_FULL:
+ return "ESP_GATT_DB_FULL";
+ case ESP_GATT_BUSY:
+ return "ESP_GATT_BUSY";
+ case ESP_GATT_ERROR:
+ return "ESP_GATT_ERROR";
+ case ESP_GATT_CMD_STARTED:
+ return "ESP_GATT_CMD_STARTED";
+ case ESP_GATT_ILLEGAL_PARAMETER:
+ return "ESP_GATT_ILLEGAL_PARAMETER";
+ case ESP_GATT_PENDING:
+ return "ESP_GATT_PENDING";
+ case ESP_GATT_AUTH_FAIL:
+ return "ESP_GATT_AUTH_FAIL";
+ case ESP_GATT_MORE:
+ return "ESP_GATT_MORE";
+ case ESP_GATT_INVALID_CFG:
+ return "ESP_GATT_INVALID_CFG";
+ case ESP_GATT_SERVICE_STARTED:
+ return "ESP_GATT_SERVICE_STARTED";
+ case ESP_GATT_ENCRYPED_NO_MITM:
+ return "ESP_GATT_ENCRYPED_NO_MITM";
+ case ESP_GATT_NOT_ENCRYPTED:
+ return "ESP_GATT_NOT_ENCRYPTED";
+ case ESP_GATT_CONGESTED:
+ return "ESP_GATT_CONGESTED";
+ case ESP_GATT_DUP_REG:
+ return "ESP_GATT_DUP_REG";
+ case ESP_GATT_ALREADY_OPEN:
+ return "ESP_GATT_ALREADY_OPEN";
+ case ESP_GATT_CANCEL:
+ return "ESP_GATT_CANCEL";
+ case ESP_GATT_STACK_RSP:
+ return "ESP_GATT_STACK_RSP";
+ case ESP_GATT_APP_RSP:
+ return "ESP_GATT_APP_RSP";
+ case ESP_GATT_UNKNOWN_ERROR:
+ return "ESP_GATT_UNKNOWN_ERROR";
+ case ESP_GATT_CCC_CFG_ERR:
+ return "ESP_GATT_CCC_CFG_ERR";
+ case ESP_GATT_PRC_IN_PROGRESS:
+ return "ESP_GATT_PRC_IN_PROGRESS";
+ case ESP_GATT_OUT_OF_RANGE:
+ return "ESP_GATT_OUT_OF_RANGE";
+ default:
+ return "Unknown";
+ }
+} // gattStatusToString
+
+
+
+std::string BLEUtils::getMember(uint32_t memberId) {
+ member_t* p = (member_t *)members_ids;
+
+ while (strlen(p->name) > 0) {
+ if (p->assignedNumber == memberId) {
+ return std::string(p->name);
+ }
+ p++;
+ }
+ return "Unknown";
+}
+
+/**
+ * @brief convert a GAP search event to a string.
+ * @param [in] searchEvt
+ * @return The search event type as a string.
+ */
+const char* BLEUtils::searchEventTypeToString(esp_gap_search_evt_t searchEvt) {
+ switch(searchEvt) {
+ case ESP_GAP_SEARCH_INQ_RES_EVT:
+ return "ESP_GAP_SEARCH_INQ_RES_EVT";
+ case ESP_GAP_SEARCH_INQ_CMPL_EVT:
+ return "ESP_GAP_SEARCH_INQ_CMPL_EVT";
+ case ESP_GAP_SEARCH_DISC_RES_EVT:
+ return "ESP_GAP_SEARCH_DISC_RES_EVT";
+ case ESP_GAP_SEARCH_DISC_BLE_RES_EVT:
+ return "ESP_GAP_SEARCH_DISC_BLE_RES_EVT";
+ case ESP_GAP_SEARCH_DISC_CMPL_EVT:
+ return "ESP_GAP_SEARCH_DISC_CMPL_EVT";
+ case ESP_GAP_SEARCH_DI_DISC_CMPL_EVT:
+ return "ESP_GAP_SEARCH_DI_DISC_CMPL_EVT";
+ case ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT:
+ return "ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT";
+ default:
+ ESP_LOGD(LOG_TAG, "Unknown event type: 0x%x", searchEvt);
+ return "Unknown event type";
+ }
+} // searchEventTypeToString
+
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEUtils.h b/sensor/patchedBLE/src/BLEUtils.h
new file mode 100644
index 0000000..b2baee5
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEUtils.h
@@ -0,0 +1,63 @@
+/*
+ * BLEUtils.h
+ *
+ * Created on: Mar 25, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_
+#define COMPONENTS_CPP_UTILS_BLEUTILS_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <esp_gattc_api.h> // ESP32 BLE
+#include <esp_gatts_api.h> // ESP32 BLE
+#include <esp_gap_ble_api.h> // ESP32 BLE
+#include <string>
+#include "BLEClient.h"
+
+/**
+ * @brief A set of general %BLE utilities.
+ */
+class BLEUtils {
+public:
+ static const char* addressTypeToString(esp_ble_addr_type_t type);
+ static std::string adFlagsToString(uint8_t adFlags);
+ static const char* advTypeToString(uint8_t advType);
+ static esp_gatt_id_t buildGattId(esp_bt_uuid_t uuid, uint8_t inst_id=0);
+ static esp_gatt_srvc_id_t buildGattSrvcId(esp_gatt_id_t gattId, bool is_primary=true);
+ static char* buildHexData(uint8_t* target, uint8_t* source, uint8_t length);
+ static std::string buildPrintData(uint8_t* source, size_t length);
+ static std::string characteristicPropertiesToString(esp_gatt_char_prop_t prop);
+ static const char* devTypeToString(esp_bt_dev_type_t type);
+ static void dumpGapEvent(
+ esp_gap_ble_cb_event_t event,
+ esp_ble_gap_cb_param_t* param);
+ static void dumpGattClientEvent(
+ esp_gattc_cb_event_t event,
+ esp_gatt_if_t gattc_if,
+ esp_ble_gattc_cb_param_t* evtParam);
+ static void dumpGattServerEvent(
+ esp_gatts_cb_event_t event,
+ esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t* evtParam);
+ static const char* eventTypeToString(esp_ble_evt_type_t eventType);
+ static BLEClient* findByAddress(BLEAddress address);
+ static BLEClient* findByConnId(uint16_t conn_id);
+ static const char* gapEventToString(uint32_t eventType);
+ static std::string gattCharacteristicUUIDToString(uint32_t characteristicUUID);
+ static std::string gattClientEventTypeToString(esp_gattc_cb_event_t eventType);
+ static std::string gattCloseReasonToString(esp_gatt_conn_reason_t reason);
+ static std::string gattcServiceElementToString(esp_gattc_service_elem_t *pGATTCServiceElement);
+ static std::string gattDescriptorUUIDToString(uint32_t descriptorUUID);
+ static std::string gattServerEventTypeToString(esp_gatts_cb_event_t eventType);
+ static std::string gattServiceIdToString(esp_gatt_srvc_id_t srvcId);
+ static std::string gattServiceToString(uint32_t serviceId);
+ static std::string gattStatusToString(esp_gatt_status_t status);
+ static std::string getMember(uint32_t memberId);
+ static void registerByAddress(BLEAddress address, BLEClient* pDevice);
+ static void registerByConnId(uint16_t conn_id, BLEClient* pDevice);
+ static const char* searchEventTypeToString(esp_gap_search_evt_t searchEvt);
+};
+
+#endif // CONFIG_BT_ENABLED
+#endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */
diff --git a/sensor/patchedBLE/src/BLEValue.cpp b/sensor/patchedBLE/src/BLEValue.cpp
new file mode 100644
index 0000000..49818e2
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEValue.cpp
@@ -0,0 +1,142 @@
+/*
+ * BLEValue.cpp
+ *
+ * Created on: Jul 17, 2017
+ * Author: kolban
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include <esp_log.h>
+
+#include "BLEValue.h"
+#ifdef ARDUINO_ARCH_ESP32
+#include "esp32-hal-log.h"
+#endif
+
+static const char* LOG_TAG="BLEValue";
+
+BLEValue::BLEValue() {
+ m_accumulation = "";
+ m_value = "";
+ m_readOffset = 0;
+} // BLEValue
+
+
+/**
+ * @brief Add a message part to the accumulation.
+ * The accumulation is a growing set of data that is added to until a commit or cancel.
+ * @param [in] part A message part being added.
+ */
+void BLEValue::addPart(std::string part) {
+ ESP_LOGD(LOG_TAG, ">> addPart: length=%d", part.length());
+ m_accumulation += part;
+} // addPart
+
+
+/**
+ * @brief Add a message part to the accumulation.
+ * The accumulation is a growing set of data that is added to until a commit or cancel.
+ * @param [in] pData A message part being added.
+ * @param [in] length The number of bytes being added.
+ */
+void BLEValue::addPart(uint8_t* pData, size_t length) {
+ ESP_LOGD(LOG_TAG, ">> addPart: length=%d", length);
+ m_accumulation += std::string((char *)pData, length);
+} // addPart
+
+
+/**
+ * @brief Cancel the current accumulation.
+ */
+void BLEValue::cancel() {
+ ESP_LOGD(LOG_TAG, ">> cancel");
+ m_accumulation = "";
+ m_readOffset = 0;
+} // cancel
+
+
+/**
+ * @brief Commit the current accumulation.
+ * When writing a value, we may find that we write it in "parts" meaning that the writes come in in pieces
+ * of the overall message. After the last part has been received, we may perform a commit which means that
+ * we now have the complete message and commit the change as a unit.
+ */
+void BLEValue::commit() {
+ ESP_LOGD(LOG_TAG, ">> commit");
+ // If there is nothing to commit, do nothing.
+ if (m_accumulation.length() == 0) {
+ return;
+ }
+ setValue(m_accumulation);
+ m_accumulation = "";
+ m_readOffset = 0;
+} // commit
+
+
+/**
+ * @brief Get a pointer to the data.
+ * @return A pointer to the data.
+ */
+uint8_t* BLEValue::getData() {
+ return (uint8_t*)m_value.data();
+}
+
+
+/**
+ * @brief Get the length of the data in bytes.
+ * @return The length of the data in bytes.
+ */
+size_t BLEValue::getLength() {
+ return m_value.length();
+} // getLength
+
+
+/**
+ * @brief Get the read offset.
+ * @return The read offset into the read.
+ */
+uint16_t BLEValue::getReadOffset() {
+ return m_readOffset;
+} // getReadOffset
+
+
+/**
+ * @brief Get the current value.
+ */
+std::string BLEValue::getValue() {
+ return m_value;
+} // getValue
+
+
+/**
+ * @brief Set the read offset
+ * @param [in] readOffset The offset into the read.
+ */
+void BLEValue::setReadOffset(uint16_t readOffset) {
+ m_readOffset = readOffset;
+} // setReadOffset
+
+
+/**
+ * @brief Set the current value.
+ */
+void BLEValue::setValue(std::string value) {
+ m_value = value;
+} // setValue
+
+
+/**
+ * @brief Set the current value.
+ * @param [in] pData The data for the current value.
+ * @param [in] The length of the new current value.
+ */
+void BLEValue::setValue(uint8_t* pData, size_t length) {
+ m_value = std::string((char*)pData, length);
+} // setValue
+
+
+
+
+
+#endif // CONFIG_BT_ENABLED
diff --git a/sensor/patchedBLE/src/BLEValue.h b/sensor/patchedBLE/src/BLEValue.h
new file mode 100644
index 0000000..92a7f9a
--- /dev/null
+++ b/sensor/patchedBLE/src/BLEValue.h
@@ -0,0 +1,38 @@
+/*
+ * BLEValue.h
+ *
+ * Created on: Jul 17, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_BLEVALUE_H_
+#define COMPONENTS_CPP_UTILS_BLEVALUE_H_
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+#include <string>
+
+/**
+ * @brief The model of a %BLE value.
+ */
+class BLEValue {
+public:
+ BLEValue();
+ void addPart(std::string part);
+ void addPart(uint8_t* pData, size_t length);
+ void cancel();
+ void commit();
+ uint8_t* getData();
+ size_t getLength();
+ uint16_t getReadOffset();
+ std::string getValue();
+ void setReadOffset(uint16_t readOffset);
+ void setValue(std::string value);
+ void setValue(uint8_t* pData, size_t length);
+
+private:
+ std::string m_accumulation;
+ uint16_t m_readOffset;
+ std::string m_value;
+};
+#endif // CONFIG_BT_ENABLED
+#endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */
diff --git a/sensor/patchedBLE/src/FreeRTOS.cpp b/sensor/patchedBLE/src/FreeRTOS.cpp
new file mode 100644
index 0000000..1ae01d7
--- /dev/null
+++ b/sensor/patchedBLE/src/FreeRTOS.cpp
@@ -0,0 +1,286 @@
+/*
+ * FreeRTOS.cpp
+ *
+ * Created on: Feb 24, 2017
+ * Author: kolban
+ */
+#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions
+#include <freertos/task.h> // Include the task definitions
+#include <freertos/semphr.h> // Include the semaphore definitions
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include "FreeRTOS.h"
+#include <esp_log.h>
+#include "sdkconfig.h"
+
+static const char* LOG_TAG = "FreeRTOS";
+
+/**
+ * Sleep for the specified number of milliseconds.
+ * @param[in] ms The period in milliseconds for which to sleep.
+ */
+void FreeRTOS::sleep(uint32_t ms) {
+ ::vTaskDelay(ms/portTICK_PERIOD_MS);
+} // sleep
+
+
+/**
+ * Start a new task.
+ * @param[in] task The function pointer to the function to be run in the task.
+ * @param[in] taskName A string identifier for the task.
+ * @param[in] param An optional parameter to be passed to the started task.
+ * @param[in] stackSize An optional paremeter supplying the size of the stack in which to run the task.
+ */
+void FreeRTOS::startTask(void task(void*), std::string taskName, void *param, int stackSize) {
+ ::xTaskCreate(task, taskName.data(), stackSize, param, 5, NULL);
+} // startTask
+
+
+/**
+ * Delete the task.
+ * @param[in] pTask An optional handle to the task to be deleted. If not supplied the calling task will be deleted.
+ */
+void FreeRTOS::deleteTask(TaskHandle_t pTask) {
+ ::vTaskDelete(pTask);
+} // deleteTask
+
+
+/**
+ * Get the time in milliseconds since the %FreeRTOS scheduler started.
+ * @return The time in milliseconds since the %FreeRTOS scheduler started.
+ */
+uint32_t FreeRTOS::getTimeSinceStart() {
+ return (uint32_t)(xTaskGetTickCount()*portTICK_PERIOD_MS);
+} // getTimeSinceStart
+
+/*
+ * public:
+ Semaphore(std::string = "<Unknown>");
+ ~Semaphore();
+ void give();
+ void take(std::string owner="<Unknown>");
+ void take(uint32_t timeoutMs, std::string owner="<Unknown>");
+ private:
+ SemaphoreHandle_t m_semaphore;
+ std::string m_name;
+ std::string m_owner;
+ };
+ *
+ */
+
+/**
+ * @brief Wait for a semaphore to be released by trying to take it and
+ * then releasing it again.
+ * @param [in] owner A debug tag.
+ * @return The value associated with the semaphore.
+ */
+uint32_t FreeRTOS::Semaphore::wait(std::string owner) {
+ ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());
+
+
+ if (m_usePthreads) {
+ pthread_mutex_lock(&m_pthread_mutex);
+ } else {
+ xSemaphoreTake(m_semaphore, portMAX_DELAY);
+ }
+
+ m_owner = owner;
+
+ if (m_usePthreads) {
+ pthread_mutex_unlock(&m_pthread_mutex);
+ } else {
+ xSemaphoreGive(m_semaphore);
+ }
+
+ ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str());
+ m_owner = std::string("<N/A>");
+ return m_value;
+} // wait
+
+
+FreeRTOS::Semaphore::Semaphore(std::string name) {
+ m_usePthreads = false; // Are we using pThreads or FreeRTOS?
+ if (m_usePthreads) {
+ pthread_mutex_init(&m_pthread_mutex, nullptr);
+ } else {
+ m_semaphore = xSemaphoreCreateMutex();
+ }
+
+ m_name = name;
+ m_owner = std::string("<N/A>");
+ m_value = 0;
+}
+
+
+FreeRTOS::Semaphore::~Semaphore() {
+ if (m_usePthreads) {
+ pthread_mutex_destroy(&m_pthread_mutex);
+ } else {
+ vSemaphoreDelete(m_semaphore);
+ }
+}
+
+
+/**
+ * @brief Give a semaphore.
+ * The Semaphore is given.
+ */
+void FreeRTOS::Semaphore::give() {
+ ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str());
+ if (m_usePthreads) {
+ pthread_mutex_unlock(&m_pthread_mutex);
+ } else {
+ xSemaphoreGive(m_semaphore);
+ }
+// #ifdef ARDUINO_ARCH_ESP32
+// FreeRTOS::sleep(10);
+// #endif
+
+ m_owner = std::string("<N/A>");
+} // Semaphore::give
+
+
+/**
+ * @brief Give a semaphore.
+ * The Semaphore is given with an associated value.
+ * @param [in] value The value to associate with the semaphore.
+ */
+void FreeRTOS::Semaphore::give(uint32_t value) {
+ m_value = value;
+ give();
+} // give
+
+
+/**
+ * @brief Give a semaphore from an ISR.
+ */
+void FreeRTOS::Semaphore::giveFromISR() {
+ BaseType_t higherPriorityTaskWoken;
+ if (m_usePthreads) {
+ assert(false);
+ } else {
+ xSemaphoreGiveFromISR(m_semaphore, &higherPriorityTaskWoken);
+ }
+} // 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.
+ */
+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 {
+ rc = ::xSemaphoreTake(m_semaphore, portMAX_DELAY);
+ }
+ m_owner = owner;
+ 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
+
+
+/**
+ * @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.
+ */
+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); // We apparently don't have a timed wait for pthreads.
+ } else {
+ rc = ::xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS);
+ }
+ m_owner = owner;
+ 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;
+ return stringStream.str();
+} // 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
+
+
+/**
+ * @brief Create a ring buffer.
+ * @param [in] length The amount of storage to allocate for the ring buffer.
+ * @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
+ */
+Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
+ m_handle = ::xRingbufferCreate(length, type);
+} // Ringbuffer
+
+
+Ringbuffer::~Ringbuffer() {
+ ::vRingbufferDelete(m_handle);
+} // ~Ringbuffer
+
+
+/**
+ * @brief Receive data from the buffer.
+ * @param [out] size On return, the size of data returned.
+ * @param [in] wait How long to wait.
+ * @return A pointer to the storage retrieved.
+ */
+void* Ringbuffer::receive(size_t* size, TickType_t wait) {
+ return ::xRingbufferReceive(m_handle, size, wait);
+} // receive
+
+
+/**
+ * @brief Return an item.
+ * @param [in] item The item to be returned/released.
+ */
+void Ringbuffer::returnItem(void* item) {
+ ::vRingbufferReturnItem(m_handle, item);
+} // returnItem
+
+
+/**
+ * @brief Send data to the buffer.
+ * @param [in] data The data to place into the buffer.
+ * @param [in] length The length of data to place into the buffer.
+ * @param [in] wait How long to wait before giving up. The default is to wait indefinitely.
+ * @return
+ */
+uint32_t Ringbuffer::send(void* data, size_t length, TickType_t wait) {
+ return ::xRingbufferSend(m_handle, data, length, wait);
+} // send
+
+
diff --git a/sensor/patchedBLE/src/FreeRTOS.h b/sensor/patchedBLE/src/FreeRTOS.h
new file mode 100644
index 0000000..ab0e83d
--- /dev/null
+++ b/sensor/patchedBLE/src/FreeRTOS.h
@@ -0,0 +1,70 @@
+/*
+ * FreeRTOS.h
+ *
+ * Created on: Feb 24, 2017
+ * Author: kolban
+ */
+
+#ifndef MAIN_FREERTOS_H_
+#define MAIN_FREERTOS_H_
+#include <stdint.h>
+#include <string>
+#include <pthread.h>
+
+#include <freertos/FreeRTOS.h> // Include the base FreeRTOS definitions.
+#include <freertos/task.h> // Include the task definitions.
+#include <freertos/semphr.h> // Include the semaphore definitions.
+#include <freertos/ringbuf.h> // Include the ringbuffer definitions.
+
+
+/**
+ * @brief Interface to %FreeRTOS functions.
+ */
+class FreeRTOS {
+public:
+ static void sleep(uint32_t ms);
+ static void startTask(void task(void *), std::string taskName, void *param=nullptr, int stackSize = 2048);
+ static void deleteTask(TaskHandle_t pTask = nullptr);
+
+ static uint32_t getTimeSinceStart();
+
+ class Semaphore {
+ public:
+ Semaphore(std::string owner = "<Unknown>");
+ ~Semaphore();
+ void give();
+ void give(uint32_t value);
+ void giveFromISR();
+ void setName(std::string name);
+ 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>");
+
+ private:
+ SemaphoreHandle_t m_semaphore;
+ pthread_mutex_t m_pthread_mutex;
+ std::string m_name;
+ std::string m_owner;
+ uint32_t m_value;
+ bool m_usePthreads;
+ };
+};
+
+
+/**
+ * @brief Ringbuffer.
+ */
+class Ringbuffer {
+public:
+ Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
+ ~Ringbuffer();
+
+ void* receive(size_t* size, TickType_t wait = portMAX_DELAY);
+ void returnItem(void* item);
+ uint32_t send(void* data, size_t length, TickType_t wait = portMAX_DELAY);
+private:
+ RingbufHandle_t m_handle;
+};
+
+#endif /* MAIN_FREERTOS_H_ */
diff --git a/sensor/patchedBLE/src/GeneralUtils.cpp b/sensor/patchedBLE/src/GeneralUtils.cpp
new file mode 100644
index 0000000..ccf74f7
--- /dev/null
+++ b/sensor/patchedBLE/src/GeneralUtils.cpp
@@ -0,0 +1,465 @@
+/*
+ * GeneralUtils.cpp
+ *
+ * Created on: May 20, 2017
+ * Author: kolban
+ */
+
+#include "GeneralUtils.h"
+#include <esp_log.h>
+#include <esp_system.h>
+#include <string.h>
+#include <stdio.h>
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <FreeRTOS.h>
+#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";
+
+static const char kBase64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+static int base64EncodedLength(size_t length) {
+ return (length + 2 - ((length + 2) % 3)) / 3 * 4;
+} // base64EncodedLength
+
+
+static int base64EncodedLength(const std::string &in) {
+ return base64EncodedLength(in.length());
+} // base64EncodedLength
+
+
+static void a3_to_a4(unsigned char * a4, unsigned char * a3) {
+ a4[0] = (a3[0] & 0xfc) >> 2;
+ a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
+ a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
+ a4[3] = (a3[2] & 0x3f);
+} // a3_to_a4
+
+
+static void a4_to_a3(unsigned char * a3, unsigned char * a4) {
+ a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
+ a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
+ a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
+} // a4_to_a3
+
+
+/**
+ * @brief Encode a string into base 64.
+ * @param [in] in
+ * @param [out] out
+ */
+bool GeneralUtils::base64Encode(const std::string &in, std::string *out) {
+ int i = 0, j = 0;
+ size_t enc_len = 0;
+ unsigned char a3[3];
+ unsigned char a4[4];
+
+ out->resize(base64EncodedLength(in));
+
+ int input_len = in.size();
+ std::string::const_iterator input = in.begin();
+
+ while (input_len--) {
+ a3[i++] = *(input++);
+ if (i == 3) {
+ a3_to_a4(a4, a3);
+
+ for (i = 0; i < 4; i++) {
+ (*out)[enc_len++] = kBase64Alphabet[a4[i]];
+ }
+
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 3; j++) {
+ a3[j] = '\0';
+ }
+
+ a3_to_a4(a4, a3);
+
+ for (j = 0; j < i + 1; j++) {
+ (*out)[enc_len++] = kBase64Alphabet[a4[j]];
+ }
+
+ while ((i++ < 3)) {
+ (*out)[enc_len++] = '=';
+ }
+ }
+
+ return (enc_len == out->size());
+} // base64Encode
+
+
+/**
+ * @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.
+ * @return True if the string ends with the given character.
+ */
+bool GeneralUtils::endsWith(std::string str, char c) {
+ if (str.empty()) {
+ return false;
+ }
+ if (str.at(str.length()-1) == c) {
+ return true;
+ }
+ return false;
+} // endsWidth
+
+
+static int DecodedLength(const std::string &in) {
+ int numEq = 0;
+ int n = in.size();
+
+ for (std::string::const_reverse_iterator it = in.rbegin(); *it == '='; ++it) {
+ ++numEq;
+ }
+ return ((6 * n) / 8) - numEq;
+} // DecodedLength
+
+
+static unsigned char b64_lookup(unsigned char c) {
+ if(c >='A' && c <='Z') return c - 'A';
+ if(c >='a' && c <='z') return c - 71;
+ if(c >='0' && c <='9') return c + 4;
+ if(c == '+') return 62;
+ if(c == '/') return 63;
+ return 255;
+}; // b64_lookup
+
+
+/**
+ * @brief Decode a chunk of data that is base64 encoded.
+ * @param [in] in The string to be decoded.
+ * @param [out] out The resulting data.
+ */
+bool GeneralUtils::base64Decode(const std::string &in, std::string *out) {
+ int i = 0, j = 0;
+ size_t dec_len = 0;
+ unsigned char a3[3];
+ unsigned char a4[4];
+
+ int input_len = in.size();
+ std::string::const_iterator input = in.begin();
+
+ out->resize(DecodedLength(in));
+
+ while (input_len--) {
+ if (*input == '=') {
+ break;
+ }
+
+ a4[i++] = *(input++);
+ if (i == 4) {
+ for (i = 0; i <4; i++) {
+ a4[i] = b64_lookup(a4[i]);
+ }
+
+ a4_to_a3(a3,a4);
+
+ for (i = 0; i < 3; i++) {
+ (*out)[dec_len++] = a3[i];
+ }
+
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++) {
+ a4[j] = '\0';
+ }
+
+ for (j = 0; j < 4; j++) {
+ a4[j] = b64_lookup(a4[j]);
+ }
+
+ a4_to_a3(a3,a4);
+
+ for (j = 0; j < i - 1; j++) {
+ (*out)[dec_len++] = a3[j];
+ }
+ }
+
+ return (dec_len == out->size());
+ } // base64Decode
+
+/*
+void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) {
+ uint32_t index=0;
+ std::stringstream ascii;
+ std::stringstream hex;
+ char asciiBuf[80];
+ char hexBuf[80];
+ hex.str("");
+ ascii.str("");
+ while(index < length) {
+ hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' ';
+ if (std::isprint(pData[index])) {
+ ascii << pData[index];
+ } else {
+ ascii << '.';
+ }
+ index++;
+ if (index % 16 == 0) {
+ strcpy(hexBuf, hex.str().c_str());
+ strcpy(asciiBuf, ascii.str().c_str());
+ ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf);
+ hex.str("");
+ ascii.str("");
+ }
+ }
+ if (index %16 != 0) {
+ while(index % 16 != 0) {
+ hex << " ";
+ index++;
+ }
+ strcpy(hexBuf, hex.str().c_str());
+ strcpy(asciiBuf, ascii.str().c_str());
+ ESP_LOGD(tag, "%s %s", hexBuf, asciiBuf);
+ //ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
+ }
+ FreeRTOS::sleep(1000);
+}
+*/
+
+/*
+void GeneralUtils::hexDump(uint8_t* pData, uint32_t length) {
+ uint32_t index=0;
+ static std::stringstream ascii;
+ static std::stringstream hex;
+ hex.str("");
+ ascii.str("");
+ while(index < length) {
+ hex << std::setfill('0') << std::setw(2) << std::hex << (int)pData[index] << ' ';
+ if (std::isprint(pData[index])) {
+ ascii << pData[index];
+ } else {
+ ascii << '.';
+ }
+ index++;
+ if (index % 16 == 0) {
+ ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
+ hex.str("");
+ ascii.str("");
+ }
+ }
+ if (index %16 != 0) {
+ while(index % 16 != 0) {
+ hex << " ";
+ index++;
+ }
+ ESP_LOGD(tag, "%s %s", hex.str().c_str(), ascii.str().c_str());
+ }
+ FreeRTOS::sleep(1000);
+}
+*/
+
+
+/**
+ * @brief Dump a representation of binary data to the console.
+ *
+ * @param [in] pData Pointer to the start of data to be logged.
+ * @param [in] length Length of the data (in bytes) to be logged.
+ * @return N/A.
+ */
+void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) {
+ char ascii[80];
+ char hex[80];
+ char tempBuf[80];
+ uint32_t lineNumber = 0;
+
+ ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ----------------");
+ strcpy(ascii, "");
+ strcpy(hex, "");
+ uint32_t index=0;
+ while(index < length) {
+ sprintf(tempBuf, "%.2x ", pData[index]);
+ strcat(hex, tempBuf);
+ if (isprint(pData[index])) {
+ sprintf(tempBuf, "%c", pData[index]);
+ } else {
+ sprintf(tempBuf, ".");
+ }
+ strcat(ascii, tempBuf);
+ index++;
+ if (index % 16 == 0) {
+ ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii);
+ strcpy(ascii, "");
+ strcpy(hex, "");
+ lineNumber++;
+ }
+ }
+ if (index %16 != 0) {
+ while(index % 16 != 0) {
+ strcat(hex, " ");
+ index++;
+ }
+ ESP_LOGD(LOG_TAG, "%.4x %s %s", lineNumber*16, hex, ascii);
+ }
+} // hexDump
+
+
+/**
+ * @brief Convert an IP address to string.
+ * @param ip The 4 byte IP address.
+ * @return A string representation of the IP address.
+ */
+std::string GeneralUtils::ipToString(uint8_t *ip) {
+ std::stringstream s;
+ s << (int)ip[0] << '.' << (int)ip[1] << '.' << (int)ip[2] << '.' << (int)ip[3];
+ return s.str();
+} // ipToString
+
+
+/**
+ * @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.
+ */
+const char* GeneralUtils::errorToString(esp_err_t errCode) {
+ switch(errCode) {
+ case ESP_OK:
+ return "OK";
+ case ESP_FAIL:
+ return "Fail";
+ case ESP_ERR_NO_MEM:
+ return "No memory";
+ case ESP_ERR_INVALID_ARG:
+ return "Invalid argument";
+ case ESP_ERR_INVALID_SIZE:
+ return "Invalid state";
+ case ESP_ERR_INVALID_STATE:
+ return "Invalid state";
+ case ESP_ERR_NOT_FOUND:
+ return "Not found";
+ case ESP_ERR_NOT_SUPPORTED:
+ return "Not supported";
+ case ESP_ERR_TIMEOUT:
+ return "Timeout";
+ case ESP_ERR_NVS_NOT_INITIALIZED:
+ return "ESP_ERR_NVS_NOT_INITIALIZED";
+ case ESP_ERR_NVS_NOT_FOUND:
+ return "ESP_ERR_NVS_NOT_FOUND";
+ case ESP_ERR_NVS_TYPE_MISMATCH:
+ return "ESP_ERR_NVS_TYPE_MISMATCH";
+ case ESP_ERR_NVS_READ_ONLY:
+ return "ESP_ERR_NVS_READ_ONLY";
+ case ESP_ERR_NVS_NOT_ENOUGH_SPACE:
+ return "ESP_ERR_NVS_NOT_ENOUGH_SPACE";
+ case ESP_ERR_NVS_INVALID_NAME:
+ return "ESP_ERR_NVS_INVALID_NAME";
+ case ESP_ERR_NVS_INVALID_HANDLE:
+ return "ESP_ERR_NVS_INVALID_HANDLE";
+ case ESP_ERR_NVS_REMOVE_FAILED:
+ return "ESP_ERR_NVS_REMOVE_FAILED";
+ case ESP_ERR_NVS_KEY_TOO_LONG:
+ return "ESP_ERR_NVS_KEY_TOO_LONG";
+ case ESP_ERR_NVS_PAGE_FULL:
+ return "ESP_ERR_NVS_PAGE_FULL";
+ case ESP_ERR_NVS_INVALID_STATE:
+ return "ESP_ERR_NVS_INVALID_STATE";
+ case ESP_ERR_NVS_INVALID_LENGTH:
+ return "ESP_ERR_NVS_INVALID_LENGTH";
+ case ESP_ERR_WIFI_NOT_INIT:
+ return "ESP_ERR_WIFI_NOT_INIT";
+ //case ESP_ERR_WIFI_NOT_START:
+ // return "ESP_ERR_WIFI_NOT_START";
+ case ESP_ERR_WIFI_IF:
+ return "ESP_ERR_WIFI_IF";
+ case ESP_ERR_WIFI_MODE:
+ return "ESP_ERR_WIFI_MODE";
+ case ESP_ERR_WIFI_STATE:
+ return "ESP_ERR_WIFI_STATE";
+ case ESP_ERR_WIFI_CONN:
+ return "ESP_ERR_WIFI_CONN";
+ case ESP_ERR_WIFI_NVS:
+ return "ESP_ERR_WIFI_NVS";
+ case ESP_ERR_WIFI_MAC:
+ return "ESP_ERR_WIFI_MAC";
+ case ESP_ERR_WIFI_SSID:
+ return "ESP_ERR_WIFI_SSID";
+ case ESP_ERR_WIFI_PASSWORD:
+ return "ESP_ERR_WIFI_PASSWORD";
+ case ESP_ERR_WIFI_TIMEOUT:
+ return "ESP_ERR_WIFI_TIMEOUT";
+ case ESP_ERR_WIFI_WAKE_FAIL:
+ return "ESP_ERR_WIFI_WAKE_FAIL";
+ }
+ return "Unknown ESP_ERR error";
+} // errorToString
+
+
+/**
+ * @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.
+ */
+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/sensor/patchedBLE/src/GeneralUtils.h b/sensor/patchedBLE/src/GeneralUtils.h
new file mode 100644
index 0000000..013953d
--- /dev/null
+++ b/sensor/patchedBLE/src/GeneralUtils.h
@@ -0,0 +1,34 @@
+/*
+ * GeneralUtils.h
+ *
+ * Created on: May 20, 2017
+ * Author: kolban
+ */
+
+#ifndef COMPONENTS_CPP_UTILS_GENERALUTILS_H_
+#define COMPONENTS_CPP_UTILS_GENERALUTILS_H_
+#include <stdint.h>
+#include <string>
+#include <esp_err.h>
+#include <algorithm>
+#include <vector>
+
+/**
+ * @brief General utilities.
+ */
+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 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_ */
diff --git a/sensor/patchedBLE/src/HIDKeyboardTypes.h b/sensor/patchedBLE/src/HIDKeyboardTypes.h
new file mode 100644
index 0000000..ef48a52
--- /dev/null
+++ b/sensor/patchedBLE/src/HIDKeyboardTypes.h
@@ -0,0 +1,402 @@
+/* Copyright (c) 2015 mbed.org, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Note: this file was pulled from different parts of the USBHID library, in mbed SDK
+ */
+
+#ifndef KEYBOARD_DEFS_H
+#define KEYBOARD_DEFS_H
+
+#define REPORT_ID_KEYBOARD 1
+#define REPORT_ID_VOLUME 3
+
+/* Modifiers */
+enum MODIFIER_KEY {
+ KEY_CTRL = 1,
+ KEY_SHIFT = 2,
+ KEY_ALT = 4,
+};
+
+
+enum MEDIA_KEY {
+ KEY_NEXT_TRACK, /*!< next Track Button */
+ KEY_PREVIOUS_TRACK, /*!< Previous track Button */
+ KEY_STOP, /*!< Stop Button */
+ KEY_PLAY_PAUSE, /*!< Play/Pause Button */
+ KEY_MUTE, /*!< Mute Button */
+ KEY_VOLUME_UP, /*!< Volume Up Button */
+ KEY_VOLUME_DOWN, /*!< Volume Down Button */
+};
+
+enum FUNCTION_KEY {
+ KEY_F1 = 128, /* F1 key */
+ KEY_F2, /* F2 key */
+ KEY_F3, /* F3 key */
+ KEY_F4, /* F4 key */
+ KEY_F5, /* F5 key */
+ KEY_F6, /* F6 key */
+ KEY_F7, /* F7 key */
+ KEY_F8, /* F8 key */
+ KEY_F9, /* F9 key */
+ KEY_F10, /* F10 key */
+ KEY_F11, /* F11 key */
+ KEY_F12, /* F12 key */
+
+ KEY_PRINT_SCREEN, /* Print Screen key */
+ KEY_SCROLL_LOCK, /* Scroll lock */
+ KEY_CAPS_LOCK, /* caps lock */
+ KEY_NUM_LOCK, /* num lock */
+ KEY_INSERT, /* Insert key */
+ KEY_HOME, /* Home key */
+ KEY_PAGE_UP, /* Page Up key */
+ KEY_PAGE_DOWN, /* Page Down key */
+
+ RIGHT_ARROW, /* Right arrow */
+ LEFT_ARROW, /* Left arrow */
+ DOWN_ARROW, /* Down arrow */
+ UP_ARROW, /* Up arrow */
+};
+
+typedef struct {
+ unsigned char usage;
+ unsigned char modifier;
+} KEYMAP;
+
+#ifdef US_KEYBOARD
+/* US keyboard (as HID standard) */
+#define KEYMAP_SIZE (152)
+const KEYMAP keymap[KEYMAP_SIZE] = {
+ {0, 0}, /* NUL */
+ {0, 0}, /* SOH */
+ {0, 0}, /* STX */
+ {0, 0}, /* ETX */
+ {0, 0}, /* EOT */
+ {0, 0}, /* ENQ */
+ {0, 0}, /* ACK */
+ {0, 0}, /* BEL */
+ {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
+ {0x2b, 0}, /* TAB */ /* Keyboard Tab */
+ {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
+ {0, 0}, /* VT */
+ {0, 0}, /* FF */
+ {0, 0}, /* CR */
+ {0, 0}, /* SO */
+ {0, 0}, /* SI */
+ {0, 0}, /* DEL */
+ {0, 0}, /* DC1 */
+ {0, 0}, /* DC2 */
+ {0, 0}, /* DC3 */
+ {0, 0}, /* DC4 */
+ {0, 0}, /* NAK */
+ {0, 0}, /* SYN */
+ {0, 0}, /* ETB */
+ {0, 0}, /* CAN */
+ {0, 0}, /* EM */
+ {0, 0}, /* SUB */
+ {0, 0}, /* ESC */
+ {0, 0}, /* FS */
+ {0, 0}, /* GS */
+ {0, 0}, /* RS */
+ {0, 0}, /* US */
+ {0x2c, 0}, /* */
+ {0x1e, KEY_SHIFT}, /* ! */
+ {0x34, KEY_SHIFT}, /* " */
+ {0x20, KEY_SHIFT}, /* # */
+ {0x21, KEY_SHIFT}, /* $ */
+ {0x22, KEY_SHIFT}, /* % */
+ {0x24, KEY_SHIFT}, /* & */
+ {0x34, 0}, /* ' */
+ {0x26, KEY_SHIFT}, /* ( */
+ {0x27, KEY_SHIFT}, /* ) */
+ {0x25, KEY_SHIFT}, /* * */
+ {0x2e, KEY_SHIFT}, /* + */
+ {0x36, 0}, /* , */
+ {0x2d, 0}, /* - */
+ {0x37, 0}, /* . */
+ {0x38, 0}, /* / */
+ {0x27, 0}, /* 0 */
+ {0x1e, 0}, /* 1 */
+ {0x1f, 0}, /* 2 */
+ {0x20, 0}, /* 3 */
+ {0x21, 0}, /* 4 */
+ {0x22, 0}, /* 5 */
+ {0x23, 0}, /* 6 */
+ {0x24, 0}, /* 7 */
+ {0x25, 0}, /* 8 */
+ {0x26, 0}, /* 9 */
+ {0x33, KEY_SHIFT}, /* : */
+ {0x33, 0}, /* ; */
+ {0x36, KEY_SHIFT}, /* < */
+ {0x2e, 0}, /* = */
+ {0x37, KEY_SHIFT}, /* > */
+ {0x38, KEY_SHIFT}, /* ? */
+ {0x1f, KEY_SHIFT}, /* @ */
+ {0x04, KEY_SHIFT}, /* A */
+ {0x05, KEY_SHIFT}, /* B */
+ {0x06, KEY_SHIFT}, /* C */
+ {0x07, KEY_SHIFT}, /* D */
+ {0x08, KEY_SHIFT}, /* E */
+ {0x09, KEY_SHIFT}, /* F */
+ {0x0a, KEY_SHIFT}, /* G */
+ {0x0b, KEY_SHIFT}, /* H */
+ {0x0c, KEY_SHIFT}, /* I */
+ {0x0d, KEY_SHIFT}, /* J */
+ {0x0e, KEY_SHIFT}, /* K */
+ {0x0f, KEY_SHIFT}, /* L */
+ {0x10, KEY_SHIFT}, /* M */
+ {0x11, KEY_SHIFT}, /* N */
+ {0x12, KEY_SHIFT}, /* O */
+ {0x13, KEY_SHIFT}, /* P */
+ {0x14, KEY_SHIFT}, /* Q */
+ {0x15, KEY_SHIFT}, /* R */
+ {0x16, KEY_SHIFT}, /* S */
+ {0x17, KEY_SHIFT}, /* T */
+ {0x18, KEY_SHIFT}, /* U */
+ {0x19, KEY_SHIFT}, /* V */
+ {0x1a, KEY_SHIFT}, /* W */
+ {0x1b, KEY_SHIFT}, /* X */
+ {0x1c, KEY_SHIFT}, /* Y */
+ {0x1d, KEY_SHIFT}, /* Z */
+ {0x2f, 0}, /* [ */
+ {0x31, 0}, /* \ */
+ {0x30, 0}, /* ] */
+ {0x23, KEY_SHIFT}, /* ^ */
+ {0x2d, KEY_SHIFT}, /* _ */
+ {0x35, 0}, /* ` */
+ {0x04, 0}, /* a */
+ {0x05, 0}, /* b */
+ {0x06, 0}, /* c */
+ {0x07, 0}, /* d */
+ {0x08, 0}, /* e */
+ {0x09, 0}, /* f */
+ {0x0a, 0}, /* g */
+ {0x0b, 0}, /* h */
+ {0x0c, 0}, /* i */
+ {0x0d, 0}, /* j */
+ {0x0e, 0}, /* k */
+ {0x0f, 0}, /* l */
+ {0x10, 0}, /* m */
+ {0x11, 0}, /* n */
+ {0x12, 0}, /* o */
+ {0x13, 0}, /* p */
+ {0x14, 0}, /* q */
+ {0x15, 0}, /* r */
+ {0x16, 0}, /* s */
+ {0x17, 0}, /* t */
+ {0x18, 0}, /* u */
+ {0x19, 0}, /* v */
+ {0x1a, 0}, /* w */
+ {0x1b, 0}, /* x */
+ {0x1c, 0}, /* y */
+ {0x1d, 0}, /* z */
+ {0x2f, KEY_SHIFT}, /* { */
+ {0x31, KEY_SHIFT}, /* | */
+ {0x30, KEY_SHIFT}, /* } */
+ {0x35, KEY_SHIFT}, /* ~ */
+ {0,0}, /* DEL */
+
+ {0x3a, 0}, /* F1 */
+ {0x3b, 0}, /* F2 */
+ {0x3c, 0}, /* F3 */
+ {0x3d, 0}, /* F4 */
+ {0x3e, 0}, /* F5 */
+ {0x3f, 0}, /* F6 */
+ {0x40, 0}, /* F7 */
+ {0x41, 0}, /* F8 */
+ {0x42, 0}, /* F9 */
+ {0x43, 0}, /* F10 */
+ {0x44, 0}, /* F11 */
+ {0x45, 0}, /* F12 */
+
+ {0x46, 0}, /* PRINT_SCREEN */
+ {0x47, 0}, /* SCROLL_LOCK */
+ {0x39, 0}, /* CAPS_LOCK */
+ {0x53, 0}, /* NUM_LOCK */
+ {0x49, 0}, /* INSERT */
+ {0x4a, 0}, /* HOME */
+ {0x4b, 0}, /* PAGE_UP */
+ {0x4e, 0}, /* PAGE_DOWN */
+
+ {0x4f, 0}, /* RIGHT_ARROW */
+ {0x50, 0}, /* LEFT_ARROW */
+ {0x51, 0}, /* DOWN_ARROW */
+ {0x52, 0}, /* UP_ARROW */
+};
+
+#else
+/* UK keyboard */
+#define KEYMAP_SIZE (152)
+const KEYMAP keymap[KEYMAP_SIZE] = {
+ {0, 0}, /* NUL */
+ {0, 0}, /* SOH */
+ {0, 0}, /* STX */
+ {0, 0}, /* ETX */
+ {0, 0}, /* EOT */
+ {0, 0}, /* ENQ */
+ {0, 0}, /* ACK */
+ {0, 0}, /* BEL */
+ {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
+ {0x2b, 0}, /* TAB */ /* Keyboard Tab */
+ {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
+ {0, 0}, /* VT */
+ {0, 0}, /* FF */
+ {0, 0}, /* CR */
+ {0, 0}, /* SO */
+ {0, 0}, /* SI */
+ {0, 0}, /* DEL */
+ {0, 0}, /* DC1 */
+ {0, 0}, /* DC2 */
+ {0, 0}, /* DC3 */
+ {0, 0}, /* DC4 */
+ {0, 0}, /* NAK */
+ {0, 0}, /* SYN */
+ {0, 0}, /* ETB */
+ {0, 0}, /* CAN */
+ {0, 0}, /* EM */
+ {0, 0}, /* SUB */
+ {0, 0}, /* ESC */
+ {0, 0}, /* FS */
+ {0, 0}, /* GS */
+ {0, 0}, /* RS */
+ {0, 0}, /* US */
+ {0x2c, 0}, /* */
+ {0x1e, KEY_SHIFT}, /* ! */
+ {0x1f, KEY_SHIFT}, /* " */
+ {0x32, 0}, /* # */
+ {0x21, KEY_SHIFT}, /* $ */
+ {0x22, KEY_SHIFT}, /* % */
+ {0x24, KEY_SHIFT}, /* & */
+ {0x34, 0}, /* ' */
+ {0x26, KEY_SHIFT}, /* ( */
+ {0x27, KEY_SHIFT}, /* ) */
+ {0x25, KEY_SHIFT}, /* * */
+ {0x2e, KEY_SHIFT}, /* + */
+ {0x36, 0}, /* , */
+ {0x2d, 0}, /* - */
+ {0x37, 0}, /* . */
+ {0x38, 0}, /* / */
+ {0x27, 0}, /* 0 */
+ {0x1e, 0}, /* 1 */
+ {0x1f, 0}, /* 2 */
+ {0x20, 0}, /* 3 */
+ {0x21, 0}, /* 4 */
+ {0x22, 0}, /* 5 */
+ {0x23, 0}, /* 6 */
+ {0x24, 0}, /* 7 */
+ {0x25, 0}, /* 8 */
+ {0x26, 0}, /* 9 */
+ {0x33, KEY_SHIFT}, /* : */
+ {0x33, 0}, /* ; */
+ {0x36, KEY_SHIFT}, /* < */
+ {0x2e, 0}, /* = */
+ {0x37, KEY_SHIFT}, /* > */
+ {0x38, KEY_SHIFT}, /* ? */
+ {0x34, KEY_SHIFT}, /* @ */
+ {0x04, KEY_SHIFT}, /* A */
+ {0x05, KEY_SHIFT}, /* B */
+ {0x06, KEY_SHIFT}, /* C */
+ {0x07, KEY_SHIFT}, /* D */
+ {0x08, KEY_SHIFT}, /* E */
+ {0x09, KEY_SHIFT}, /* F */
+ {0x0a, KEY_SHIFT}, /* G */
+ {0x0b, KEY_SHIFT}, /* H */
+ {0x0c, KEY_SHIFT}, /* I */
+ {0x0d, KEY_SHIFT}, /* J */
+ {0x0e, KEY_SHIFT}, /* K */
+ {0x0f, KEY_SHIFT}, /* L */
+ {0x10, KEY_SHIFT}, /* M */
+ {0x11, KEY_SHIFT}, /* N */
+ {0x12, KEY_SHIFT}, /* O */
+ {0x13, KEY_SHIFT}, /* P */
+ {0x14, KEY_SHIFT}, /* Q */
+ {0x15, KEY_SHIFT}, /* R */
+ {0x16, KEY_SHIFT}, /* S */
+ {0x17, KEY_SHIFT}, /* T */
+ {0x18, KEY_SHIFT}, /* U */
+ {0x19, KEY_SHIFT}, /* V */
+ {0x1a, KEY_SHIFT}, /* W */
+ {0x1b, KEY_SHIFT}, /* X */
+ {0x1c, KEY_SHIFT}, /* Y */
+ {0x1d, KEY_SHIFT}, /* Z */
+ {0x2f, 0}, /* [ */
+ {0x64, 0}, /* \ */
+ {0x30, 0}, /* ] */
+ {0x23, KEY_SHIFT}, /* ^ */
+ {0x2d, KEY_SHIFT}, /* _ */
+ {0x35, 0}, /* ` */
+ {0x04, 0}, /* a */
+ {0x05, 0}, /* b */
+ {0x06, 0}, /* c */
+ {0x07, 0}, /* d */
+ {0x08, 0}, /* e */
+ {0x09, 0}, /* f */
+ {0x0a, 0}, /* g */
+ {0x0b, 0}, /* h */
+ {0x0c, 0}, /* i */
+ {0x0d, 0}, /* j */
+ {0x0e, 0}, /* k */
+ {0x0f, 0}, /* l */
+ {0x10, 0}, /* m */
+ {0x11, 0}, /* n */
+ {0x12, 0}, /* o */
+ {0x13, 0}, /* p */
+ {0x14, 0}, /* q */
+ {0x15, 0}, /* r */
+ {0x16, 0}, /* s */
+ {0x17, 0}, /* t */
+ {0x18, 0}, /* u */
+ {0x19, 0}, /* v */
+ {0x1a, 0}, /* w */
+ {0x1b, 0}, /* x */
+ {0x1c, 0}, /* y */
+ {0x1d, 0}, /* z */
+ {0x2f, KEY_SHIFT}, /* { */
+ {0x64, KEY_SHIFT}, /* | */
+ {0x30, KEY_SHIFT}, /* } */
+ {0x32, KEY_SHIFT}, /* ~ */
+ {0,0}, /* DEL */
+
+ {0x3a, 0}, /* F1 */
+ {0x3b, 0}, /* F2 */
+ {0x3c, 0}, /* F3 */
+ {0x3d, 0}, /* F4 */
+ {0x3e, 0}, /* F5 */
+ {0x3f, 0}, /* F6 */
+ {0x40, 0}, /* F7 */
+ {0x41, 0}, /* F8 */
+ {0x42, 0}, /* F9 */
+ {0x43, 0}, /* F10 */
+ {0x44, 0}, /* F11 */
+ {0x45, 0}, /* F12 */
+
+ {0x46, 0}, /* PRINT_SCREEN */
+ {0x47, 0}, /* SCROLL_LOCK */
+ {0x39, 0}, /* CAPS_LOCK */
+ {0x53, 0}, /* NUM_LOCK */
+ {0x49, 0}, /* INSERT */
+ {0x4a, 0}, /* HOME */
+ {0x4b, 0}, /* PAGE_UP */
+ {0x4e, 0}, /* PAGE_DOWN */
+
+ {0x4f, 0}, /* RIGHT_ARROW */
+ {0x50, 0}, /* LEFT_ARROW */
+ {0x51, 0}, /* DOWN_ARROW */
+ {0x52, 0}, /* UP_ARROW */
+};
+#endif
+
+#endif
diff --git a/sensor/patchedBLE/src/HIDTypes.h b/sensor/patchedBLE/src/HIDTypes.h
new file mode 100644
index 0000000..726b84b
--- /dev/null
+++ b/sensor/patchedBLE/src/HIDTypes.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or
+* substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef USBCLASS_HID_TYPES
+#define USBCLASS_HID_TYPES
+
+#include <stdint.h>
+
+/* */
+#define HID_VERSION_1_11 (0x0111)
+
+/* HID Class */
+#define HID_CLASS (3)
+#define HID_SUBCLASS_NONE (0)
+#define HID_PROTOCOL_NONE (0)
+
+/* Descriptors */
+#define HID_DESCRIPTOR (33)
+#define HID_DESCRIPTOR_LENGTH (0x09)
+#define REPORT_DESCRIPTOR (34)
+
+/* Class requests */
+#define GET_REPORT (0x1)
+#define GET_IDLE (0x2)
+#define SET_REPORT (0x9)
+#define SET_IDLE (0xa)
+
+/* HID Class Report Descriptor */
+/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */
+/* of data as per HID Class standard */
+
+/* Main items */
+#ifdef ARDUINO_ARCH_ESP32
+#define HIDINPUT(size) (0x80 | size)
+#define HIDOUTPUT(size) (0x90 | size)
+#else
+#define INPUT(size) (0x80 | size)
+#define OUTPUT(size) (0x90 | size)
+#endif
+#define FEATURE(size) (0xb0 | size)
+#define COLLECTION(size) (0xa0 | size)
+#define END_COLLECTION(size) (0xc0 | size)
+
+/* Global items */
+#define USAGE_PAGE(size) (0x04 | size)
+#define LOGICAL_MINIMUM(size) (0x14 | size)
+#define LOGICAL_MAXIMUM(size) (0x24 | size)
+#define PHYSICAL_MINIMUM(size) (0x34 | size)
+#define PHYSICAL_MAXIMUM(size) (0x44 | size)
+#define UNIT_EXPONENT(size) (0x54 | size)
+#define UNIT(size) (0x64 | size)
+#define REPORT_SIZE(size) (0x74 | size) //bits
+#define REPORT_ID(size) (0x84 | size)
+#define REPORT_COUNT(size) (0x94 | size) //bytes
+#define PUSH(size) (0xa4 | size)
+#define POP(size) (0xb4 | size)
+
+/* Local items */
+#define USAGE(size) (0x08 | size)
+#define USAGE_MINIMUM(size) (0x18 | size)
+#define USAGE_MAXIMUM(size) (0x28 | size)
+#define DESIGNATOR_INDEX(size) (0x38 | size)
+#define DESIGNATOR_MINIMUM(size) (0x48 | size)
+#define DESIGNATOR_MAXIMUM(size) (0x58 | size)
+#define STRING_INDEX(size) (0x78 | size)
+#define STRING_MINIMUM(size) (0x88 | size)
+#define STRING_MAXIMUM(size) (0x98 | size)
+#define DELIMITER(size) (0xa8 | size)
+
+/* HID Report */
+/* Where report IDs are used the first byte of 'data' will be the */
+/* report ID and 'length' will include this report ID byte. */
+
+#define MAX_HID_REPORT_SIZE (64)
+
+typedef struct {
+ uint32_t length;
+ uint8_t data[MAX_HID_REPORT_SIZE];
+} HID_REPORT;
+
+#endif