ESP32-BLE Scanner-Mem Leak issue

Hi,
This is a somewhat common BLE Scanning sketch on an ESP32 Devkit to monitor TPMS. I've added FreeRTOS to have this processed on Core 0. I've noticed a memory issue, but am unable to deduce the cause. Would greatly appreciate if someone could put some fresh eyes on this and see what I'm doing wrong?

Also. This same issue occurred regardless of using FreeRTOS or not.

I'm including both the Sketch and copy of the Serial Terminal to show the memory loss. Unfortunately, the Arduino-ESP32 only offers four functions for getting Heap Memory Info.

These calls do not provide a 'fragmentation' (unlike the ESP-IDF), but it does show how the "FREE" memory is slowly declining over time.

Thank you ahead of time for any assistance that could be lent!

#include <Streaming.h>  // http://arduiniana.org/libraries/streaming/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

//Assignments
BLEScan* pBLEScan;
static BLEAddress *pServerAddress;

// Variables
int BLEscantime = 0;
int CF = 0;
bool boot = 0;
int pFTime_interval = 1000;          //Checks HEAP every interval
unsigned long pFTime,cTime;
int getFreeHeap_prev;

////////////////////////////////For GUISlice
char TPMS_Pres[6], TPMS_Temp[6], TPMS_Battery[6], TPMS_Signal[6];

// TPMS BLE SENSORS known addresses
String knownAddresses[] = {
  "80:ea:ca:12:1d:10",    //FRONT
  "81:ea:ca:22:19:43",    //BACK
  "80:ea:ca:12:8b:2c",    //LEFT
  "81:ea:ca:22:8c:85"     //RIGHT
  };
// TPMS active data
int TPMSNew [4][5] = {    //pres,temp,bat,sig,alarm
  {999,999,99,99,0},
  {999,999,99,99,0},
  {999,999,99,99,0},
  {999,999,99,99,0},  
};
int TPMSPrev [4][5] = {    //pres,temp,bat,sig,alarm
  {999,999,99,99,0},
  {999,999,99,99,0},
  {999,999,99,99,0},
  {999,999,99,99,0},  
};

/////////////////////////// FreeRTOS
TaskHandle_t BLETPMS;
void Task1code( void * parameter ){
  Serial << "BLE_TPMS Scanning is running on core " << xPortGetCoreID() << endl;
  for(;;){
     BLEScanResults scanResults = pBLEScan->start(BLEscantime, false);
  } 
}

