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.