#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>
static const char* LOG_TAG = "BLEClient";
BLEClient::BLEClient() {
m_pClientCallbacks = nullptr;
m_conn_id = 0;
m_gattc_if = 0;
m_haveServices = false;
}
bool BLEClient::connect(BLEAddress address) {
ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
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;
m_semaphoreOpenEvt.take("connect");
errRc = ::esp_ble_gattc_open(
getGattcIf(),
*getPeerAddress().getNative(),
1
);
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;
}
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;
}
m_peerAddress = BLEAddress("00:00:00:00:00:00");
ESP_LOGD(LOG_TAG, "<< disconnect()");
}
void BLEClient::gattClientEventHandler(
esp_gattc_cb_event_t event,
esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t* evtParam) {
switch(event) {
case ESP_GATTC_NOTIFY_EVT: {
BLERemoteService *pBLERemoteService = getService(BLEUUID(evtParam->notify.srvc_id.id.uuid));
if (pBLERemoteService == nullptr) {
ESP_LOGE(LOG_TAG, "Could not find service with UUID %s for notification", BLEUUID(evtParam->notify.srvc_id.id.uuid).toString().c_str());
break;
}
BLERemoteCharacteristic* pBLERemoteCharacteristic = pBLERemoteService->getCharacteristic(BLEUUID(evtParam->notify.char_id.uuid));
if (pBLERemoteCharacteristic == nullptr) {
ESP_LOGE(LOG_TAG, "Could not find characteristic with UUID %s for notification", BLEUUID(evtParam->notify.char_id.uuid).toString().c_str());
break;
}
if (pBLERemoteCharacteristic->m_notifyCallback != nullptr) {
pBLERemoteCharacteristic->m_notifyCallback(pBLERemoteCharacteristic, evtParam->notify.value, evtParam->notify.value_len, evtParam->notify.is_notify);
}
break;
}
case ESP_GATTC_OPEN_EVT: {
m_conn_id = evtParam->open.conn_id;
if (m_pClientCallbacks != nullptr) {
m_pClientCallbacks->onConnect(this);
}
m_semaphoreOpenEvt.give();
break;
}
case ESP_GATTC_REG_EVT: {
m_gattc_if = gattc_if;
m_semaphoreRegEvt.give();
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
m_semaphoreSearchCmplEvt.give();
break;
}
case ESP_GATTC_SEARCH_RES_EVT: {
BLEUUID uuid = BLEUUID(evtParam->search_res.srvc_id);
BLERemoteService* pRemoteService = new BLERemoteService(evtParam->search_res.srvc_id, this);
m_servicesMap.insert(std::pair<std::string, BLERemoteService *>(uuid.toString(), pRemoteService));
break;
}
default: {
break;
}
}
for (auto &myPair : m_servicesMap) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
}
BLEAddress BLEClient::getPeerAddress() {
return m_peerAddress;
}
uint16_t BLEClient::getConnId() {
return m_conn_id;
}
esp_gatt_if_t BLEClient::getGattcIf() {
return m_gattc_if;
}
BLERemoteService* BLEClient::getService(const char* uuid) {
return getService(BLEUUID(uuid));
}
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
if (!m_haveServices) {
getServices();
}
std::string v = uuid.toString();
for (auto &myPair : m_servicesMap) {
if (myPair.first == v) {
return myPair.second;
}
}
return nullptr;
}
std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
ESP_LOGD(LOG_TAG, ">> getServices");
m_servicesMap.clear();
esp_err_t errRc = esp_ble_gattc_search_service(
getGattcIf(),
getConnId(),
NULL
);
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;
ESP_LOGD(LOG_TAG, "<< getServices");
return &m_servicesMap;
}
void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
m_pClientCallbacks = pClientCallbacks;
}
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";
}
return ss.str();
}
#endif