Batch Saving to SD Card Help

Hello!
I am hoping to get some help with adding functionality to my code to enable the data I am gathering to be saved in batches to the SD card to hopefully increase the speed at which I am able to gather data and save it. If this is not feasible with an SD card, please let me know any suggestions you may have.

Here is my code:

#include <Wire.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_MPL3115A2.h>
#include <SD.h>

// Initialize sensors
Adafruit_LIS3DH lis = Adafruit_LIS3DH();
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();

// Chip select pin for the SD card breakout board
const int chipSelect = 10;

File dataFile;

void setup(void) {
  Serial.begin(9600);

  // Initialize SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // Don't do anything more if the SD isn't working
    while (1)
      ;
  }

  // Initialize LIS3DH
  if (!lis.begin(0x18)) {
    Serial.println("Could not start LIS3DH");
    while (1)
      ;
  }
  lis.setRange(LIS3DH_RANGE_16_G);

  // Initialize MPL3115A2
  if (!baro.begin()) {
    Serial.println("Could not start MPL3115A2");
    while (1)
      ;
  }
}

void loop() {
  // Read sensor data and store it in variables
  sensors_event_t event;
  lis.getEvent(&event);
  float pressure = baro.getPressure();
  float altitude = baro.getAltitude();
  float temperature = baro.getTemperature();

  // Open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  dataFile = SD.open("datalog.txt", FILE_WRITE);

  // If the file is available, write to it:
  if (dataFile) {
    dataFile.print(event.acceleration.x);
    dataFile.print(event.acceleration.y);
    dataFile.print(event.acceleration.z);
    dataFile.print(pressure);
    dataFile.print(altitude);
    dataFile.println(temperature);
    dataFile.close();  // close the file
  } else {
    // if the file isn't open, pop up an error:
    Serial.println("error opening datalog.txt");
  }

  //delay(1); // Delay a second between readings. Definitely Slower than this.
}

This is a very common beginner mistake that creates several problems: it dramatically slows down the overall speed of writing data to the card, greatly increases the SD card error rate, and greatly increases the current draw.

Open the file once in setup(), and close it again only when you are all done collecting data. You might see 10X or more increase in writing speed.

The major drawback is that if the power fails before the file is closed, you lose all data written to the card.

So, for long term data collection, issue a datafile.flush() command to update the file, but only once an hour or day. That way, you lose only the data not saved since the last .flush() operation.

This project will be used to gather g-force data in a relatively small window of time. There is probably a better way to do this and I don’t know about. This is a good step in the direction I want to go though, so thank you!

You should have no problem saving data at a sample frequency between 10 and 100 Hz, if you don't open and close the file for every tidbit.

It is also possible to write binary data, which will be much faster and more compact than using datafile.print(). For example, collect an array of X,Y and Z values and write it out as one object, as follows. Read the data back in the similarly.

float a[3];
a[0]=event.acceleration.x;
a[1]=event.acceleration.y;
a[2]=event.acceleration.z;
datafile.write(a,12); //12 bytes total

I have tried to implement it. But I still get data points 1 second apart and I can't seem to get them any quicker.

#include <Wire.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_MPL3115A2.h>
#include <SD.h>

// Initialize sensors
Adafruit_LIS3DH lis = Adafruit_LIS3DH();
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();

// Chip select pin for the SD card breakout board
const int chipSelect = 10;

File dataFile;

unsigned long previousFlushMillis = 0;
const long flushInterval = 30000; // interval at which to flush data (30 seconds in milliseconds)

void setup(void) {
  Serial.begin(9600);

  // Initialize SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1);
  }

  // Initialize LIS3DH
  if (!lis.begin(0x18)) {
    Serial.println("Could not start LIS3DH");
    while (1);
  }
  lis.setRange(LIS3DH_RANGE_16_G);

  // Initialize MPL3115A2
  if (!baro.begin()) {
    Serial.println("Could not start MPL3115A2");
    while (1);
  }

  // Open the file once here instead of in the loop
  dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (!dataFile) {
    Serial.println("error opening datalog.txt");
    while (1);
  }

  // Write a startup message to the file with a timestamp
  dataFile.println("Startup time (ms): " + String(millis()));
  // No need to flush here since we're just starting up
}

void loop() {
  // Get the current time at the start of the loop
  unsigned long currentMillis = millis();
  
  // Flush data at specified intervals
  if (currentMillis - previousFlushMillis >= flushInterval) {
    previousFlushMillis = currentMillis; // update the last flush timestamp
    if (dataFile) {
      dataFile.flush(); // flush data to SD card
    }
  }

  // Read sensor data and store it in variables
  sensors_event_t event;
  lis.getEvent(&event); // Assuming this is non-blocking
  float pressure = baro.getPressure(); // Assuming this is non-blocking
  float altitude = baro.getAltitude(); // Assuming this is non-blocking
  float temperature = baro.getTemperature(); // Assuming this is non-blocking

  // If the file is open, write to it
  if (dataFile) {
    dataFile.print("Time (ms): ");
    dataFile.print(currentMillis);
    dataFile.print(", ");
    dataFile.print("Accel X: ");
    dataFile.print(event.acceleration.x);
    dataFile.print(", ");
    dataFile.print("Accel Y: ");
    dataFile.print(event.acceleration.y);
    dataFile.print(", ");
    dataFile.print("Accel Z: ");
    dataFile.print(event.acceleration.z);
    dataFile.print(", ");
    dataFile.print("Pressure: ");
    dataFile.print(pressure);
    dataFile.print(", ");
    dataFile.print("Altitude: ");
    dataFile.print(altitude);
    dataFile.print(", ");
    dataFile.print("Temp: ");
    dataFile.println(temperature);
    // Do not close the file; keep it open for the next write
  } else {
    // Handle the error once, then try to continue
    Serial.println("error writing to datalog.txt");
    dataFile = SD.open("datalog.txt", FILE_WRITE);
  }

  // No delay here, loop will run as fast as the sensor read and SD write will allow
}

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