Combining SPIFFS and BLE

Hello everyone!
For a project for school, I'm checking temperature, TVOC, humidity and location using sensors. These variables get converted to std::string and send using ble. This worked perfectly.

Now I first save the variables using SPIFFS. Then I connect to my phone. At this point my "machine" should stop measuring and start reading in the file, line after line. Each line should be sent separately. However, I don't receive any data on my phone.

-> Is it known that SPIFFS and BLE don't work together?
-> How do I make sure I read a line, send it, and then do the same for the next line?

I look forward to hear from someone.

Welcome to the forum

Please post your sketch, using code tags when you do

Here's the code's summary:

if(beurt == 1)
  {
    Message = String(beurt).c_str() + std::string(",") + String(temp).c_str() + std::string(",") + String(druk).c_str() + std::string(",") + String(vocht).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 2;
  }
  else if(beurt == 2)
  {
    Message = String(beurt).c_str() + std::string(",") + String(CO2).c_str() + std::string(",") + String(TVOC).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 3;
  }
  else if(beurt == 3)
  {
    Message = String(beurt).c_str() + std::string(",") + String(latitude_decimal).c_str() + std::string(",") + String(NorS).c_str() + std::string(",") + String(longitude_decimal).c_str() + std::string(",") + String(EorW).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 1;
  }
  else if(beurt == 4)
  {
    beurt = 3;
  }

  // opslaan
  //appendStringToSPIFFS(filename, Message);
  
  // notify changed value
  if (deviceConnected)
  {
    beurt = 0;
    std::string dataRead = readStringFromSPIFFS(filename);  
    pCharacteristic->setValue(dataRead);
    pCharacteristic->notify();
    value++;
    delay(1000);
  }
  // disconnecting
  if (!deviceConnected && oldDeviceConnected)
  {
    delay(500); // give the bluetooth stack the chance to get things ready
    beurt = 1;
    pServer->startAdvertising(); // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }

and here are the voids for SPIFFS:

void appendStringToSPIFFS(String filename, std::string data)
{
  File file = SPIFFS.open(filename, "a");
  if (!file)
  {
    Serial.println("Failed to open file for appending");
    return;
  }
  //file.println(data);
  if (file.println(data.c_str()))
  {
    Serial.println("Data was appended");
  }
  else
  {
    Serial.println("Append failed");
  }
  file.close();
}

std::string readStringFromSPIFFS(String filename)
{
  File file = SPIFFS.open(filename, "r");
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return "";
  }
  
  std::string data;
  char c;
  while(file.available())
  {
    c = file.read();
    if(c == '\n')
    {
      break;
    }
  }
  file.close();
  Serial.println("has been read");
  return data;
}

Each sensor has it's own turn to work (called "beurt" in the code). When connected via BLE, beurt becomes 0, which makes sure the sensors won't measure anything, while things are getting send.

It would be better if you posted your full sketch so that your problem can be seen in context

Unless there's something else you haven't posted, this code will not work: as it's written, oldDeviceConnected never gets updated if it is false. The proper way to apply that pattern is to update the previous state, at every cycle, outside all conditions.
Not sure it is the bug you are experiencing, but surely will help :slightly_smiling_face:

First of all, I am sorry if the code is unclear.

/*
***How to connect***
*5V -> NEO-6M's VCC
*3.3V -> andere VCC
*16 -> NEO-6M's TX
*17 -> NEO-6M's RX
*21 -> SDA
*22 -> SCL
*GND -> GND & WAK
*/

// SPIFFS
#include <FS.h>;
#include <SPIFFS.h>;

// sensoren
unsigned long tijdstip;

#include <string>

int beurt;
String uitvoer;
String temp;
String druk;
String vocht;
String CO2;
String TVOC;

// CJMCU-811
#include <Adafruit_CCS811.h>
Adafruit_CCS811 ccs;

// BMP/BMP280
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

bool status;
Adafruit_BME280 bme;

// NEO-6M
#include <HardwareSerial.h>

String latitude;
String NorS; // north or south
String longitude; 
String EorW; // east or west
String foutmelding; // string for errors
float lat_degrees;
float lat_minutes;
float lon_degrees;
float lon_minutes;
float latitude_decimal;
float longitude_decimal;

HardwareSerial gpsSerial(2);

// Bluetooth communication
std::string Message;

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 0;

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"


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

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

String dataToWrite;
String filename = "/data.txt";

void setup()
{
  // put your setup code here, to run once:
  // BLE
  Serial.begin(115200);
  gpsSerial.begin(9600, SERIAL_8N1, 16, 17); // Begin seriële communicatie met de GPS-module

  // Create the BLE Device
  BLEDevice::init("bikelogger");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

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

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );

  // Create a BLE Descriptor
  pCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
  BLEDevice::startAdvertising();
  Serial.println("Waiting a client connection to notify...");

  // sensoren
  Serial.begin(115200);
  Wire.begin();
  Serial.println("Opstarten . . .");
  tijdstip = millis();
  //ss.begin(115200);

  status = bme.begin(0x76);  
  if (!status)
  {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  if(!ccs.begin() == true)
  {
    Serial.println("Failed to start sensor! Please check your wiring.");
    while(1);
  }
  beurt = 1;

  // Initialize SPIFFS
  if (!SPIFFS.begin(true))
  {
    Serial.println("An error occurred while mounting SPIFFS.");
    return;
  }
  Serial.println("SPIFFS mounted successfully.");
  
  Serial.println("temp,druk,vocht,CO2,TVOC");
}