static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    // Serial.print("Notify callback for characteristic ");
    // Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
    // Serial.print(" of data length ");
    // Serial.println(length);
  }
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  // int i;
  void onResult(BLEAdvertisedDevice Device){
    pServerAddress = new BLEAddress(Device.getAddress());
    bool known = false;
    bool Master = false;
    String ManufData = Device.toString().c_str();
    for (int i = 0; i < 4; i++) {
      if (strcmp(pServerAddress->toString().c_str(), knownAddresses[i].c_str()) == 0) {
        known = true;
        delete pServerAddress;
        if (known) {
          String instring=retmanData(ManufData, 0);

          if (CF == 0) {                                                              //METRIC
            TPMSNew[i][0] = round(returnData(instring, 8) / 1000.0);                    //Pres in Kpa
            TPMSNew[i][1] = round(returnData(instring, 12) / 100.0);                    //Temp in C°
          } else {                                                                    //IMPERIAL
            TPMSNew[i][0] = round((returnData(instring, 8) / 1000.0) / 6.89475729);     //Pres in PSI
            TPMSNew[i][1] = round(((returnData(instring, 12) / 100.0) * 1.8) + 32);     //Temp in F°
          }   

          TPMSNew[i][2] = round(returnBatt(instring));  //Battery
          TPMSNew[i][3] = round(Device.getRSSI());      //Signal         

          if (returnAlarm(instring)) {                  //1=ALARM, 0=NOALARM
            TPMSNew[i][4] = 1;
          } else {
            TPMSNew[i][4] = 0;
          }      
          
          if (!boot) {                                  //startup boot: Copies New to Prev
            for (int ti = 0; ti < 5; ti++) {
              TPMSPrev[i][ti] = TPMSNew[i][ti];         //copy New to Prev
            }
            UpdateALLTPMS();                            //Displays TPMS on first boot
          } else {
            for (int ti = 0; ti < 5; ti++) {            //Routine ops: comparison
              if (TPMSPrev[i][ti] != TPMSNew[i][ti]) {
                TPMSPrev[i][ti] = TPMSNew[i][ti];       //copy New to Prev (for next comparison)
                // newprint = 1;                           //Sets New Printing Flag
              }
            }
          }

          // PrintNEWTPMSReading();    //TESTING

          sprintf(TPMS_Pres,    "%d", TPMSPrev[i][0]);  //Pres
          sprintf(TPMS_Temp,    "%d", TPMSPrev[i][1]);  //Temp
          sprintf(TPMS_Battery, "%d", TPMSPrev[i][2]);  //Battery in %
          sprintf(TPMS_Signal,  "%d", TPMSPrev[i][3]);  //Signal in dB

          //SERIAL TEST PRINT TPMS DATA
            // String instring=retmanData(ManufData, 0);
            // Serial << instring << endl << "Device found: " << Device.getRSSI() << endl;
            // Tire Temperature in C°
            // Serial << "Temperature:     " << (returnData(instring,12)/100.0) << "C°" << endl;
            // Tire pressure in Kpa           
            // Serial << "Pressure Kpa:    " << (returnData(instring,8)/1000.0) << "Kpa" << endl;
            // // Tire pressure in Bar           
            // Serial << "Pressure Bar:    " << (returnData(instring,8)/100000.0) << "bar" << endl;
            // // Battery percentage             
            // Serial << "Battery:         " << (returnBatt(instring)) << "%" << endl;
            // if (returnAlarm(instring)) {Serial << "ALARM!" << endl;}
            // Device->getScan()->stop();
            // delay(500);

          pBLEScan->clearResults();  // delete results fromBLEScan buffer to release memory
        }//if known loop
      }//if loop - checks TMPS Address ID
    }//for loop
  }//onResult loop
};//class

void setup() {
  Serial.begin(115200);
  delay(500);
  // BLE Init
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  Serial << "BLE Ready to SCAN for TPMS" << endl;
  //FreeRTOS: using Core 0 for BLE Scanning
  xTaskCreatePinnedToCore(Task1code,"BLETPMS",20000,NULL,1,&BLETPMS,0);
  //HEAP Monitoring
  Serial << endl << "Total\tLargest\tSmall\tFREE\tUsed%" << endl;   //prints titles
}

void loop() {
  FragPrint();              //Measures HEAP
  if (!boot) {boot = 1;}
}


