Inconsistency in the Connection Using BLE Extension

Hi there,

As of about a week ago my program has started having completely unreliable connection attempts. Where just before then I was successfully connecting and reading around 100 bytes per packet of data from my ESP32S3.

In the current situation of the app, about 1 in 20 attempts to try and connect to the ESP32 are successful and around 50% of those connections operate how they are expected to. The failed connection attempts return either a connection timeout or the status error code 133.

As far as troubleshooting goes:

  • using nRF connect, I can connect easily and read or write data to the ESP32 without any issues
  • I have used a separate BLE connection test app that connects with the same issues as my current application
  • downloading the apk file still results in the errors ive been getting through the companion
  • re-downloading the companion app or restarting my phone allows for a single successful connection.
  • I have another partner in this project and have yet to use their phone after this issue popped up but will soon.

Overall my program has become quite inconsistent and have been roadblocked on this issue for some time now.Any ideas would be greatly appreciated!
aia file:
BirdProject.aia (203.9 KB)


ESP32 code:
ESP32 Code:
BirdProjectIDE.ino (11.1 KB)

Before diving into your problem, here's your code on a velvet pillow for board users on their phones:

ino code
//Bluetooth and SD Card Communication Libraries
#include <SPI.h>
#include <SdFat.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>

// Sensor Communication Libraries
#include <Wire.h>
#include <ESP32Time.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_SSD1306.h>
#include "Adafruit_BME680.h"
#include "Adafruit_PM25AQI.h"

// Define SD card pins for HSPI on ESP32-S3 PICO
const int chipSelect = 4; // CS (Chip Select)
const int MOSI_PIN = 11; // GPIO for MOSI (Master Out Slave In)
const int MISO_PIN = 13; // GPIO for MISO (Master In Slave Out)
const int SCK_PIN = 14; // GPIO for SCK (Serial Clock)
int chunkSize = 512;  // Size of each chunk of data

// Create an SdFat object
SdFat SD;

//Creates an ESP32 RTC instance
ESP32Time rtc(0);

// BLE Service and Characteristic UUIDs
#define SERVICE_UUID        "13227b52-6f07-4f5b-83ff-7448412076d8"
#define CHARACTERISTIC_UUID "69d25972-9c52-4a88-8243-59033932827d"
#define CHARACTERISTIC_UUID2 "376506c3-8e21-4641-ac3c-6da7eb343a29"
#define CHARACTERISTIC_UUID3 "08a49d94-91d1-4b43-9fcb-eb8f49549cc8"

// Create BLE Server and Characteristic objects
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
BLECharacteristic* pCharacteristic2 = NULL;
BLECharacteristic* pCharacteristic3 = NULL;
BLE2902 *pBLE2902;
BLE2902 *pBLE2902_2;
BLE2902 *pBLE2902_3;

// Define BME680 I2C addresses
#define BMEIN_ADDR 0x76
#define BMEOUT_ADDR 0x77

void sendCSVData();
void SD_Setup();
void BmeSetup();
float AmbientSoundLevel();

// BME, RTC, & PMS objects
Adafruit_BME680 bmeIN(&Wire);
Adafruit_BME680 bmeOUT(&Wire);
Adafruit_PM25AQI aqi = Adafruit_PM25AQI();
PM25_AQI_Data PM25;

// I2C pins & config
int I2C_SDA = 37;
int I2C_SCL = 38;

// UART pins
int TX_PIN = 17;
int RX_PIN = 18;

//PMS5003 enable pins
const int enablePn = 7;

// ADC pin 
const int SEN_ADC = 1;

//Mosfet pin
const int digSwitch = 40;

//Error value for error readings
const float errorVal = -50;

//BLE variables
bool deviceConnected = false;
int fileCounter = 0;
bool liveUpdate = false;

class MyServerCallbacks: public BLEServerCallbacks {
  void onConnect(BLEServer* pServer) {
    deviceConnected = true;
  };

  void onDisconnect(BLEServer* pServer) {
    deviceConnected = false;
    delay(500);
    pServer->startAdvertising();
  }
};

// Callback class to handle BLE events
class MyCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    String pChar_value_stdstr = pChar->getValue();
    String value = String(pChar_value_stdstr.c_str());
    Serial.println(value);
    if (value == "CSVData") {
      Serial.println("CSVData request received.");
      //sendCSVData();
    }
  }
};

class LiveUpdateCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pChar) override {
    String pChar_value_stdstr = pChar->getValue();
    String value = String(pChar_value_stdstr.c_str());
    Serial.println(value);
    if (value == "LiveUpdate") {
      liveUpdate = true;
      Serial.println("Live Update On");
    }else if(value == "LiveUpdateOff"){
      liveUpdate = false;
      Serial.println("Live Update Off");
    }
  }
};


