#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
#include <esp_log.h>
#include <esp_bt.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include "BLEClient.h"
#include "BLEUtils.h"
#include "BLEService.h"
#include "GeneralUtils.h"
#include <string>
#include <sstream>
#include <unordered_set>
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
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;
}
BLEClient::~BLEClient() {
for (auto &myPair : m_servicesMap) {
delete myPair.second;
}
m_servicesMap.clear();
}
void BLEClient::clearServices() {
ESP_LOGD(LOG_TAG, ">> clearServices");
for (auto &myPair : m_servicesMap) {
delete myPair.second;
}
m_servicesMap.clear();
ESP_LOGD(LOG_TAG, "<< clearServices");
}
bool BLEClient::connect(BLEAddress address) {
ESP_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
m_semaphoreRegEvt.take("connect");
clearServices();
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(),
BLE_ADDR_TYPE_PUBLIC,
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;
}
esp_ble_gattc_app_unregister(getGattcIf());
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_DISCONNECT_EVT: {
if (m_pClientCallbacks != nullptr) {
m_pClientCallbacks->onDisconnect(this);
}
m_isConnected = false;
break;
}
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;
}
m_semaphoreOpenEvt.give(evtParam->open.status);
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,
evtParam->search_res.start_handle,
evtParam->search_res.end_handle
);
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);
}
}
uint16_t BLEClient::getConnId() {
return m_conn_id;
}
esp_gatt_if_t BLEClient::getGattcIf() {
return m_gattc_if;
}
BLEAddress BLEClient::getPeerAddress() {
return m_peerAddress;
}
int BLEClient::getRssi() {
ESP_LOGD(LOG_TAG, ">> getRssi()");
if (!isConnected()) {
ESP_LOGD(LOG_TAG, "<< getRssi(): Not connected");
return 0;
}
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;
}
BLERemoteService* BLEClient::getService(const char* uuid) {
return getService(BLEUUID(uuid));
}
BLERemoteService* BLEClient::getService(BLEUUID uuid) {
ESP_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
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;
}
}
ESP_LOGD(LOG_TAG, "<< getService: not found");
throw new BLEUuidNotFoundException;
}
std::map<std::string, BLERemoteService*>* BLEClient::getServices() {
ESP_LOGD(LOG_TAG, ">> getServices");
clearServices();
esp_err_t errRc = esp_ble_gattc_search_service(
getGattcIf(),
getConnId(),
nullptr
);
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;
}
std::string BLEClient::getValue(BLEUUID serviceUUID, BLEUUID characteristicUUID) {
ESP_LOGD(LOG_TAG, ">> getValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
std::string ret = getService(serviceUUID)->getCharacteristic(characteristicUUID)->readValue();
ESP_LOGD(LOG_TAG, "<<getValue");
return ret;
}
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) {
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT: {
m_semaphoreRssiCmplEvt.give((uint32_t)param->read_rssi_cmpl.rssi);
break;
}
default:
break;
}
}
bool BLEClient::isConnected() {
return m_isConnected;
}
void BLEClient::setClientCallbacks(BLEClientCallbacks* pClientCallbacks) {
m_pClientCallbacks = pClientCallbacks;
}
void BLEClient::setValue(BLEUUID serviceUUID, BLEUUID characteristicUUID, std::string value) {
ESP_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s", serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
getService(serviceUUID)->getCharacteristic(characteristicUUID)->writeValue(value);
ESP_LOGD(LOG_TAG, "<< setValue");
}
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