publish libraries to avoid users to have to download them one by one
This commit is contained in:
1 parent
93c075e880
commit
f0546fab8c
396 files changed
+536928
No files matched your search
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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_ */
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* BLEAdvertising.cpp
|
||||
*
|
||||
* This class encapsulates advertising a BLE Server.
|
||||
* Created on: Jun 21, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include "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;
|
||||
} // 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
|
||||
|
||||
|
||||
/**
|
||||
* @brief Start advertising.
|
||||
* Start advertising.
|
||||
* @return N/A.
|
||||
*/
|
||||
void BLEAdvertising::start() {
|
||||
ESP_LOGD(LOG_TAG, ">> start");
|
||||
|
||||
// We have a vector of service UUIDs that we wish to advertise. In order to use the
|
||||
// ESP-IDF framework, these must be supplied in a contiguous array of their 128bit (16 byte)
|
||||
// representations. If we have 1 or more services to advertise then we allocate enough
|
||||
// storage to host them and then copy them in one at a time into the contiguous storage.
|
||||
int numServices = m_serviceUUIDs.size();
|
||||
if (numServices > 0) {
|
||||
m_advData.service_uuid_len = 16*numServices;
|
||||
m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len];
|
||||
uint8_t* p = m_advData.p_service_uuid;
|
||||
for (int i=0; i<numServices; i++) {
|
||||
ESP_LOGD(LOG_TAG, "- advertising service: %s", m_serviceUUIDs[i].toString().c_str());
|
||||
BLEUUID serviceUUID128 = m_serviceUUIDs[i].to128();
|
||||
memcpy(p, serviceUUID128.getNative()->uuid.uuid128, 16);
|
||||
p+=16;
|
||||
}
|
||||
} else {
|
||||
m_advData.service_uuid_len = 0;
|
||||
ESP_LOGD(LOG_TAG, "- no services advertised");
|
||||
}
|
||||
|
||||
|
||||
// Set the configuration for advertising.
|
||||
m_advData.set_scan_rsp = false;
|
||||
esp_err_t errRc = ::esp_ble_gap_config_adv_data(&m_advData);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "<< esp_ble_gap_config_adv_data: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_advData.set_scan_rsp = 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
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 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);
|
||||
private:
|
||||
esp_ble_adv_data_t m_advData;
|
||||
esp_ble_adv_params_t m_advParams;
|
||||
std::vector<BLEUUID> m_serviceUUIDs;
|
||||
};
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */
|
||||
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* 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 "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>(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
void BLECharacteristic::handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param) {
|
||||
switch(event) {
|
||||
// Events handled:
|
||||
// ESP_GATTS_ADD_CHAR_EVT
|
||||
// ESP_GATTS_WRITE_EVT
|
||||
// ESP_GATTS_READ_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
|
||||
//
|
||||
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: {
|
||||
ESP_LOGD(LOG_TAG, "- Testing: 0x%.2x == 0x%.2x", param->read.handle, m_handle);
|
||||
if (param->read.handle == m_handle) {
|
||||
|
||||
if (m_pCallbacks != nullptr) {
|
||||
m_pCallbacks->onRead(this); // Invoke the read callback.
|
||||
}
|
||||
|
||||
// Here's an interesting thing. The read request has the option of saying whether we need a response
|
||||
// or not. What would it "mean" to receive a read request and NOT send a response back? That feels like
|
||||
// a very strange read.
|
||||
//
|
||||
// We have to handle the case where the data we wish to send back to the client is greater than the maximum
|
||||
// packet size of 22 bytes. In this case, we become responsible for chunking the data into uints of 22 bytes.
|
||||
// The apparent algorithm is as follows.
|
||||
// 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.
|
||||
//
|
||||
if (param->read.need_rsp) {
|
||||
ESP_LOGD(LOG_TAG, "Sending a response (esp_ble_gatts_send_response)");
|
||||
esp_gatt_rsp_t rsp;
|
||||
std::string value = m_value.getValue();
|
||||
if (param->read.is_long) {
|
||||
if (value.length() - m_value.getReadOffset() < 22) {
|
||||
// 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 = 22;
|
||||
rsp.attr_value.offset = m_value.getReadOffset();
|
||||
memcpy(rsp.attr_value.value, value.data() + rsp.attr_value.offset, rsp.attr_value.len);
|
||||
m_value.setReadOffset(rsp.attr_value.offset + 22);
|
||||
}
|
||||
} else {
|
||||
if (value.length() > 21) {
|
||||
// Too big for a single shot entry.
|
||||
m_value.setReadOffset(22);
|
||||
rsp.attr_value.len = 22;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
} // 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() > 20) {
|
||||
ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
|
||||
}
|
||||
|
||||
size_t length = m_value.getValue().length();
|
||||
if (length > 20) {
|
||||
length = 20;
|
||||
}
|
||||
|
||||
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() > 20) {
|
||||
ESP_LOGD(LOG_TAG, "- Truncating to 20 bytes (maximum notify size)");
|
||||
}
|
||||
|
||||
size_t length = m_value.getValue().length();
|
||||
if (length > 20) {
|
||||
length = 20;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 "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 setWriteProperty(bool value);
|
||||
void setWriteNoResponseProperty(bool value);
|
||||
std::string toString();
|
||||
uint16_t getHandle();
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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_ */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* BLEDevice.cpp
|
||||
*
|
||||
* Created on: Mar 22, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_log.h>
|
||||
#include <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 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");
|
||||
|
||||
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
|
||||
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");
|
||||
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.
|
||||
m_isConnected = false;
|
||||
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();
|
||||
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.
|
||||
*/
|
||||
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");
|
||||
return nullptr;
|
||||
} // 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");
|
||||
m_servicesMap.clear();
|
||||
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;
|
||||
}
|
||||
m_semaphoreSearchCmplEvt.wait("getServices");
|
||||
m_haveServices = true; // Remember that we now have services.
|
||||
ESP_LOGD(LOG_TAG, "<< getServices");
|
||||
return &m_servicesMap;
|
||||
} // getServices
|
||||
|
||||
/**
|
||||
* @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 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
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "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);
|
||||
void disconnect();
|
||||
BLEAddress getPeerAddress();
|
||||
int getRssi();
|
||||
std::map<std::string, BLERemoteService*>* getServices();
|
||||
BLERemoteService* getService(const char* uuid);
|
||||
BLERemoteService* getService(BLEUUID uuid);
|
||||
void handleGAPEvent(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t* param);
|
||||
bool isConnected();
|
||||
void setClientCallbacks(BLEClientCallbacks *pClientCallbacks);
|
||||
std::string toString();
|
||||
|
||||
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");
|
||||
uint16_t m_conn_id;
|
||||
// int m_deviceType;
|
||||
esp_gatt_if_t m_gattc_if;
|
||||
bool m_isConnected;
|
||||
|
||||
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;
|
||||
bool m_haveServices; // Have we previously obtain the set of services.
|
||||
}; // class BLEDevice
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callbacks associated with a %BLE client.
|
||||
*/
|
||||
class BLEClientCallbacks {
|
||||
public:
|
||||
virtual ~BLEClientCallbacks() {};
|
||||
virtual void onConnect(BLEClient *pClient) = 0;
|
||||
};
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDEVICE_H_ */
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* 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;
|
||||
m_value.attr_max_len = ESP_GATT_MAX_ATTR_LEN;
|
||||
m_handle = NULL_HANDLE;
|
||||
m_pCharacteristic = nullptr; // No initial characteristic.
|
||||
m_pCallback = nullptr; // No initial callback.
|
||||
|
||||
} // BLEDescriptor
|
||||
|
||||
|
||||
/**
|
||||
* @brief BLEDescriptor destructor.
|
||||
*/
|
||||
BLEDescriptor::~BLEDescriptor() {
|
||||
free(m_value.attr_value);
|
||||
} // ~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.char_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
|
||||
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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();
|
||||
size_t getLength();
|
||||
BLEUUID getUUID();
|
||||
uint8_t* getValue();
|
||||
void handleGATTServerEvent(
|
||||
esp_gatts_cb_event_t event,
|
||||
esp_gatt_if_t gatts_if,
|
||||
esp_ble_gatts_cb_param_t* param);
|
||||
void setCallbacks(BLEDescriptorCallbacks* pCallbacks);
|
||||
void setValue(uint8_t* data, size_t size);
|
||||
void setValue(std::string value);
|
||||
std::string toString();
|
||||
|
||||
private:
|
||||
friend class BLEDescriptorMap;
|
||||
friend class BLECharacteristic;
|
||||
BLEUUID m_bleUUID;
|
||||
esp_attr_value_t m_value;
|
||||
uint16_t m_handle;
|
||||
BLECharacteristic* m_pCharacteristic;
|
||||
BLEDescriptorCallbacks* m_pCallback;
|
||||
void executeCreate(BLECharacteristic* pCharacteristic);
|
||||
void setHandle(uint16_t handle);
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
};
|
||||
|
||||
/**
|
||||
* @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_ */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 <bt.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_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"
|
||||
#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;
|
||||
/**
|
||||
* @brief Create a new instance of a client.
|
||||
* @return A new instance of the client.
|
||||
*/
|
||||
BLEClient* BLEDevice::createClient() {
|
||||
m_pClient = new BLEClient();
|
||||
return m_pClient;
|
||||
} // createClient
|
||||
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of a server.
|
||||
* @return A new instance of the server.
|
||||
*/
|
||||
BLEServer* BLEDevice::createServer() {
|
||||
ESP_LOGD(LOG_TAG, ">> createServer");
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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.
|
||||
*/
|
||||
void BLEDevice::gapEventHandler(
|
||||
esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param) {
|
||||
|
||||
BLEUtils::dumpGapEvent(event, param);
|
||||
|
||||
switch(event) {
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT: {
|
||||
esp_err_t errRc = ::esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gap_security_rsp: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
}
|
||||
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);
|
||||
}
|
||||
} // gapEventHandler
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve the Scan object that we use for scanning.
|
||||
* @return The scanning object reference. This is a singleton object. The caller should not
|
||||
* try and release/delete it.
|
||||
*/
|
||||
BLEScan* BLEDevice::getScan() {
|
||||
//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 Initialize the %BLE environment.
|
||||
* @param deviceName The device name of the device.
|
||||
*/
|
||||
void BLEDevice::init(std::string deviceName) {
|
||||
if(!initialized){
|
||||
initialized = true;
|
||||
esp_err_t errRc = ::nvs_flash_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "nvs_flash_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
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
|
||||
errRc = esp_bluedroid_init();
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_bluedroid_init: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
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
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 <bt.h>
|
||||
|
||||
#include "BLEServer.h"
|
||||
#include "BLEClient.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEScan.h"
|
||||
#include "BLEAddress.h"
|
||||
/**
|
||||
* @brief %BLE functions.
|
||||
*/
|
||||
class BLEDevice {
|
||||
public:
|
||||
|
||||
static BLEClient* createClient();
|
||||
static BLEServer* createServer();
|
||||
static void dumpDevices();
|
||||
static BLEScan* getScan();
|
||||
static void init(std::string deviceName);
|
||||
|
||||
private:
|
||||
static BLEServer *m_pServer;
|
||||
static BLEScan *m_pScan;
|
||||
static BLEClient *m_pClient;
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
static void setPower(esp_power_level_t powerLevel);
|
||||
|
||||
}; // class BLE
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
#endif /* MAIN_BLEDevice_H_ */
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* 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 "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());
|
||||
|
||||
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());
|
||||
|
||||
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 */
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 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) {
|
||||
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;
|
||||
return "";
|
||||
} // 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());
|
||||
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 */
|
||||
@@ -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();
|
||||
BLEUUID getUUID();
|
||||
std::string readValue(void);
|
||||
uint8_t readUInt8(void);
|
||||
uint16_t readUInt16(void);
|
||||
uint32_t readUInt32(void);
|
||||
std::string toString(void);
|
||||
//void registerForNotify(void (*notifyCallback)(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify));
|
||||
void writeValue(uint8_t* data, size_t length, bool response = false);
|
||||
void writeValue(std::string newValue, bool response = false);
|
||||
void writeValue(uint8_t newValue, bool response = false);
|
||||
|
||||
|
||||
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_ */
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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.first->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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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.second == v) {
|
||||
return myPair.first;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
} // 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.
|
||||
/*
|
||||
m_semaphoreGetCharEvt.take("getCharacteristics");
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gattc_get_characteristic(
|
||||
m_pClient->getGattcIf(),
|
||||
m_pClient->getConnId(),
|
||||
&m_srvcId,
|
||||
nullptr);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_characteristic: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
|
||||
return;
|
||||
}
|
||||
|
||||
m_semaphoreGetCharEvt.wait("getCharacteristics"); // Wait for the characteristics to become available.
|
||||
|
||||
m_haveCharacteristics = true; // Remember that we have received the characteristics.
|
||||
*/
|
||||
//ESP_LOGE(LOG_TAG, "!!! NOT IMPLEMENTED !!!");
|
||||
//ESP_LOGD(LOG_TAG, "--- test code ---");
|
||||
/*
|
||||
uint16_t count;
|
||||
esp_gatt_status_t status = ::esp_ble_gattc_get_attr_count(
|
||||
getClient()->getGattcIf(),
|
||||
getClient()->getConnId(),
|
||||
ESP_GATT_DB_CHARACTERISTIC,
|
||||
m_startHandle,
|
||||
m_endHandle,
|
||||
0, // Characteristic handle ... only used for ESP_GATT_DB_DESCRIPTOR
|
||||
&count
|
||||
);
|
||||
if (status != ESP_GATT_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_attr_count: %s", BLEUtils::gattStatusToString(status).c_str());
|
||||
} else {
|
||||
ESP_LOGD(LOG_TAG, "Number of characteristics associated with service is %d", count);
|
||||
}
|
||||
|
||||
count = 1;
|
||||
esp_gattc_service_elem_t srvcElem;
|
||||
status = ::esp_ble_gattc_get_service(
|
||||
getClient()->getGattcIf(),
|
||||
getClient()->getConnId(),
|
||||
&m_srvcId.uuid, // UUID of service
|
||||
&srvcElem, // Records
|
||||
&count, // records retrieved
|
||||
0 // offset
|
||||
);
|
||||
if (status != ESP_GATT_OK) {
|
||||
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_service: %s", BLEUtils::gattStatusToString(status).c_str());
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(LOG_TAG, "%s", BLEUtils::gattcServiceElementToString(&srvcElem).c_str());
|
||||
}
|
||||
*/
|
||||
|
||||
uint16_t offset = 0;
|
||||
esp_gattc_char_elem_t result;
|
||||
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<BLERemoteCharacteristic*, std::string>(pNewRemoteCharacteristic, pNewRemoteCharacteristic->getUUID().toString()));
|
||||
|
||||
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<BLERemoteCharacteristic*, std::string>* 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
|
||||
|
||||
|
||||
BLEClient* BLERemoteService::getClient() {
|
||||
return m_pClient;
|
||||
}
|
||||
|
||||
|
||||
uint16_t BLERemoteService::getEndHandle() {
|
||||
return m_endHandle;
|
||||
}
|
||||
|
||||
|
||||
esp_gatt_id_t* BLERemoteService::getSrvcId() {
|
||||
return &m_srvcId;
|
||||
}
|
||||
|
||||
|
||||
uint16_t BLERemoteService::getStartHandle() {
|
||||
return m_startHandle;
|
||||
}
|
||||
|
||||
uint16_t BLERemoteService::getHandle() {
|
||||
ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str());
|
||||
//ESP_LOGE(LOG_TAG, "!!! getHandle: NOT IMPLEMENTED !!!");
|
||||
ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle());
|
||||
return getStartHandle();
|
||||
}
|
||||
|
||||
|
||||
BLEUUID BLERemoteService::getUUID() {
|
||||
return m_uuid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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.first;
|
||||
m_characteristicMap.erase(myPair.first);
|
||||
}
|
||||
m_characteristicMap.clear(); // Clear the map
|
||||
} // removeCharacteristics
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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.first->toString();
|
||||
// myPair.second is the value
|
||||
}
|
||||
return ss.str();
|
||||
} // toString
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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);
|
||||
BLERemoteCharacteristic* getCharacteristic(BLEUUID uuid);
|
||||
BLERemoteCharacteristic* getCharacteristic(uint16_t uuid);
|
||||
std::map<BLERemoteCharacteristic*, std::string>* getCharacteristics();
|
||||
void getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* ptr);
|
||||
|
||||
BLEClient* getClient(void);
|
||||
uint16_t getHandle();
|
||||
BLEUUID getUUID(void);
|
||||
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);
|
||||
esp_gatt_id_t* getSrvcId(void);
|
||||
uint16_t getStartHandle();
|
||||
uint16_t getEndHandle();
|
||||
void gattClientEventHandler(
|
||||
esp_gattc_cb_event_t event,
|
||||
esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t* evtParam);
|
||||
void removeCharacteristics();
|
||||
|
||||
// Properties
|
||||
|
||||
// We maintain a map of characteristics owned by this service keyed by a string representation of the UUID.
|
||||
std::map<BLERemoteCharacteristic *, std::string> m_characteristicMap;
|
||||
// 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;
|
||||
uint16_t m_startHandle;
|
||||
uint16_t m_endHandle;
|
||||
}; // BLERemoteService
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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;
|
||||
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;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
void BLEScan::setAdvertisedDeviceCallbacks(BLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks) {
|
||||
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.
|
||||
* @return N/A.
|
||||
*/
|
||||
BLEScanResults BLEScan::start(uint32_t duration) {
|
||||
ESP_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
|
||||
|
||||
m_semaphoreScanEnd.take(std::string("start"));
|
||||
|
||||
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 m_scanResults;
|
||||
}
|
||||
|
||||
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 m_scanResults;
|
||||
}
|
||||
|
||||
m_stopped = false;
|
||||
|
||||
m_semaphoreScanEnd.wait("start"); // Wait for the semaphore to release.
|
||||
|
||||
ESP_LOGD(LOG_TAG, "<< start()");
|
||||
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 */
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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);
|
||||
void setInterval(uint16_t intervalMSecs);
|
||||
void setWindow(uint16_t windowMSecs);
|
||||
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;
|
||||
}; // BLEScan
|
||||
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
#endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* BLEServer.cpp
|
||||
*
|
||||
* Created on: Apr 16, 2017
|
||||
* Author: kolban
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if defined(CONFIG_BT_ENABLED)
|
||||
#include <esp_log.h>
|
||||
#include <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 <gatt_api.h>
|
||||
#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.
|
||||
* @return A reference to the new service object.
|
||||
*/
|
||||
BLEService* BLEServer::createService(BLEUUID uuid, uint32_t numHandles) {
|
||||
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_LOGE(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);
|
||||
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_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++;
|
||||
break;
|
||||
} // ESP_GATTS_CONNECT_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_REG_EVT
|
||||
// reg:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t app_id
|
||||
case ESP_GATTS_REG_EVT: {
|
||||
m_gatts_if = gatts_if;
|
||||
m_semaphoreRegisterAppEvt.give(); // Unlock the mutex waiting for the registration of the app.
|
||||
break;
|
||||
} // ESP_GATTS_REG_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_CREATE_EVT
|
||||
// Called when a new service is registered as having been created.
|
||||
//
|
||||
// create:
|
||||
// * esp_gatt_status_t status
|
||||
// * uint16_t service_handle
|
||||
// * esp_gatt_srvc_id_t service_id
|
||||
//
|
||||
case ESP_GATTS_CREATE_EVT: {
|
||||
BLEService* pService = m_serviceMap.getByUUID(param->create.service_id.id.uuid);
|
||||
m_serviceMap.setByHandle(param->create.service_handle, pService);
|
||||
//pService->setHandle(param->create.service_handle);
|
||||
m_semaphoreCreateEvt.give();
|
||||
break;
|
||||
} // ESP_GATTS_CREATE_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_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_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;
|
||||
}
|
||||
|
||||
// ESP_GATTS_DISCONNECT_EVT
|
||||
// If we receive a disconnect event then invoke the callback for disconnects (if one is present).
|
||||
// we also want to start advertising again.
|
||||
case ESP_GATTS_DISCONNECT_EVT: {
|
||||
m_connectedCount--;
|
||||
if (m_pServerCallbacks != nullptr) {
|
||||
m_pServerCallbacks->onDisconnect(this);
|
||||
}
|
||||
startAdvertising();
|
||||
break;
|
||||
} // ESP_GATTS_DISCONNECT_EVT
|
||||
|
||||
|
||||
// ESP_GATTS_ADD_CHAR_EVT - Indicate that a characteristic was added to the service.
|
||||
// add_char:
|
||||
// - esp_gatt_status_t status
|
||||
// - uint16_t attr_handle
|
||||
// - uint16_t service_handle
|
||||
// - esp_bt_uuid_t char_uuid
|
||||
case ESP_GATTS_ADD_CHAR_EVT: {
|
||||
break;
|
||||
} // ESP_GATTS_ADD_CHAR_EVT
|
||||
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 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
|
||||
|
||||
|
||||
/**
|
||||
* @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 BLEServer::addCharacteristic(BLECharacteristic *characteristic, BLEService *pService) {
|
||||
ESP_LOGD(tag, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s, serviceHandle=0x%.2x",
|
||||
characteristic->m_bleUUID.toString().c_str(),
|
||||
pService->getHandle());
|
||||
|
||||
m_characteristicMap.setByUUID(characteristic->m_bleUUID, characteristic);
|
||||
|
||||
esp_err_t errRc = ::esp_ble_gatts_add_char(
|
||||
pService->getHandle(),
|
||||
characteristic->getUUID().getNative(),
|
||||
(esp_gatt_perm_t)(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE),
|
||||
characteristic->getProperties(),
|
||||
&characteristic->m_value,
|
||||
NULL);
|
||||
|
||||
if (errRc != ESP_OK) {
|
||||
ESP_LOGE(tag, "esp_ble_gatts_add_char: rc=%d %s", errRc, espToString(errRc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void BLEServerCallbacks::onConnect(BLEServer* pServer) {
|
||||
ESP_LOGD("BLEServerCallbacks", ">> onConnect(): Default");
|
||||
ESP_LOGD("BLEServerCallbacks", "<< onConnect()");
|
||||
} // onConnect
|
||||
|
||||
void BLEServerCallbacks::onDisconnect(BLEServer* pServer) {
|
||||
ESP_LOGD("BLEServerCallbacks", ">> onDisconnect(): Default");
|
||||
ESP_LOGD("BLEServerCallbacks", "<< onDisconnect()");
|
||||
} // onDisconnect
|
||||
|
||||
#endif // CONFIG_BT_ENABLED
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 "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();
|
||||
|
||||
private:
|
||||
std::map<std::string, BLEService*> m_uuidMap;
|
||||
std::map<uint16_t, BLEService*> m_handleMap;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
BLEAdvertising* getAdvertising();
|
||||
void setCallbacks(BLEServerCallbacks *pCallbacks);
|
||||
void startAdvertising();
|
||||
|
||||
|
||||
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_ */
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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.id.inst_id = 0;
|
||||
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 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 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_LOGE(LOG_TAG, "<< Attempt to add a characteristic but we already have one with this UUID");
|
||||
//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_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))) {
|
||||
setHandle(param->create.service_handle);
|
||||
m_semaphoreCreateEvt.give();
|
||||
}
|
||||
break;
|
||||
} // ESP_GATTS_CREATE_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
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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:
|
||||
BLEService(const char* uuid, uint32_t numHandles=10);
|
||||
BLEService(BLEUUID uuid, uint32_t numHandles=10);
|
||||
|
||||
void addCharacteristic(BLECharacteristic* pCharacteristic);
|
||||
BLECharacteristic* createCharacteristic(const char* uuid, uint32_t properties);
|
||||
BLECharacteristic* createCharacteristic(BLEUUID uuid, uint32_t properties);
|
||||
void dump();
|
||||
void executeCreate(BLEServer* pServer);
|
||||
BLECharacteristic* getCharacteristic(const char* uuid);
|
||||
BLECharacteristic* getCharacteristic(BLEUUID uuid);
|
||||
BLEUUID getUUID();
|
||||
BLEServer* getServer();
|
||||
void start();
|
||||
std::string toString();
|
||||
uint16_t getHandle();
|
||||
|
||||
private:
|
||||
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;
|
||||
char deleteMe[10];
|
||||
//FreeRTOS::Semaphore m_serializeMutex;
|
||||
FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt");
|
||||
FreeRTOS::Semaphore m_semaphoreStartEvt = FreeRTOS::Semaphore("StartEvt");
|
||||
|
||||
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_ */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.second->getUUID().equals(uuid)) {
|
||||
return myPair.second;
|
||||
}
|
||||
}
|
||||
//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<std::string, BLEService *>(uuid.toString(), service));
|
||||
} // 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.second->handleGATTServerEvent(event, gatts_if, param);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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 "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 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
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
BLEUUID BLEUUID::fromString(std::string _uuid){
|
||||
uint8_t start = 0;
|
||||
if(strstr(_uuid.c_str(), "0x") != nullptr){
|
||||
start = 2;
|
||||
}
|
||||
uint8_t len = _uuid.length() - start;
|
||||
if(len == 4 ){
|
||||
uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
|
||||
return BLEUUID(x);
|
||||
}else if(len == 8){
|
||||
uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
|
||||
return BLEUUID(x);
|
||||
}else if (len == 36){
|
||||
return BLEUUID(_uuid);
|
||||
}
|
||||
return BLEUUID();
|
||||
}
|
||||
#endif /* CONFIG_BT_ENABLED */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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();
|
||||
bool equals(BLEUUID uuid);
|
||||
esp_bt_uuid_t* getNative();
|
||||
BLEUUID to128();
|
||||
std::string toString();
|
||||
static BLEUUID fromString(std::string uuid);
|
||||
|
||||
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_ */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::take(std::string owner)
|
||||
{
|
||||
ESP_LOGD(LOG_TAG, "Semaphore taking: %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;
|
||||
ESP_LOGD(LOG_TAG, "Semaphore taken: %s", toString().c_str());
|
||||
} // 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.
|
||||
*/
|
||||
void FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
|
||||
|
||||
ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
|
||||
|
||||
if (m_usePthreads) {
|
||||
assert(false);
|
||||
} else {
|
||||
xSemaphoreTake(m_semaphore, timeoutMs/portTICK_PERIOD_MS);
|
||||
}
|
||||
m_owner = owner;
|
||||
ESP_LOGV(LOG_TAG, "Semaphore taken: %s", toString().c_str());
|
||||
} // Semaphore::take
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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);
|
||||
void take(std::string owner="<Unknown>");
|
||||
void 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_ */
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
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 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 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 Restart the ESP32.
|
||||
*/
|
||||
void GeneralUtils::restart() {
|
||||
esp_restart();
|
||||
} // restart
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/**
|
||||
* @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 bool endsWith(std::string str, char c);
|
||||
static const char* errorToString(esp_err_t errCode);
|
||||
static void hexDump(const uint8_t* pData, uint32_t length);
|
||||
static std::string ipToString(uint8_t* ip);
|
||||
static void restart();
|
||||
};
|
||||
|
||||
#endif /* COMPONENTS_CPP_UTILS_GENERALUTILS_H_ */
|
||||
Reference in new issue
Block a user