void writeStringToSPIFFS(String filename, std::string data)
{
  File file = SPIFFS.open(filename, "w");
  if (!file)
  {
    Serial.println("Failed to open file for writing");
    return;
  }
  if (file.print(data.c_str()))
  {
    Serial.println("File was written");
  }
  else
  {
    Serial.println("Write failed");
  }
  file.close();
}

void appendStringToSPIFFS(String filename, std::string data)
{
  File file = SPIFFS.open(filename, "a");
  if (!file)
  {
    Serial.println("Failed to open file for appending");
    return;
  }
  //file.println(data);
  if (file.println(data.c_str()))
  {
    Serial.println("Data was appended");
  }
  else
  {
    Serial.println("Append failed");
  }
  file.close();
}

std::string readStringFromSPIFFS(String filename)
{
  File file = SPIFFS.open(filename, "r");
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return "";
  }
  
  std::string data;
  char c;
  while(file.available())
  {
    c = file.read();
    if(c == '\n')
    {
      break;
    }
  }
  file.close();
  Serial.println("has been read");
  return data;
}

void loop()
{
  if(beurt == 1) // BME280
  {
    temp = bme.readTemperature();
    druk = bme.readPressure() / 100.0F;
    vocht = bme.readHumidity();
  }
  else if(beurt == 2) // CJMCU-811
  {
    if(ccs.available() == true)
    {
      float temp = ccs.calculateTemperature();
      if(ccs.readData())
      {
        CO2 = ccs.geteCO2();
        TVOC = ccs.getTVOC();
        Serial.println(uitvoer);
      }
      else
      {
        while(1);
      }
    }
  }
  else if(beurt == 3) // NEO-6M
  {
    if (gpsSerial.available())
    { // Check if sensor is ready

      String gpsData = gpsSerial.readStringUntil('\n');

    // Check if the message starts with "$GPGGA" 
      if (gpsData.startsWith("$GPGGA"))
      {
        // Split string at each comma
        int index = 0;
        String data[15]; // there are 15 fields in a $GPGGA message
        while (gpsData.length() > 0)
        {
          int commaIndex = gpsData.indexOf(',');
          if (commaIndex > 0)
          {
            data[index] = gpsData.substring(0, commaIndex);
            gpsData = gpsData.substring(commaIndex + 1);
          }
          else
          {
            data[index] = gpsData; // Last field
            gpsData = "";
          }
          index++;
        }
    
        
        latitude = data[2];
        NorS = data[3]; // north or south
        longitude = data[4];
        EorW = data[5]; // east or west
        foutmelding = data[6]; 

        // Convert to decimal
        lat_degrees = latitude.substring(0, 2).toFloat();
        lat_minutes = latitude.substring(2).toFloat();
        lon_degrees = longitude.substring(0, 3).toFloat();
        lon_minutes = longitude.substring(3).toFloat();
    
        // Combine degrees and minutes
        latitude_decimal = lat_degrees + (lat_minutes / 60.0);
        longitude_decimal = lon_degrees + (lon_minutes / 60.0);

        tijdstip = millis();
      }
    }
    else if(millis() - tijdstip < 10000)
    {
      beurt = 4;
    }
    else
    {
      //Serial.println("request timed out");
    }
  }

  if(beurt == 1)
  {
    Message = String(beurt).c_str() + std::string(",") + String(temp).c_str() + std::string(",") + String(druk).c_str() + std::string(",") + String(vocht).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 2;
  }
  else if(beurt == 2)
  {
    Message = String(beurt).c_str() + std::string(",") + String(CO2).c_str() + std::string(",") + String(TVOC).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 3;
  }
  else if(beurt == 3)
  {
    Message = String(beurt).c_str() + std::string(",") + String(latitude_decimal).c_str() + std::string(",") + String(NorS).c_str() + std::string(",") + String(longitude_decimal).c_str() + std::string(",") + String(EorW).c_str();
    appendStringToSPIFFS(filename, Message);
    beurt = 1;
  }
  else if(beurt == 4)
  {
    beurt = 3;
  }

  // saving
  //appendStringToSPIFFS(filename, Message);
//THIS IS CURRENTLY DONE IN THE PREVIOUS IF ELSE BECAUSE OF DEBUGGING
  
  // notify changed value
  if (deviceConnected)
  {
    beurt = 0;
    std::string dataRead = readStringFromSPIFFS(filename);  
    pCharacteristic->setValue(dataRead);
    pCharacteristic->notify();
    value++;
    delay(1000);
  }
  // disconnecting
  if (!deviceConnected && oldDeviceConnected)
  {
    delay(500); // give the bluetooth stack the chance to get things ready
    beurt = 1;
    pServer->startAdvertising(); // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected)
  {
    // do stuff here on connecting
    oldDeviceConnected = deviceConnected;
  }
}

I hope my code isn't too disorganized.

Hello.
I meanwhile did find out that the problem happens in this method;

std::string readStringFromSPIFFS(String filename)
{
  File file = SPIFFS.open(filename, "r");
  if (!file)
  {
    Serial.println("Failed to open file for reading");
    return "";
  }
  
  std::string data;
  char c;
  while(file.available())
  {
    c = file.read();
    if(c == '\n')
    {
      break;
    }
  }
  file.close();
  Serial.println("has been read");
  return data;
}

It returns data, but If I check it by printing it in Serial monitor, it just prints an empty line.
Why does this happen?

Thanks for clearing it up for me!
In this function you define data, then you read the file one char at a time into c until you find a newline or EOF, then you return data - which is just sitting there waiting for some love. I think you're just missing an "add this c to data" bit :slight_smile:

Ah, yes, thank you!
This will help me for sure! I will test this out right away.
Once again thank you very much

1 Like

This was the solution. I thank you for your time.

1 Like