Memory Leak scanning BLE devices

Viewed 526

I'm using an ESP32 to scan BLE advertisers but I have a memory leak. If I comment out line 94 (pBLEScan->start) the leak stops, but so does the scanning. When scanning, it works as expected, but leaks roughly 148 bytes per scan.

How do I identify and fix this leak?

I have tried many variations, but to no avail.

https://pastebin.com/TsNU02h2

#include <iostream>
#include <string>
#include <Arduino.h>

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

#define SCAN_TIME 5

// Define a struct to hold the data on a single BLE device
struct BleDevice {
  char address[18];
  char name[80];
  char manufacturer[80];
  int appearance;
  int rssi;
  int txPower;
};

// create a static array to hold 80 BLE Devices
static struct BleDevice BleDevices[80];  
static int BleDeviceCounter = 0;

// BLE variables
BLEScan* pBLEScan;
BLEScanResults foundDevices;
static char dev_uuid[80];
static int ind = 0;           // used in loop as an index
static int lastFreeHeap = 0;
static int deviceCounter = 0;

// BLE Callback
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice adDevice) {

        if (adDevice.haveServiceUUID()) {  
            strcpy(dev_uuid, adDevice.getServiceUUID().toString().c_str());   
        } else {
            strcpy(dev_uuid, "UNKNOWN");   
        }

        if (adDevice.haveName()) {         
            strcpy(BleDevices[BleDeviceCounter].name, adDevice.getName().c_str());  
        } else {
            strcpy(BleDevices[BleDeviceCounter].name, "");
        }
        
        if (adDevice.haveManufacturerData()) {
            strcpy(BleDevices[BleDeviceCounter].manufacturer, BLEUtils::buildHexData(NULL, (uint8_t*)adDevice.getManufacturerData().data(), adDevice.getManufacturerData().length()));
        } else {
            strcpy(BleDevices[BleDeviceCounter].manufacturer, "");
        }
        
        if (adDevice.haveTXPower()) {
            BleDevices[BleDeviceCounter].txPower = adDevice.getTXPower();
        } else {
            BleDevices[BleDeviceCounter].txPower = -999;      // -999 means NULL
        }
        
        if (adDevice.haveAppearance()) {                   
            BleDevices[BleDeviceCounter].appearance = adDevice.getAppearance();
        } else {   // clear the appearance
            BleDevices[BleDeviceCounter].appearance = -999 ;  // -999 means NULL
        }
        
        strcpy(BleDevices[BleDeviceCounter].address, adDevice.getAddress().toString().c_str());
        BleDevices[BleDeviceCounter].rssi = adDevice.getRSSI();
        
        BleDeviceCounter++;    // increment the static device counter last
    }
};

void setup()
{
    Serial.begin(115200);
    Serial.println("\n\n Started...\n");

    // BLE setup
    Serial.println("Scanning for BT Devices...");
    BleDeviceCounter = 0;
    BLEDevice::init("");
    pBLEScan = BLEDevice::getScan();
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setActiveScan(false); 
    pBLEScan->setInterval(100);
    pBLEScan->setWindow(99);  // less or equal setInterval value
}

void loop()
{
    BleDeviceCounter = 0;   // Set counter to zero
    foundDevices = pBLEScan->start(SCAN_TIME, false);   // Start scan

    deviceCounter = BleDeviceCounter;
    for (ind=0; ind < deviceCounter; ind++) {
      Serial.printf("[%i] {address=\"%s\", rssi = \"%i\", txPower = \"%i\", mData = \"%s\", app = \"%i\", name = \"%s\"}\n", 
          ind, 
          BleDevices[ind].address, 
          BleDevices[ind].rssi, 
          BleDevices[ind].txPower, 
          BleDevices[ind].manufacturer,
          BleDevices[ind].appearance,
          BleDevices[ind].name
          );
    }

    pBLEScan->clearResults();   // delete results fromBLEScan buffer to release memory
    Serial.printf("Free Heap = %i  <%i> \n", ESP.getFreeHeap(), (ESP.getFreeHeap()-lastFreeHeap));
    lastFreeHeap = ESP.getFreeHeap();
    delay(500);                // delay 1/2 second
}

//eof
1 Answers

It turns out that BLEUtils::buildHexData() will allocate memory and return a pointer that needs to be freed. It works as expected when the above line of code is replaced with:

char *hex = BLEUtils::buildHexData(NULL, 
            (uint8_t*)adDevice.getManufacturerData().data(),
            adDevice.getManufacturerData().length());
strcpy(BleDevices[BleDeviceCounter].manufacturer, hex);
free(hex);
Related