void FragPrint() {
  cTime = millis();
  if (cTime - pFTime > pFTime_interval) {           //interval check
    pFTime = cTime;
    double Used = 100 - (ESP.getMaxAllocHeap() * 100 / ESP.getHeapSize());
    if (ESP.getFreeHeap() != getFreeHeap_prev) {    //HEAP comparison
      getFreeHeap_prev = ESP.getFreeHeap();
      // Serial << endl << "Total\tLargest\tSmall\tFREE\tUsed%" << endl;
      Serial << ESP.getHeapSize() << "\t" << ESP.getMaxAllocHeap() << "\t" << ESP.getMinFreeHeap() << "\t" << getFreeHeap_prev << "\t" << Used << endl;
    }
  }
}
void UpdateALLTPMS()  {    ////////////////////////////////////// ACTUAL DISPLAY PRINTING
  int i,ti;
  for (i=0; i<4; i++) {
    for (ti=0; ti<5; ti++) {                      //copy New to Prev
      TPMSPrev[i][ti] = TPMSNew[i][ti];
    }
    sprintf(TPMS_Pres,    "%u", TPMSPrev[i][0]);  //Pres
    sprintf(TPMS_Temp,    "%u", TPMSPrev[i][1]);  //Temp
    sprintf(TPMS_Battery, "%u", TPMSPrev[i][2]);  //Battery in %
    sprintf(TPMS_Signal,  "%d", TPMSPrev[i][3]);  //Signal in dB
  }
}
void PrintNEWTPMSReading()  {    //Live Readings
  Serial  << endl << "PRINTNG NEW TPMS Sensor VALUES" << endl;
     for (int pi = 0; pi < 4; pi++) {   //TEST change "pi < 4" to 1
       switch (pi) {
         case 0:
          Serial << "    Front" << endl;
          break;
         case 1:
          Serial << "    Back" << endl;
          break;
         case 2:
          Serial << "    Left" << endl;
          break;
         case 3:
          Serial << "    Right" << endl;
          break;
       }
       Serial << "Pres   \t" << TPMSNew[pi][0] << endl
              << "Temp   \t" << TPMSNew[pi][1] << endl
              << "Battery\t" << TPMSNew[pi][2] << endl
              << "Signal \t" << TPMSNew[pi][3] << endl
              << "Alarm  \t" << TPMSNew[pi][4] << endl;
      }
}

//BLE Scanning Routines
String retmanData(String txt, int shift) {
  // Return only manufacturer data string
  int start=txt.indexOf("data: ")+6+shift;
  return txt.substring(start,start+(36-shift));  
}
byte retByte(String Data,int start) {
  // Return a single byte from string
  int sp=(start)*2;
  char *ptr;
  return strtoul(Data.substring(sp,sp+2).c_str(),&ptr, 16);
}
long returnData(String Data,int start) {
  // Return a long value with little endian conversion
  return retByte(Data,start)|retByte(Data,start+1)<<8|retByte(Data,start+2)<<16|retByte(Data,start+3)<<24;
}
int returnBatt(String Data) {
  // Return battery percentage
  return retByte(Data,16);
}
int returnAlarm(String Data) {
  // Return battery percentage
  return retByte(Data,17);
}
ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1344
load:0x40078000,len:13924
ho 0 tail 12 room 4
load:0x40080400,len:3600
entry 0x400805f0
BLE Ready to SCAN for TPMS
BLE_TPMS Scanning is running on core 0

