Page 1 of 1

ESP32 (Sparkfun ESP32 Thing), Arduino IDE, BLE connections

Posted: Sun Feb 27, 2022 8:35 pm
by IanBuk
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:

Code: Select all

/*
   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:

Code: Select all

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. Apart from debug output, the only thing I've changed is to add a timeout in the 'take' calls. e.g.:

Code: Select all

	
	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