So I've recently started a project where I am using an accelerometer, along with a SD card breakout board. I've been able to get information to write properly to the SD card with no issues whatsoever. However, my speed is a bit lower than I would like, at about only 40Hz or so, where my accelerometer can log data at 260Hz (MPU-6050). Each reading contains an X, Y, Z, and timestamp value since recording started. Each reading is output into a .txt file for later separation via comma-separated-value sorting in Excel(I wish) or GNUplot, which handles the massive sample size much better.
I've referenced https://forum.arduino.cc/t/how-to-write-data-with-high-sampling-rates-to-a-sd-card/281496/9 to try and figure this out, but I couldn't get much out of it. I don't understand arrays very well beyond the fact they are just tables like in excel, and can be set up in 1, 2, 3, or more dimensions.
I've read around a bit and it seems my problem is that I'm closing and opening the file each and every write cycle. However, I don't know how to get out of doing this as the program seems to not run properly if I don't close out the file or if I use flush. My general idea of how this could flow without opening/close cycles every single time would be:
- Create arrays for each variable (X, Y, Z, elapsedTime)
- Take all my recordings for a given time, maybe five-hundred readings or so which would come out to about 12 seconds, or whatever possible value I can squeeze out of my Atmega328P and maintain program stability, and shove them into their respective arrays. Ideally the longer the better, but I understand I have memory constraints.
- In one write-cycle, I write all the values into CSV form, or even multiple print cycles cycling through each array cell continuously until all the values are input with the proper format.
- Clear the arrays of all the info they held, and return to step 2.
Really appreciate any help you guys can offer. Thank you!
//
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
int recordStatus = false; //Default recordStatus is zero.
int recordingLed = 3;
const int chipSelect = 10;
Adafruit_MPU6050 mpu;
void setup(void) {
pinMode(2, INPUT_PULLUP);
pinMode(3, OUTPUT);
Serial.begin(500000);
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
//SD CARD WRITE CODE BEGIN
{
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
}
//SD CARD WRITE CODE END
Serial.println("Adafruit MPU6050 test!"); //MPU6050 ACCELEROMETER INITIALIZATION CODE BEGIN
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
break;
case MPU6050_RANGE_4_G:
break;
case MPU6050_RANGE_8_G:
break;
case MPU6050_RANGE_16_G:
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
break;
case MPU6050_RANGE_500_DEG:
break;
case MPU6050_RANGE_1000_DEG:
break;
case MPU6050_RANGE_2000_DEG:
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_260_HZ);
Serial.print("Filter bandwidth set to: ");
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println("260 Hz");
break;
case MPU6050_BAND_184_HZ:
Serial.println("184 Hz");
break;
case MPU6050_BAND_94_HZ:
Serial.println("94 Hz");
break;
case MPU6050_BAND_44_HZ:
Serial.println("44 Hz");
break;
case MPU6050_BAND_21_HZ:
Serial.println("21 Hz");
break;
case MPU6050_BAND_10_HZ:
Serial.println("10 Hz");
break;
case MPU6050_BAND_5_HZ:
Serial.println("5 Hz");
break;
}
//MPU6050 ACCELEROMETER INITIALIZATION CODE END
}
unsigned long startMillis;
unsigned long currentMillis;
unsigned long elapsedTime;
int rawMillis;
void loop() {
digitalWrite(recordingLed, LOW);
int buttonRecord = digitalRead(2); //Check button for GND input
/* Get new sensor events with the readings */
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
// make a string for assembling the data to log: //SD CARD VALUES ASSIGN BEGIN
int X = "";
int Y = "";
int Z = "";
String timeofRecord = String(millis()/1000.0, 10);
X = (a.acceleration.x/9.81);
Y = (a.acceleration.y/9.81);
Z = (a.acceleration.z/9.81);
if(buttonRecord == false){ //Toggle record status upon pin 2 going to GND
delay(500);
recordStatus = !recordStatus; //If button is pressed, make recordStatus switch from true to false.
startMillis = millis();
}
if(recordStatus == true){ //While record is toggled, do below
rawMillis = millis()-startMillis;
String elapsedTime = String(rawMillis/1000.00, 3);
File dataFile = SD.open("datalog.txt", FILE_WRITE);
if (dataFile) { // if the file is available, write to it:
digitalWrite(recordingLed, HIGH);
dataFile.print(elapsedTime);
dataFile.print(", ");
dataFile.print(X);
dataFile.print(", ");
dataFile.print(Y);
dataFile.print(", ");
dataFile.print(Z);
dataFile.println("");
dataFile.close();
}
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
UPDATE
So I've been messing with this a while, and I believe I just about have it figured out. I have arrays set up for time, X, Y, and Z and they are populating values correctly. However, I can't seem to get into my SD card file. There is no issue when the setup initializes, it's like it skips the
if (dataFile){
entirely. The modified code is below here. There's no shutdowns, and the program loops continuously so I don't think I'm running out of memory or having any power issues. The red LED on the SD card is flashing as though it's being written. Even if I remove the if(dataFile) check and force the commands contained in the if statement to execute, my txt file is still left empty. Previously I was writing to this just fine. Memory problem? I have less than 500 bytes of memory available. Possibly this is the cause? Or is it the for loop causing my trouble?
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
int recordStatus = false; //Default recordStatus is zero.
int recordingLed = 3;
const int chipSelect = 10;
Adafruit_MPU6050 mpu;
void setup(void) {
pinMode(2, INPUT_PULLUP);
pinMode(3, OUTPUT);
Serial.begin(500000);
while (!Serial)
delay(10);
//SD CARD WRITE CODE BEGIN
{
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
}
//SD CARD WRITE CODE END
Serial.println("Adafruit MPU6050 test!"); //MPU6050 ACCELEROMETER INITIALIZATION CODE BEGIN
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_4_G);
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
break;
case MPU6050_RANGE_4_G:
break;
case MPU6050_RANGE_8_G:
break;
case MPU6050_RANGE_16_G:
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
break;
case MPU6050_RANGE_500_DEG:
break;
case MPU6050_RANGE_1000_DEG:
break;
case MPU6050_RANGE_2000_DEG:
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_260_HZ);
Serial.print("Filter bandwidth set to: ");
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println("260 Hz");
break;
case MPU6050_BAND_184_HZ:
Serial.println("184 Hz");
break;
case MPU6050_BAND_94_HZ:
Serial.println("94 Hz");
break;
case MPU6050_BAND_44_HZ:
Serial.println("44 Hz");
break;
case MPU6050_BAND_21_HZ:
Serial.println("21 Hz");
break;
case MPU6050_BAND_10_HZ:
Serial.println("10 Hz");
break;
case MPU6050_BAND_5_HZ:
Serial.println("5 Hz");
break;
}
//MPU6050 ACCELEROMETER INITIALIZATION CODE END
}
float startMillis;
float elapsedTime[3];
float rawMillis;
float X[3];
float Y[3];
float Z[3];
void loop() {
digitalWrite(recordingLed, LOW);
int buttonRecord = digitalRead(2); //Check button for GND input
/* Get new sensor events with the readings */
if(buttonRecord == false){ //Toggle record status upon pin 2 being pulled down to GND
delay(500);
recordStatus = !recordStatus; //If button is pressed, make recordStatus switch from true to false.
startMillis = millis();
Serial.print("BUTTON PRESSED");
}
if(recordStatus == true){ //While record is toggled, do below
digitalWrite(recordingLed, HIGH);
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
for(int arrayNumber = 0; arrayNumber <= 3; ++arrayNumber){
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
X[arrayNumber] = (a.acceleration.x); //SD CARD VALUES ASSIGN BEGIN/DO MATH TO CALCULATE G'S
Y[arrayNumber] = (a.acceleration.y);
Z[arrayNumber] = (a.acceleration.z);
rawMillis = millis()-startMillis;
elapsedTime[arrayNumber] = rawMillis;
Serial.println(elapsedTime[arrayNumber]);
Serial.println(X[arrayNumber]);
Serial.println(Y[arrayNumber]);
Serial.println(Z[arrayNumber]);
Serial.println(arrayNumber); // diagnostic for checking to make sure arrays are being populated properly
}
File dataFile = SD.open("datalog.txt", FILE_WRITE);
Serial.print("LEFT LOGGING LOOP, ENTERING WRITE LOOP");
if (dataFile) { // if the file is available, write to it
for(int arrayNumber = 0; arrayNumber <= 3; ++arrayNumber){
Serial.print("NOW INSIDE WRITE LOOP"); // confirmation of being inside the write loop, and writing data to the file
String(dataString) = (
elapsedTime[arrayNumber] + String(", " ) + X[arrayNumber] + String(", ") + Y[arrayNumber] + String(", ") + Z[arrayNumber] //Format data for output to the comma separated format for excel/gnuplot
);
dataFile.println(dataString);
}
dataFile.close();
Serial.println("WRITE LOOP DONE");
}
}
}