Hi, im trying to connect ESP32(client) and HID Gamepad(Server) with BLE_client sample.
My gamepad has multiple characteristics, and its have same CharUUID and DescripoterUUID.
but descriptor's value field is diffelent.
In attached file, top of charcteristic(Value fields are 01 00 and 04 00) is correct,
and others(value fields are null) are not correct.
Then I want to get value field to find correct characteristics, but it seems value field is not implemented in BLERemoteDescriptor.
(Multiple Characteristics that has same CharUUID is not assumpted?)
Can I get descriptor's value field?
My english is poor,sorry ;D

Try to change this line:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEHIDDevice.cpp#L19
Change 40 to 100 or something.
Thanks for reply.
But its not take effect.
Then I changed BLERemoteService::retrieveCharacteristics() to accept same Characteristics.
m_characteristicMap.insert(std::pair
↓
char str[10];
sprintf(str, "%u", offset);
m_characteristicMap.insert(std::pair
And I got 9 Charactirstics. (UUID+offset)
00002a4a-0000-1000-8000-00805f9b34fb0
00002a4b-0000-1000-8000-00805f9b34fb3
00002a4c-0000-1000-8000-00805f9b34fb1
00002a4d-0000-1000-8000-00805f9b34fb4
00002a4d-0000-1000-8000-00805f9b34fb5
00002a4d-0000-1000-8000-00805f9b34fb6
00002a4d-0000-1000-8000-00805f9b34fb7
00002a4d-0000-1000-8000-00805f9b34fb8
00002a4e-0000-1000-8000-00805f9b34fb2
I found correct Characteristic, but its hard coded. because I cannnot get Discriptor's value field(´・ω・`)
int i = 0;
auto itr = pCharacteristicMap->begin();
for(auto itr = pCharacteristicMap->begin(); itr != pCharacteristicMap->end(); ++itr) {
if (charUUID.toString() == itr->second->getUUID().toString()) { // 0x00002a4d
Serial.print("CharUUID mached: ");
if (itr->second->canRead() && itr->second->canNotify() && !itr->second->canWrite()) { // Characteristic Property
if (i == 1) { // 2nd of NR and not Write Characteristics. but its hard coded.
Serial.print("Correct : ");
...
Ok, im not sure i understand you. You want to have esp32 hid device or you have hid device and you want to build esp32 host device?
I want to connect Bluetooth Gamepad(BLE Server) and ESP32(BLE Client).
and i want to control RC Car(with ESP32) by Gamepad with BLE HID.
Gamepad is this. https://www.amazon.co.jp/gp/product/B01N0V7UWY/
Ok, that sound cool. I will try to help you with any issue with HID class and connecting client with server.
@coppercele I see where is the problem. There is getCharacteristics() function that you found left for back compatibility reasons and there is another for multiple characteristics with the same UUID https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLERemoteService.h#L37
But the issue is it is only declared in header file. I have no idea why it is not implemented. I will try to add implementation when i find some time. Im trying to do some bugfixes and additions to this library.
Thank you!
I attched my test codes.
ino and cpp are not accepted, then i renamed to txt.
It can read from Gamepad like below.
it seems can read datas length 2 and 4, but cannot 8 or up?
Notify callback for characteristic 00002a4d-0000-1000-8000-00805f9b34fb of data length 4
Data : 00000000
@coppercele Try this code, this is BLERemoteService.cpp:
/*
* 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_characteristicMapByHandle) {
myPair.second->gattClientEventHandler(event, gattc_if, evtParam);
}
} // gattClientEventHandler
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
* @return Reference to the remote characteristic object.
* @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(BLEUUID(uuid));
} // getCharacteristic
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
* @return Reference to the characteristic object.
* @throws BLEUuidNotFoundException
*/
BLERemoteCharacteristic* BLERemoteService::getCharacteristic(BLEUUID uuid) {
// Design
// ------
// We wish to retrieve the characteristic given its UUID. It is possible that we have not yet asked the
// device what characteristics it has in which case we have nothing to match against. If we have not
// asked the device about its characteristics, then we do that now. Once we get the results we can then
// examine the characteristics map to see if it has the characteristic we are looking for.
if (!m_haveCharacteristics) {
retrieveCharacteristics();
}
std::string v = uuid.toString();
for (auto &myPair : m_characteristicMap) {
if (myPair.first == v) {
return myPair.second;
}
}
throw new BLEUuidNotFoundException();
} // getCharacteristic
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
* @return N/A
*/
void BLERemoteService::retrieveCharacteristics() {
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
removeCharacteristics(); // Forget any previous characteristics.
uint16_t offset = 0;
esp_gattc_char_elem_t result;
while(1) {
uint16_t count = 1;
esp_gatt_status_t status = ::esp_ble_gattc_get_all_char(
getClient()->getGattcIf(),
getClient()->getConnId(),
m_startHandle,
m_endHandle,
&result,
&count,
offset
);
if (status == ESP_GATT_INVALID_OFFSET) { // We have reached the end of the entries.
break;
}
if (status != ESP_GATT_OK) { // If we got an error, end.
ESP_LOGE(LOG_TAG, "esp_ble_gattc_get_all_char: %s", BLEUtils::gattStatusToString(status).c_str());
break;
}
if (count == 0) { // If we failed to get any new records, end.
break;
}
ESP_LOGD(LOG_TAG, "Found a characteristic: Handle: %d, UUID: %s", result.char_handle, BLEUUID(result.uuid).toString().c_str());
// We now have a new characteristic ... let us add that to our set of known characteristics
BLERemoteCharacteristic *pNewRemoteCharacteristic = new BLERemoteCharacteristic(
result.char_handle,
BLEUUID(result.uuid),
result.properties,
this
);
m_characteristicMap.insert(std::pair<std::string, BLERemoteCharacteristic*>(pNewRemoteCharacteristic->getUUID().toString(), pNewRemoteCharacteristic));
m_characteristicMapByHandle.insert(std::pair<uint16_t, BLERemoteCharacteristic*>(result.char_handle, pNewRemoteCharacteristic));
offset++; // Increment our count of number of descriptors found.
} // Loop forever (until we break inside the loop).
m_haveCharacteristics = true; // Remember that we have received the characteristics.
ESP_LOGD(LOG_TAG, "<< getCharacteristics()");
} // getCharacteristics
/**
* @brief Retrieve a map of all the characteristics of this service.
* @return A map of all the characteristics of this service.
*/
std::map<std::string, BLERemoteCharacteristic *> * BLERemoteService::getCharacteristics() {
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
// If is possible that we have not read the characteristics associated with the service so do that
// now. The request to retrieve the characteristics by calling "retrieveCharacteristics" is a blocking
// call and does not return until all the characteristics are available.
if (!m_haveCharacteristics) {
retrieveCharacteristics();
}
ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str());
return &m_characteristicMap;
} // getCharacteristics
void BLERemoteService::getCharacteristics(std::map<uint16_t, BLERemoteCharacteristic*>* pCharacteristicMap){
pCharacteristicMap = &m_characteristicMapByHandle;
} // Get the characteristics map.
/**
* @brief Get the client associated with this service.
* @return A reference to the client associated with this service.
*/
BLEClient* BLERemoteService::getClient() {
return m_pClient;
} // getClient
uint16_t BLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
esp_gatt_id_t* BLERemoteService::getSrvcId() {
return &m_srvcId;
} // getSrvcId
uint16_t BLERemoteService::getStartHandle() {
return m_startHandle;
} // getStartHandle
uint16_t BLERemoteService::getHandle() {
ESP_LOGD(LOG_TAG, ">> getHandle: service: %s", getUUID().toString().c_str());
ESP_LOGD(LOG_TAG, "<< getHandle: %d 0x%.2x", getStartHandle(), getStartHandle());
return getStartHandle();
} // getHandle
BLEUUID BLERemoteService::getUUID() {
return m_uuid;
}
/**
* @brief Read the value of a characteristic associated with this service.
*/
std::string BLERemoteService::getValue(BLEUUID characteristicUuid) {
ESP_LOGD(LOG_TAG, ">> readValue: uuid: %s", characteristicUuid.toString().c_str());
std::string ret = getCharacteristic(characteristicUuid)->readValue();
ESP_LOGD(LOG_TAG, "<< readValue");
return ret;
} // readValue
/**
* @brief Delete the characteristics in the characteristics map.
* We maintain a map called m_characteristicsMap that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleteing
* them. This method does just that.
* @return N/A.
*/
void BLERemoteService::removeCharacteristics() {
for (auto &myPair : m_characteristicMap) {
delete myPair.second;
//m_characteristicMap.erase(myPair.first); // Should be no need to delete as it will be deleted by the clear
}
m_characteristicMap.clear(); // Clear the map
for (auto &myPair : m_characteristicMapByHandle) {
delete myPair.second;
}
m_characteristicMapByHandle.clear(); // Clear the map
} // removeCharacteristics
/**
* @brief Set the value of a characteristic.
* @param [in] characteristicUuid The characteristic to set.
* @param [in] value The value to set.
* @throws BLEUuidNotFound
*/
void BLERemoteService::setValue(BLEUUID characteristicUuid, std::string value) {
ESP_LOGD(LOG_TAG, ">> setValue: uuid: %s", characteristicUuid.toString().c_str());
getCharacteristic(characteristicUuid)->writeValue(value);
ESP_LOGD(LOG_TAG, "<< setValue");
} // setValue
/**
* @brief Create a string representation of this remote service.
* @return A string representation of this remote service.
*/
std::string BLERemoteService::toString() {
std::ostringstream ss;
ss << "Service: uuid: " + m_uuid.toString();
ss << ", start_handle: " << std::dec << m_startHandle << " 0x" << std::hex << m_startHandle <<
", end_handle: " << std::dec << m_endHandle << " 0x" << std::hex << m_endHandle;
for (auto &myPair : m_characteristicMap) {
ss << "\n" << myPair.second->toString();
// myPair.second is the value
}
return ss.str();
} // toString
#endif /* CONFIG_BT_ENABLED */
Modified code in your ble_client.ino:
std::map<uint16_t, BLERemoteCharacteristic *>* pCharacteristicMap;
pRemoteService->getCharacteristics(pCharacteristicMap);
Serial.print("Number of Charactirstic: ");
Serial.println(pCharacteristicMap->size());
// int i = 0;
auto itr = pCharacteristicMap->begin();
for(auto itr = pCharacteristicMap->begin(); itr != pCharacteristicMap->end(); ++itr) {
Serial.print("first: ");
Serial.print(itr->first);
Serial.print(" second :");
Serial.println(itr->second->getUUID().toString().c_str());
if (charUUID.toString() == itr->second->getUUID().toString()) {
Serial.print("CharUUID mached: ");
Serial.println(itr->second->getUUID().toString().c_str());
if (itr->second->canRead() && itr->second->canNotify() && !itr->second->canWrite()) {
// if (i == 1) {
Serial.print("Correct : ");
// Serial.println(i);
pRemoteCharacteristic = itr->second;
// Read the value of the characteristic.
std::string value = pRemoteCharacteristic->readValue();
Serial.print("The characteristic value was: ");
Serial.println(value.c_str());
pRemoteCharacteristic->registerForNotify(notifyCallback);
// break;
// }
// i++;
Serial.println("next");
}
else {
Serial.println("Incorrect");
}
Hi, I tryed its.
BLERemoteService::getCharacteristics() not return multiple same Characteristics.
then I made BLERemoteService::getCharacteristicsByHandle() in BLERemoteService.h and .cpp.
std::map
ESP_LOGD(LOG_TAG, ">> getCharacteristics() for service: %s", getUUID().toString().c_str());
if (!m_haveCharacteristics) {
retrieveCharacteristics();
}
ESP_LOGD(LOG_TAG, "<< getCharacteristics() for service: %s", getUUID().toString().c_str());
return &m_characteristicMapByHandle;
}
and i used it in BLE_Client.ino.
then I can read from Gamepad =D
Notify callback for characteristic 00002a4d-0000-1000-8000-00805f9b34fb of data length 4
Data : 02000000
I cound not notice that resisterForNotify() can accept all same characteristics.
It seems that your function looks better than mine. I will change it in code then.
You can have notification callback that will be tied to every input report characteristic by reportID or you can have only one input report characteristic and all necessary data packed in one single message.
Of course you can have single notification callback function for all characteristics.
I succeed controling RC tank with Bluetooth HID.
↓I uploaded a movie gif to imgur.com
https://imgur.com/a/3yHAKKn
Nice job.
Thank you very much =D
Most helpful comment
I succeed controling RC tank with Bluetooth HID.
↓I uploaded a movie gif to imgur.com
https://imgur.com/a/3yHAKKn