Total   Largest Small   FREE    Used%
270280  90100   143816  145276  67.00
269592  90100   141948  142528  67.00
269512  90100   141576  142196  67.00
269480  90100   141576  142032  67.00
269512  90100   141576  142196  67.00
269432  90100   141312  141896  67.00
269416  90100   141312  141768  67.00
269432  90100   141312  141896  67.00
269576  90100   140872  142836  67.00
269496  90100   140872  142532  67.00
269416  90100   140872  142200  67.00
269384  90100   140872  141868  67.00
269416  90100   140872  142200  67.00
269256  90100   140872  141564  67.00
269176  90100   140680  141264  67.00
269128  90100   140680  140988  67.00
269176  90100   140680  141264  67.00
269096  90100   140432  140936  67.00
269064  90100   140432  140656  67.00
269096  90100   140432  140936  67.00
269032  90100   140180  140660  67.00
268984  90100   140180  140368  67.00
269032  90100   140180  140660  67.00
269016  90100   140180  140532  67.00
269032  90100   140180  140660  67.00
269000  90100   140180  140496  67.00
269032  90100   140180  140660  67.00
268984  90100   140180  140368  67.00
269032  90100   140180  140660  67.00
269016  90100   140180  140532  67.00
269032  90100   140180  140660  67.00
269016  90100   140180  140532  67.00
269032  90100   140180  140660  67.00
268984  90100   140180  140276  67.00
269032  90100   140180  140660  67.00
268968  90100   140180  140240  67.00
269032  90100   140180  140660  67.00
269000  90100   140180  140328  67.00
269032  90100   140180  140660  67.00
269000  90100   140180  140404  67.00
269032  90100   140180  140660  67.00
269016  90100   140180  140532  67.00
269032  90100   140180  140660  67.00
269016  90100   140180  140532  67.00
269032  90100   140180  140660  67.00
268952  90100   139824  140336  67.00
268872  90100   139512  140024  67.00
268840  90100   139512  139860  67.00
268872  90100   139512  140024  67.00
268840  90100   139512  139744  67.00
268872  90100   139512  140024  67.00
268792  90100   139068  139708  67.00
268712  90100   138872  139396  67.00
268696  90100   138872  139268  67.00
268712  90100   138872  139396  67.00
268632  90100   138580  139072  67.00
268600  90100   138580  138904  67.00
268632  90100   138580  139072  67.00
268616  90100   138580  138944  67.00
268632  90100   138580  139072  67.00
268616  90100   138580  138944  67.00
268632  90100   138580  139072  67.00
268552  90100   138376  138760  67.00
268536  90100   138276  138632  67.00
268552  90100   138276  138760  67.00
268536  90100   138276  138632  67.00
268552  90100   138276  138760  67.00
268520  90100   138164  138504  67.00
268552  90100   138164  138760  67.00
268520  90100   138164  138504  67.00
268552  90100   138164  138760  67.00
268536  90100   138164  138684  67.00
268552  90100   138164  138760  67.00
268520  90100   138164  138588  67.00
268552  90100   138164  138760  67.00
268408  90100   137552  138204  67.00
268424  90100   137552  138332  67.00
268392  90100   137552  138056  67.00
268424  90100   137552  138332  67.00

don't use the variable type String.
You can read about it here

use the SafeString-library instead
https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/docs/html/class_safe_string.html

best regards Stefan

Thank you for the suggestion! I'll check that out.

check this, in onResult()..
this gets deleted inside the for loop if it matches a known address..
i'd move the delete just outside of the for loop so it will always get freed..

good luck.. ~q

Thank you qubits-us: I have tried moving the delete pServerAddress, but get this error:

D:\Arduinofiles\MCINFO\MCInfo_v6e1_SubTest_v1\MCInfo_v6e1_SubTest_v1.ino:144:3: error: expected unqualified-id before 'delete'
   delete pServerAddress;
   ^~~~~~

Any suggestions?