void setup() {
  pinMode(digSwitch, OUTPUT);
  pinMode(enablePn, OUTPUT);

  digitalWrite(digSwitch, HIGH);
  digitalWrite(enablePn, HIGH);

  // Initialize serial communication
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for serial port to connect. Needed for native USB port only
  }

  // Configure SPI bus
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, chipSelect);
  // Initialize SD card
  SD_Setup();

  // Initialize BLE
  BLEDevice::init("ESP32");

  //Creates Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  //Create BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_WRITE |
                      BLECharacteristic::PROPERTY_WRITE_NR |
                      BLECharacteristic::PROPERTY_NOTIFY
                    );
  
  pCharacteristic2 = pService->createCharacteristic(
                      CHARACTERISTIC_UUID2,
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_WRITE |
                      BLECharacteristic::PROPERTY_WRITE_NR
                    );
  
  pCharacteristic3 = pService->createCharacteristic(
                      CHARACTERISTIC_UUID3,
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_READ |
                      BLECharacteristic::PROPERTY_WRITE |
                      BLECharacteristic::PROPERTY_WRITE_NR
                    );
  

  //Appendeds callbacks to characteristic
  pBLE2902 = new BLE2902();
  pBLE2902->setNotifications(true);
  pCharacteristic->addDescriptor(pBLE2902);

  pBLE2902_2 = new BLE2902();
  pBLE2902_2->setNotifications(true);
  pCharacteristic2->addDescriptor(pBLE2902_2);

  pBLE2902_3 = new BLE2902();
  pBLE2902_3->setNotifications(true);
  pCharacteristic2->addDescriptor(pBLE2902_3);

  // After defining the desriptors, sets the callback functions
  pCharacteristic->setCallbacks(new MyCallbacks());

  pCharacteristic2->setCallbacks(new LiveUpdateCallbacks());

  //Starts Service
  pService->start();

  //Initiates advertising on BLEService
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x00);
  BLEDevice::startAdvertising();
  Serial.println("BLE service started.");

  //  I2C setup
  Wire.begin(I2C_SDA, I2C_SCL);

  // Establishes connection to both BME680s
  BmeSetup();
  
  // UART connection between ESP32 & PMS5003
  Serial1.begin(9600, SERIAL_8N1, TX_PIN, RX_PIN);
  if (! aqi.begin_UART(&Serial1)) { // connect to the sensor over hardware serial
    Serial.println("Could not find PM 2.5 sensor!");
    while (1) delay(10);
  }

  // Create or open the CSV file  
  File dataFile = SD.open("data.csv", FILE_WRITE);
  if (dataFile) {
    // Write CSV headers if file is new
    if (dataFile.size() == 0) {
      dataFile.println("Time,Ambient Noise(dbA),Temp In (C), Pressure In(hPa),Humidity In(%),Temp Out (C), Pressure Out(hPa),Humidity Out(%),pm10 Standard, pm25 Standard, pm100 Standard, pm10 Environmental,pm25 Environmental, pm100 Environmental, 03um particles, 05um particles, 10um particles, 25um particles, 50um particles, 100um particles");
      Serial.println("CSV file created with headers.");
    } else {
      Serial.println("CSV file opened.");
    }
    dataFile.close();
  } else {
    Serial.println("Error opening data.csv");
  }
  
  rtc.setTime(0,11,15,16,3,2025); //Temporary time Set

}

