#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"
#define NULL_HANDLE (0xffff)
static const char* LOG_TAG = "BLEService";
BLEService::BLEService(const char* uuid) : BLEService(BLEUUID(uuid)) {
}
BLEService::BLEService(BLEUUID uuid) {
m_uuid = uuid;
m_handle = NULL_HANDLE;
m_pServer = nullptr;
m_lastCreatedCharacteristic = nullptr;
}
void BLEService::executeCreate(BLEServer *pServer) {
ESP_LOGD(LOG_TAG, ">> executeCreate() - Creating service (esp_ble_gatts_create_service) service uuid: %s", getUUID().toString().c_str());
m_pServer = pServer;
esp_gatt_srvc_id_t srvc_id;
srvc_id.id.inst_id = 0;
srvc_id.id.uuid = *m_uuid.getNative();
m_semaphoreCreateEvt.take("executeCreate");
esp_err_t errRc = ::esp_ble_gatts_create_service(getServer()->getGattsIf(), &srvc_id, 10);
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");
}
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());
}
BLEUUID BLEService::getUUID() {
return m_uuid;
}
void BLEService::start() {
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();
}
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()");
}
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");
}
uint16_t BLEService::getHandle() {
return m_handle;
}
void BLEService::addCharacteristic(BLECharacteristic* pCharacteristic) {
ESP_LOGD(LOG_TAG, ">> addCharacteristic()");
ESP_LOGD(LOG_TAG, "Adding characteristic (esp_ble_gatts_add_char): uuid=%s to service: %s",
pCharacteristic->getUUID().toString().c_str(),
toString().c_str());
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;
}
m_characteristicMap.setByUUID(pCharacteristic->getUUID(), pCharacteristic);
ESP_LOGD(LOG_TAG, "<< addCharacteristic()");
}
BLECharacteristic* BLEService::createCharacteristic(const char* uuid, uint32_t properties) {
return createCharacteristic(BLEUUID(uuid), properties);
}
BLECharacteristic* BLEService::createCharacteristic(BLEUUID uuid, uint32_t properties) {
BLECharacteristic *pCharacteristic = new BLECharacteristic(uuid, properties);
addCharacteristic(pCharacteristic);
return pCharacteristic;
}
void BLEService::handleGATTServerEvent(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) {
switch(event) {
case ESP_GATTS_ADD_CHAR_EVT: {
if (m_handle == param->add_char.service_handle) {
BLECharacteristic *pCharacteristic = getCharacteristic(BLEUUID(param->add_char.char_uuid));
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();
m_semaphoreAddCharEvt.give();
break;
}
pCharacteristic->setHandle(param->add_char.attr_handle);
m_characteristicMap.setByHandle(param->add_char.attr_handle, pCharacteristic);
m_semaphoreAddCharEvt.give();
break;
}
break;
}
case ESP_GATTS_START_EVT: {
if (param->start.service_handle == getHandle()) {
m_semaphoreStartEvt.give();
}
break;
}
case ESP_GATTS_CREATE_EVT: {
if (getUUID().equals(BLEUUID(param->create.service_id.id.uuid))) {
setHandle(param->create.service_handle);
m_semaphoreCreateEvt.give();
}
break;
}
default: {
break;
}
}
m_characteristicMap.handleGATTServerEvent(event, gatts_if, param);
}
BLECharacteristic* BLEService::getCharacteristic(const char* uuid) {
return getCharacteristic(BLEUUID(uuid));
}
BLECharacteristic* BLEService::getCharacteristic(BLEUUID uuid) {
return m_characteristicMap.getByUUID(uuid);
}
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();
}
BLECharacteristic* BLEService::getLastCreatedCharacteristic() {
return m_lastCreatedCharacteristic;
}
BLEServer* BLEService::getServer() {
return m_pServer;
}
#endif