Well that's strange..
I don't..

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    // int i;
    void onResult(BLEAdvertisedDevice Device) {
      pServerAddress = new BLEAddress(Device.getAddress());
      bool known = false;
      bool Master = false;
      String ManufData = Device.toString().c_str();
      for (int i = 0; i < 4; i++) {
        if (strcmp(pServerAddress->toString().c_str(), knownAddresses[i].c_str()) == 0) {
          known = true;
          //        delete pServerAddress;
          if (known) {
            String instring = retmanData(ManufData, 0);

            if (CF == 0) {                                                              //METRIC
              TPMSNew[i][0] = round(returnData(instring, 8) / 1000.0);                    //Pres in Kpa
              TPMSNew[i][1] = round(returnData(instring, 12) / 100.0);                    //Temp in C°
            } else {                                                                    //IMPERIAL
              TPMSNew[i][0] = round((returnData(instring, 8) / 1000.0) / 6.89475729);     //Pres in PSI
              TPMSNew[i][1] = round(((returnData(instring, 12) / 100.0) * 1.8) + 32);     //Temp in F°
            }

            TPMSNew[i][2] = round(returnBatt(instring));  //Battery
            TPMSNew[i][3] = round(Device.getRSSI());      //Signal

            if (returnAlarm(instring)) {                  //1=ALARM, 0=NOALARM
              TPMSNew[i][4] = 1;
            } else {
              TPMSNew[i][4] = 0;
            }

            if (!boot) {                                  //startup boot: Copies New to Prev
              for (int ti = 0; ti < 5; ti++) {
                TPMSPrev[i][ti] = TPMSNew[i][ti];         //copy New to Prev
              }
              UpdateALLTPMS();                            //Displays TPMS on first boot
            } else {
              for (int ti = 0; ti < 5; ti++) {            //Routine ops: comparison
                if (TPMSPrev[i][ti] != TPMSNew[i][ti]) {
                  TPMSPrev[i][ti] = TPMSNew[i][ti];       //copy New to Prev (for next comparison)
                  // newprint = 1;                           //Sets New Printing Flag
                }
              }
            }

            // PrintNEWTPMSReading();    //TESTING

            sprintf(TPMS_Pres,    "%d", TPMSPrev[i][0]);  //Pres
            sprintf(TPMS_Temp,    "%d", TPMSPrev[i][1]);  //Temp
            sprintf(TPMS_Battery, "%d", TPMSPrev[i][2]);  //Battery in %
            sprintf(TPMS_Signal,  "%d", TPMSPrev[i][3]);  //Signal in dB

            //SERIAL TEST PRINT TPMS DATA
            // String instring=retmanData(ManufData, 0);
            // Serial << instring << endl << "Device found: " << Device.getRSSI() << endl;
            // Tire Temperature in C°
            // Serial << "Temperature:     " << (returnData(instring,12)/100.0) << "C°" << endl;
            // Tire pressure in Kpa
            // Serial << "Pressure Kpa:    " << (returnData(instring,8)/1000.0) << "Kpa" << endl;
            // // Tire pressure in Bar
            // Serial << "Pressure Bar:    " << (returnData(instring,8)/100000.0) << "bar" << endl;
            // // Battery percentage
            // Serial << "Battery:         " << (returnBatt(instring)) << "%" << endl;
            // if (returnAlarm(instring)) {Serial << "ALARM!" << endl;}
            // Device->getScan()->stop();
            // delay(500);

            pBLEScan->clearResults();  // delete results fromBLEScan buffer to release memory
          }//if known loop
        }//if loop - checks TMPS Address ID
      }//for loop
      //free this here..
      delete pServerAddress;
    }//onResult loop
};//class

maybe you did something different??

Compiles here..

good luck.. ~q

You should never use naked new and delete in high-level code.
Use a std::unique_ptr instead.

In this case, however, why use a pointer at all? Should it be an optional<BLEAddress>?
And why is it global? It doesn't seem to be used outside of the onResult function.

PieterP.
Thank you for the comments. I'm still learning about BLE, callbacks, new/delete, etc.

Would it be possible to see an example of what you're talking about?

Thanks again!

~q: Good catch! My mistake. I put the delete instruction before the 'onResult' closing bracket. It's good now.
Thanks!

At first sight, there's no need for pointers or dynamic memory allocations for the device addresses.

@@ -6,7 +6,6 @@
 
 //Assignments
 BLEScan* pBLEScan;
-static BLEAddress *pServerAddress;
 
 // Variables
 int BLEscantime = 0;
@@ -62,14 +61,13 @@
 class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
   // int i;
   void onResult(BLEAdvertisedDevice Device){
-    pServerAddress = new BLEAddress(Device.getAddress());
+    BLEAddress serverAddress = Device.getAddress();
     bool known = false;
     bool Master = false;
     String ManufData = Device.toString().c_str();
     for (int i = 0; i < 4; i++) {
-      if (strcmp(pServerAddress->toString().c_str(), knownAddresses[i].c_str()) == 0) {
+      if (serverAddress.toString() == knownAddresses[i]) {
         known = true;
-        delete pServerAddress;
         if (known) {
           String instring=retmanData(ManufData, 0);
 

PieterP: Thank You! Your suggestions (and examples) work very well and appears (hopefully) to have fixed the Memory Leak I've been trying to figure out for the last week!
A Beer to You!

Thank you and Cheers,
Bwanna

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.