void loop() {
  File dataFile = SD.open("data.csv", FILE_WRITE);

  //Ambient Noise Data
  float dbLevel = errorVal;

  //BME680 Sensor Data
  float tempIN = errorVal;
  float pressIN = errorVal;
  float humidIN = errorVal;
  float tempOUT = errorVal;
  float pressOUT = errorVal;
  float humidOUT = errorVal;
  
  //PMS Sensor Data
  float pm10sta = errorVal;
  float pm25sta = errorVal;
  float pm100sta = errorVal;
  float pm10env = errorVal;
  float pm25env = errorVal;
  float pm100env = errorVal;
  float pmpart03 = errorVal;
  float pmpart05 = errorVal;
  float pmpart10 = errorVal;
  float pmpart25 = errorVal;
  float pmpart50 = errorVal;
  float pmpart100 = errorVal;


  if(dataFile){
    digitalWrite(digSwitch,HIGH);
    digitalWrite(enablePn,HIGH);
    delay(15000);
    if(deviceConnected){
      Serial.println("Device connected currently");
    }
    BmeSetup();
    String currentTime = rtc.getTime("%B %d %Y %A %H:%M:%S");
    dbLevel = AmbientSoundLevel();

    if (bmeIN.performReading() && bmeOUT.performReading()){
      tempIN = bmeIN.temperature;
      pressIN = bmeIN.pressure / 100.0;
      humidIN = bmeIN.humidity;
      tempOUT = bmeOUT.temperature;
      pressOUT = bmeOUT.pressure / 100;
      humidOUT = bmeOUT.humidity;
    }else{
      Serial.print("Failed BME Readings");
    }

    if (aqi.read(&PM25)) {
      //Concentration Units Standard
      pm10sta = PM25.pm10_standard;
      pm25sta = PM25.pm25_standard;
      pm100sta = PM25.pm100_standard;

      //Concentration Units Environmental
      pm10env = PM25.pm10_env;
      pm25env =PM25.pm25_env;
      pm100env = PM25.pm100_env;

      //Particles
      pmpart03 = PM25.particles_03um;
      pmpart05 = PM25.particles_05um;
      pmpart10 = PM25.particles_10um;
      pmpart25 = PM25.particles_25um;
      pmpart50 = PM25.particles_50um;
      pmpart100 = PM25.particles_100um;
    }else{
      Serial.println("Particulate Matter Sensor Reading Failed");
    }

    float sensorData[] = {dbLevel, tempIN, pressIN, humidIN, tempOUT, pressOUT, humidOUT, pm10sta, pm25sta, pm100sta, pm10env, pm25env, pm100env, pmpart03, pmpart05, pmpart10, pmpart25, pmpart50, pmpart100};

    String dataString = "";
    dataString += currentTime;
    dataString += ",";
    for (int i = 0; i < sizeof(sensorData) / sizeof(sensorData[0]); i++) {
      dataString += String(sensorData[i]);
      if (i < sizeof(sensorData) / sizeof(sensorData[0]) - 1) {
        dataString += ",";
      }
    }
    dataString += "\n";
    if(!liveUpdate){
      dataFile.print(dataString);
      dataFile.flush();
      dataFile.close();

      Serial.println("Data Written to SD:");
      Serial.print(dataString);
    }else{
      Serial.println("Data Written to Live Update:");
      Serial.print(dataString);
      pCharacteristic3->setValue(dataString);
      pCharacteristic3->notify();
    }
    

    }else{
      Serial.println("Error Opening Data File");
  }

  digitalWrite(enablePn,LOW);
  digitalWrite(digSwitch,LOW);
  delay(10000);
  
}

void SD_Setup(){
  bool connectionTest = true;
  int attempts = 0;
  Serial.println("Initializing SD card...");
  while(connectionTest){
    if (!SD.begin(chipSelect, SPI_HALF_SPEED)) {
      Serial.println("SD card initialization failed!");
      delay(2000);
      attempts += 1;
    }
    else{
      connectionTest = false;
    }
    if(attempts >= 5){
      Serial.println("SD card initialization failed after 5 attempts, ending program");
      while(1);
    }

  }
  Serial.println("SD card initialized.");
}

void BmeSetup(){

  bool check = true;
  while (check){
    if (!bmeIN.begin(BMEIN_ADDR)) {
      Serial.println("Could not find a valid BME680 sensor, check wiring!");
      check = true;
      delay(2000);
    }
    else{
      check = false;
      bmeIN.begin(BMEIN_ADDR);
    }
  }

  check = true;
  while (check){
    if (!bmeOUT.begin(BMEOUT_ADDR)) {
      Serial.println("Could not find a valid outside BME680 sensor, check wiring!");
      check = true;
      delay(2000);
    }
    else{
      check = false;
      bmeOUT.begin(BMEOUT_ADDR);
    }
  }
  bmeIN.setTemperatureOversampling(BME680_OS_8X);
  bmeIN.setHumidityOversampling(BME680_OS_2X);

  bmeOUT.setTemperatureOversampling(BME680_OS_8X);
  bmeOUT.setHumidityOversampling(BME680_OS_2X);
}

float AmbientSoundLevel(){
  float ADC = analogRead(SEN_ADC);
  float voltageValue = ADC / 4096 * 3.3;
  float dbValue = voltageValue * 50;
  Serial.println(dbValue);
  return dbValue;
}

Your blocks, Cleaned Up

Analysis:

Your BLE extension is current, and there are no other extensions to worry about.

Your string handling is sketchy:

stringValues is already a list, so treating it like a piece of text is redundantly duplicative (unless you are sending commas, which I did not check.)

Wild guesses:

Things that can go bad on their own:

  • low battery somewhere, due to time/temperature
  • Android drops app permissions (unlikely to be intermittent, though)
  • noisy BLE radio environment (neighbor got into WiFI6?)
  • things that pile up somewhere (a BLE connection table?)
  • channel conflicts

I'm stopping here, hoping those with more practical experience will chime in.