Hello,
I'm using a Sparkfun ESP32 Thing with the Arduino framework, trying to connect to and read from services on some W4 beacons - https://www.mokosmart.com/positioning-beacon-w3-w4/
I can successfully scan for the beacons, get the address, local name etc. however, actually connecting never succeeds - the call to client->connect() never returns.
My sketch is:
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
*/
#include <BLEDevice.h>
#include <BLEClient.h>
#include <BLEUtils.h>
#include <BLEDevice.h>
#include <BLEAdvertisedDevice.h>
#include <sstream>
BLEScan* pBLEScan;
int sensorCount = 0;
int scanTime = 2;
BLEAdvertisedDevice sensorsFound[10];
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks
{
void onResult(BLEAdvertisedDevice advertisedDevice)
{
std::stringstream ss;
if (advertisedDevice.haveManufacturerData())
{
int dataLength = advertisedDevice.getManufacturerData().length();
char *pHex = BLEUtils::buildHexData(nullptr, (uint8_t*)advertisedDevice.getManufacturerData().data(), dataLength);
ss << pHex;
free(pHex);
if (isMyBeacon(ss.str().c_str(), 0))
{
Serial.printf("Beacon: %s \n", ss.str().c_str());
sensorsFound[sensorCount] = advertisedDevice;
sensorCount++;
Serial.printf("\n\n");
}
}
}
bool isMyBeacon(const char* beaconManufacturer, int offset)
{
const char* _beaconManufacturerUUID = "4c000215e2c56db5dffb48d2b060d0f5a71096e";
bool matches = true;
for (int i = 0; i < 39; i++)
{
if (_beaconManufacturerUUID[i] != beaconManufacturer[i + offset])
{
matches = false;
break;
}
}
return matches;
}
};
int getMinorVersion(uint8_t *pData, int dataLen)
{
int version = getVersion(pData, dataLen, 3);
return version;
}
int getMajorVersion(uint8_t *pData, int dataLen)
{
int version = getVersion(pData, dataLen, 5);
return version;
}
int getVersion(uint8_t *pData, int dataLen, int offset)
{
int versionOffset = dataLen - offset;
byte lowByte = (uint8_t)pData[versionOffset + 1];
byte highByte = (uint8_t)pData[versionOffset];
int version = highByte << 8 | lowByte;
return version;
}
void listFoundSensorDetails(BLEAdvertisedDevice *advertisedDevice)
{
BLEScan *scan = advertisedDevice->getScan();
std::string serviceData = advertisedDevice->getServiceData();
int dataLength = advertisedDevice->getManufacturerData().length();
Serial.println("Details: "); Serial.println(advertisedDevice->toString().c_str());
Serial.print("Address: "); Serial.println(advertisedDevice->getAddress().toString().c_str());
int minor = getMinorVersion((uint8_t*)advertisedDevice->getManufacturerData().data(), dataLength);
int major = getMajorVersion((uint8_t*)advertisedDevice->getManufacturerData().data(), dataLength);
Serial.printf("Sensor ID %d:%03d\n", major, minor);
Serial.print("Name"); Serial.println(advertisedDevice->getName().c_str());
if (advertisedDevice->haveServiceUUID())
{
Serial.print("Service UUID:"); Serial.println(advertisedDevice->getServiceUUID().toString().c_str());
}
else
{
Serial.println("haveServiceUUID was false.");
}
if (advertisedDevice->haveServiceData())
{
Serial.print("Service Data UUID:"); Serial.println(advertisedDevice->getServiceDataUUID().toString().c_str());
Serial.print("Service Data:"); Serial.println(advertisedDevice->getServiceData().c_str());
}
else
{
Serial.println("haveServiceData was false.");
}
size_t payloadLength = advertisedDevice->getPayloadLength();
Serial.print("Payload length "); Serial.println(payloadLength);
listServices(advertisedDevice);
}
void listServices(BLEAdvertisedDevice *advertisedDevice)
{
static BLEUUID serviceUUID("180F-0000-1000-8000-00805F9B34FB");
static BLEUUID charUUID("2A19-0000-1000-8000-00805F9B34FB");
printf("listServices\n");
Serial.println("creat BLEClient");
BLEAddress pAddress = advertisedDevice->getAddress();
Serial.print("Address: "); Serial.println(pAddress.toString().c_str());
BLEClient *pClient = BLEDevice::createClient();
Serial.println("created the client");
pClient->connect(pAddress);
printf("back from connect\n");
BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
printf("back from getService\n");
int serviceCount = advertisedDevice->getServiceCount();
Serial.printf(" %d services exposed\n", serviceCount);
if (serviceCount > 0)
{
for (int i = 0; i < serviceCount; i++)
{
BLEUUID uuid = advertisedDevice->getAllServiceUUIDs()[i];
Serial.printf(" UUID: %s \n", uuid.toString());
}
}
pClient->disconnect();
}
void listAllFoundSensorDetails()
{
if (sensorCount == 0)
{
printf("No sensors found\n");
}
else
{
for (int i = 0; i < sensorCount; i++)
{
listFoundSensorDetails(&sensorsFound[i]);
Serial.println("\n\n\n\n");
}
}
Serial.println("**********************************************");
}
void setup()
{
Serial.begin(115200);
Serial.println("Scanning...");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->start(scanTime, false);
}
void loop()
{
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");
pBLEScan->clearResults();
pBLEScan->stop();
listAllFoundSensorDetails();
sensorCount = 0;
delay(20);
}
And here's some sample output from the listFoundSensorDetails function:
Details:
Name: S00383, Address: e1:4e:ab:f2:64:dd, manufacturer data: 4c000215e2c56db5dffb48d2b060d0f5a71096e00000017fbf, txPower: -12
Address: e1:4e:ab:f2:64:dd
Sensor ID 0:383
NameS00383
haveServiceUUID was false.
Service Data UUID:0000ff01-0000-1000-8000-00805f9b34fb
Service Data:R
Payload length 58
listServices
creat BLEClient
Address: e1:4e:ab:f2:64:dd
created the client
1
do m_semaphoreRegEvt.take('connect');
done m_semaphoreRegEvt.take('connect');
2
4
do m_semaphoreRegEvt.wait('connect');
getGattcIf
getGattcIf
into gattClientEventHandler
do m_semaphoreRegEvt.give();
done m_semaphoreRegEvt.give();
done m_semaphoreRegEvt.wait('connect');
5
m_semaphoreOpenEvt.take(msTimeOut,'connect'
m_semaphoreOpenEvt.take(msTimeOut,'connect'
6
getPeerAddress
7
8
uint32_t rc = m_semaphoreOpenEvt.wait('connect');
getGattcIf
into gattClientEventHandler
ESP_GATTC_DISCONNECT_EVT
m_semaphoreRssiCmplEvt.give();
done m_semaphoreRssiCmplEvt.give();
m_semaphoreSearchCmplEvt.give(1);
done m_semaphoreSearchCmplEvt.give(1);
There is a delay between 'uint32_t rc = m_semaphoreOpenEvt.wait('connect');' and 'getCattcIf' of around 30-60 seconds. The 1...8 debug lines are simply stage markers showing how far through the code I've got.
You might be wondering where all that debug information comes from. It's from BLEClient - I added some printf statements to help me understand what's going on. The BLEClient.cpp and BLEClient.h that I'm using are attached. Apart from debug output, the only thing I've changed is to add a timeout in the 'take' calls. e.g.:
printf("m_semaphoreRssiCmplEvt.take(msTimeOut, 'getRssi');\n");
m_semaphoreRssiCmplEvt.take(msTimeOut, "getRssi");
printf("done m_semaphoreRssiCmplEvt.take(msTimeOut, 'getRssi');\n");
Now it looks to me like it's getting into the getServices function and eventually waiting for the ESP_GATTC_OPEN_EVT, but that never arrives. ESP_GATTC_DISCONNECT_EVT does though, which we can see in the debug output.
The beacons do work - I can connect with the nRFConnect app and see the data. I can also connect with the MokoBeacon app on my iPad - the beacons are from Moko.
So, any suggestions as to what I can do to get these beacons to connect to my Sparkfun ESP32 Thing?
Thanks for any suggestions.
Ian
BLEClient.cpp (18.8 KB)
BLEClient.h (4.2 KB)