Problem with datalogging to SD-Card

Yeah i found a way to close it just every 30 secs, which should be fine.
But a new problem occurred. There data i want to printToSd in void printEvent is not written to the SD.

const int SAMPLE_RATE = 100; //100 Hz
const int SAMPLE_TIME = 60; //60 Sekunden
const int NUMBER_OF_SAMPLES = SAMPLE_RATE * SAMPLE_TIME;
char data_array1[NUMBER_OF_SAMPLES];
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <SPI.h>
#include <SD.h>
#include <SdFat.h>
#include "SdFat.h"

const int chipSelect = 6; // CS Pin für SD

// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
//                                   id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28);

/* Set the delay between fresh samples */
uint16_t BNO055_SAMPLERATE_DELAY_MS = 0;

//SdFat_h sd;
File myFile;

int last_data_save = 0;

const char dataFile[] = "test1.txt";

void setup() {

  Serial.begin(19200);
  Serial.println("Orientation Sensor Test"); Serial.println("");
  pinMode(LED_BUILTIN, OUTPUT);
  // SD-Card Initialise
  Serial.print("Initializing SD card...");

  if (!SD.begin(28)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  /* Initialise the sensor */
  if (!bno.begin())
  {
    /* There was a problem detecting the BNO055 ... check your connections */
    Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    while (1);

  }
  delay(1000);
}

void loop(void)
{
  //could add VECTOR_ACCELEROMETER, VECTOR_MAGNETOMETER,VECTOR_GRAVITY...
  sensors_event_t orientationData , angVelocityData , linearAccelData, magnetometerData, accelerometerData, gravityData;
  bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER);
  bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE);
  bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL);
  bno.getEvent(&magnetometerData, Adafruit_BNO055::VECTOR_MAGNETOMETER);
  bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER);
  bno.getEvent(&gravityData, Adafruit_BNO055::VECTOR_GRAVITY);

  printEvent(&angVelocityData, &accelerometerData, &linearAccelData);

  uint8_t system, gyro, accel, mag = 0;
  bno.getCalibration(&system, &gyro, &accel, &mag);

  //Wenn die System-Kalibration auf 3 ist leuchtet eine LED
  if (system == 3) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void printEvent(sensors_event_t* angVelocityData, sensors_event_t* accelerometerData, sensors_event_t* linearAccelData) {
  double x = -1000000, y = -1000000 , z = -1000000; //dumb values, easy to spot problem

  unsigned long timestamp = millis();
  myFile.print(timestamp);
  Serial.print(timestamp);
  myFile.print(',');
  Serial.print(',');

  myFile.print(angVelocityData->gyro.x);
  Serial.print(angVelocityData->gyro.x);
  myFile.print(',');
  Serial.print(',');

  myFile.print(angVelocityData->gyro.y);
  myFile.print(',');
  Serial.print(angVelocityData->gyro.y);
  Serial.print(',');

  myFile.print(angVelocityData->gyro.z);
  myFile.print(',');
  Serial.print(angVelocityData->gyro.z);
  Serial.print(',');

  // linear Accelerometer (ohne g)

  myFile.print(linearAccelData->acceleration.x);
  Serial.print(linearAccelData->acceleration.x);
  myFile.print(',');
  Serial.print(',');

  myFile.print(linearAccelData->acceleration.y);
  myFile.print(',');
  Serial.print(linearAccelData->acceleration.y);
  Serial.print(',');

  myFile.print(linearAccelData->acceleration.z);
  myFile.print(',');
  Serial.print(linearAccelData->acceleration.z);

  myFile.print("\n");
  Serial.print("\n");

  // alle 30s Daten auf SD speichern
  if (millis() -  last_data_save >= 1000 * 30) {
    last_data_save = millis();
    writeStringToSD(data_array1, dataFile);
  }
  uint8_t system, gyro, accel, mag = 0;
  bno.getCalibration(&system, &gyro, &accel, &mag);
}

// ------------------ initializeSD ----------------------------------
void initializeSD() {
  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");

    Serial.println("NO SD!");
    Serial.println("Program stopped.");
    while (1) {
    }
    return;
  }
  Serial.println("Initialization done.");
}


// ------------------ writeStringToSD ----------------------------------
//Ein belibiger String wird in ein belibiges Files geschrieben

boolean writeStringToSD(const char *data, const char *test1)
{
  Serial.print("Writing to: ");
  Serial.println(test1);
  myFile = SD.open(test1, FILE_WRITE); //Öffnet file zum Lesen

  if (myFile) { //Wenn File erfolgreich geöffnet wurde
    myFile.println(data); //Schreibt Datenstring ans Ende des Files
  }
  else {
    Serial.println("Error while opening the file!");
    return false;
  }

  myFile.close(); //Schließt File (Daten werden gesichert)
  return true;
}

I would like to insert the construct from printEvent to the data below. Is there a easy way to send to layout down there?

xeonus:
Yeah i found a way to close it just every 30 secs, which should be fine.
But a new problem occurred. There data i want to printToSd in void printEvent is not written to the SD.

It looks like 'myFile' is not open when you call 'printEvent()' so it can't be written.

Oh yea you are right.

I think I solved the problem and wrote everythign in the void "printEvent"

But now new error: printEvent was not declared in this scope. And the first printEvent is highlighted. (I also commented it for better visability)

Tried to restart may pc but nothing changed.

In another code it works exaclty like that.. :confused:

I read something about creating prototypes.

But i am not sure how it wold work in my case. What printEvent would need.
float printEvent (); //didnt work
does it need void printEvent?

Any hints are welcome.

You are probably missing a closing brace somewhere.

"error: printEvent was not declared in this scope." sometimes means "error: printEvent failed to compile due to an error elsewhere.". Look at the other warnings and errors in the text box below your sketch. You can drag the boundary between the two boxes to make more room to see the messages.

Oh yeah now I saw, that there are some more errors hidden.

I changed now the structure of my code because it was a bit patchworky (if that's a word).

Now I am facing the problem to implement the 30 sec buffer.
I would like to store the sensor data (linear Accel and gyro) in an array for let's say 10 secs. When 10 secs are over and want the array to be written to the SD-Card (attached on the end).
After 10 secs the old array is deleted and a new one is starting with the latest data. Which is later added to the older data which is already on the SD-Card. In that way I want to keep the writing cycles low and optimize for the sample time.
But I can't get my head around this. I am more used to python and where "append" is a thing.
I know the forum is not made for this, but at this point I would be very glad if someone could just write me the 3 missing lines of code and I can research how it was done. Yeah not the best learning experience, but my deadline is lurking .. :o

Of course any other help is appreciated! I hope we are getting to an end haha.

Latest Code:

const int SAMPLE_RATE = 100; //100 Hz
const int SAMPLE_TIME = 60; //60 Sekunden
const int NUMBER_OF_SAMPLES = SAMPLE_RATE * SAMPLE_TIME;

//6000 Slots
char data_array1[NUMBER_OF_SAMPLES];

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <SPI.h>
#include <SD.h>
#include <SdFat.h>
#include "SdFat.h"


// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
//                                   id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28);

/* Set the delay between fresh samples */
uint16_t BNO055_SAMPLERATE_DELAY_MS = 0;

//SdFat_h sd;
File myFile;
const int chipSelect = 28; // CS Pin für SD

int last_data_save = 0;

const char dataFile[] = "test1.txt";


// ------------------ initializeSD&Sensor ----------------------------------
void setup(void)
{
  Serial.begin(19200);
  Serial.println("Orientation Sensor Test"); Serial.println("");
  pinMode(LED_BUILTIN, OUTPUT);
  // SD-Card Initialise
  Serial.print("Initializing SD card...");

  if (!SD.begin(28)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");

  /* Initialise the sensor */
  if (!bno.begin())
  {
    /* There was a problem detecting the BNO055 ... check your connections */
    Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    while (1);

  }
  delay(1000);
}

void loop(void)
{
  //could add VECTOR_ACCELEROMETER, VECTOR_MAGNETOMETER,VECTOR_GRAVITY...
  sensors_event_t orientationData , angVelocityData , linearAccelData, magnetometerData, accelerometerData, gravityData;
  bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER);
  bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE);
  bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL);
  bno.getEvent(&magnetometerData, Adafruit_BNO055::VECTOR_MAGNETOMETER);
  bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER);
  bno.getEvent(&gravityData, Adafruit_BNO055::VECTOR_GRAVITY);

  //append
  data_array1 += generate_data(&angVelocityData, &accelerometerData, &linearAccelData);  // how do i do this?

  // I want to buffer the sensor data in an array and store it to the SD-Card every X seconds. 
  // When this happens the buffer(array) should start from new with the latest values..

  if(millis() -  last_data_save >= SAMPLE_RATE * SAMPLE_TIME){
    last_data_save = millis();
    writeStringToSD(data_array1, dataFile);
  }

  uint8_t system, gyro, accel, mag = 0;
  bno.getCalibration(&system, &gyro, &accel, &mag);

  //Wenn die System-Kalibration auf 3 ist leuchtet eine LED
  if (system == 3) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

// ------------------ writeStringToSD ----------------------------------
//Ein belibiger String wird in ein belibiges Files geschrieben

char data_to_string(const char *data, const char *test1, float gyro_x, float gyro_y, float gyro_z, float lin_acc_x, float lin_acc_y, float lin_acc_z)
{
  unsigned long timestamp = millis();
  return timestamp + ',' + gyro_x + ',' + gyro_y + ',' + gyro_z + ',' + lin_acc_x + ',' + lin_acc_y + ',' + lin_acc_z + '\n';
}

boolean writeStringToSD(const char *data, const char *filename) {
  Serial.print("Writing to: ");
  Serial.println(filename);
  file = sd.open(filename, FILE_WRITE); //Öffnet file zum Lesen

  if (file) { //Wenn File erfolgreich geöffnet wurde
    file.println(data); //Schreibt Datenstring ans Ende des Files
  }
  else {
    Serial.println("Error while opening the file!");
    return false;
  }

  file.close(); //Schließt File (Daten werden gesichert)
  return true;
}

char generate_data(sensors_event_t* angVelocityData, sensors_event_t* accelerometerData, sensors_event_t* linearAccelData) {
  double x = -1000000, y = -1000000 , z = -1000000; //dumb values, easy to spot problem

  return data_to_string(data_array1, dataFile, angVelocityData->gyro.x, angVelocityData->gyro.y, angVelocityData->gyro.z, linearAccelData->acceleration.x, linearAccelData->acceleration.y, linearAccelData->acceleration.z);
}
const int SAMPLE_RATE = 100; //100 Hz
const int SAMPLE_TIME = 60; //60 Sekunden
const int NUMBER_OF_SAMPLES = SAMPLE_RATE * SAMPLE_TIME;

//6000 Slots
char data_array1[NUMBER_OF_SAMPLES];

A: That's a 60-second buffer, not a 10-second buffer.
B: I don't think that all of your data will fit in a single byte. Each entry in your buffer has to be big enough to hold all the data you want to store. An array of 'structures' is the usual solution.

struct DataItem
{
  sensors_event_t orientationData;
  sensors_event_t angVelocityData;
  sensors_event_t linearAccelData;
  sensors_event_t magnetometerData;
  sensors_event_t accelerometerData;
  sensors_event_t gravityData;
} DataBuffer[NUMBER_OF_SAMPLES];

I don't know how big a 'sensors_event_t' is but I would guess at least 6 bytes. That would make the struct at least 36 bytes. Six thousand of those would be 216000 bytes. Does the Arduino you are using have that much RAM?

yes right now the code says 60 secs. But I thought that there are some limitations waiting thats why i reduced it to 10 secs.

Using Arduino MKR Zero with 32 kB SRAM. Looks like i am missing a digit ^^.

But when we lets say reduce 60 to 8 secs. it should work with 32kB i think.. in theory.

I read about struct arrays that they are for dissimilar data. I thought it would be possible to just safe one line as a string. Is that not a good solution?

xeonus:
I thought it would be possible to just safe one line as a string. Is that not a good solution?

You only set aside room for one character for each of your 6000 samples. That is not enough space to hold a string. How long is each string going to be? Multiply your 6000 by that size to find out how much memory you need.

Oh okay. Yeah the lengths can vary, but 50 characters are enough for sure. 50* 6000 = 300 000. Would that stand for 300kb? If yes, I got a problem :frowning:

6000 samples in 1 min (100hz).
If we cut the time to 6 secs. We are at 30kb, which would be under my SRAM of 32kb.
Can we work with this?

I am totally fine with a smaller buffer and saving the data every 6 secs. I just dont wanna store every single file seperetly.

xeonus:
If we cut the time to 6 secs. We are at 30kb, which would be under my SRAM of 32kb.
Can we work with this?

Might be worth a try.

The Arduino UNO doesn't have enough RAM space to buffer massive amounts of data. Even switching to binary and saving only one timestamp and six floats does not allow more than 27 samples to fit in memory. If you try to fit more than 9 you will get a warning that available RAM is low. I think you need an Arduino with MUCH more RAM.

If you use an Arduino MEGA 2560 R3 you can fit up to 247 samples in memory (up to 173 before you get a warning).

#include <Adafruit_BNO055.h>
#include <SD.h>


// Buffering and writing in binary to save space and time.
const int BufferCount = 9;  // Largest I could set it without a warning on UNO.  You will need an Arduino with much more RAM.
struct // 28 bytes each
{
  unsigned long timestamp;
  float gx;
  float gy;
  float gz;
  float lx;
  float ly;
  float lz;
} Buffer[BufferCount];


int Count = 0;


/* Set the delay between fresh samples */
uint16_t BNO055_SAMPLERATE_DELAY_MS = 10;


// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
//                                   id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28);


// myFile=Datei für Beschreiben der SD Karte
File myFile;


void setup(void)
{
  Serial.begin(19200);
  delay(200); // Give Serial Monitor time to connect and reset the Arduino


  Serial.println();
  Serial.println("Orientation Sensor Test");
  Serial.println();


  pinMode(LED_BUILTIN, OUTPUT);


  // SD-Card Initialise
  Serial.print("Initializing SD card...");
  if (!SD.begin(28))
  {
    Serial.println("SD initialization failed!");
    while (1);
  }


  Serial.println("SD initialization done.");


  /* Initialise the sensor */
  if (!bno.begin())
  {
    /* There was a problem detecting the BNO055 ... check your connections */
    Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
    while (1);
  }
  delay(1000);


  // Kalibration
  static uint8_t system = 3;
  while (system == 3)
  {
    uint8_t gyro, accel, mag = 0;


    bno.getCalibration(&system, &gyro, &accel, &mag);


    //Wenn die System-Kalibration auf 3 ist leuchtet eine LED
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  digitalWrite(LED_BUILTIN, LOW);
}


void loop(void)
{
  sensors_event_t angVelocityData;
  sensors_event_t linearAccelData;


  bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE);
  bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL);


  Buffer[Count].timestamp = millis();
  Buffer[Count].gx = angVelocityData.gyro.x;
  Buffer[Count].gy = angVelocityData.gyro.y;
  Buffer[Count].gz = angVelocityData.gyro.z;
  Buffer[Count].lx = linearAccelData.acceleration.x;
  Buffer[Count].ly = linearAccelData.acceleration.y;
  Buffer[Count].lz = linearAccelData.acceleration.z;


  Count++;


  // If the buffer is full, write it to the file.
  if (Count >= BufferCount)
  {
    // erstellen eines bin files
    myFile = SD.open("test.bin", FILE_WRITE);
    if (myFile)
    {
      myFile.write((byte *)Buffer, sizeof Buffer);
    }
    Count = 0;
    myFile.close();
  }
}

Oh why just 27 samples?

I thought with using 50byte per sample and having 32kb i could store. Like at least 300 samples.

Thank you for your help with the buffer.

I don't have a MKR Zero to test on. You may be able to get 1000 samples on your board. Give it a try!

Thank you for your dedicated help :slight_smile:

I got it running with a max of 800 samples. A problem that is occuring is, that the fuller the RAM gets, the slower the sample rate gets.
As in the datasheet mentioned, the sample rate starts with 100hz and gets slower and slower to about 50 hz.

I need to discuss further with my supervisor what to da. I think a solution would be, to use another Microcontroller wiht more ram and maybe even better speeds. Like that Teensy 4.1.

For anyone interested here is my now working code to buffer the BNO055 euler angles and store it to a SD.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
#include <SPI.h>
#include <SD.h>
#include <avr/dtostrf.h>
#include "BNO055_support.h"
//#include <SdFat.h>
//#include "SdFat.h"

// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
//                                   id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28);

const int SAMPLE_RATE = 100; //100 Hz
const int SAMPLE_TIME = 1000 / SAMPLE_RATE; // Milisekunde
const char dataFileName[] = "test1.txt";

const int SAVE_TO_SD_TIME = 8; //Sekunden
const int NUMBER_OF_SAMPLES = SAMPLE_RATE * SAVE_TO_SD_TIME; //Wie viele Daten eingelesen werde bevor auf SD gespeichert

File file;
const int chipSelect = 28; // CS Pin für SD
int last_data_read = 0; //Speichert letztes Dateneinlesen
char dataString[16384]; //Buffert/Speichert Daten
int counter = 0; //Zählt wie oft Daten eingelesen

struct bno055_euler myEulerData; //Structure to hold the Euler data
struct bno055_t myBNO;

// ------------------ initializeSD&Sensor ----------------------------------
void setup(void)
{

  //Initialize I2C communication
  Wire.begin();

  //Initialization of the BNO055
  BNO_Init(&myBNO); //Assigning the structure to hold information about the device

  //Configuration to NDoF mode
  bno055_set_operation_mode(OPERATION_MODE_NDOF);

  delay(1);
  Serial.begin(19200);

  //Wartet dass Serialer Monitor geöffnet wird
  //Lösch mich für Feldmessungen
  while (!Serial) {}

  Serial.print("Number of Samples:");
  Serial.println(NUMBER_OF_SAMPLES);


  // SD-Card Initialise
  Serial.print("Initializing SD card...");
  if (!SD.begin(28)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");
}

void loop(void)
{
  //Einlesen und speichern
  if (millis() -  last_data_read >= SAMPLE_TIME) {
    last_data_read = millis();

    storeValues();
  }
}

//Ließt Werte ein und speichert alle x Werte nach y auf die SD
void storeValues() {
  counter = counter + 1;

  //Speichert Zeit
  int t = millis();
  char timestamp[16];
  itoa(t, timestamp, 10); //Konvertiert int to char[]

  //Speichert count
  char count_c[16];
  itoa(counter, count_c, 10);

  //converting orientation (x,y,z) as char
  //________________________________________________________________

  int x;
  int y;
  int z;
  x = (myEulerData.h / 16);
  y = (myEulerData.r / 16);
  z = (myEulerData.p / 16);

  char orientationX[6];
  itoa(x, orientationX, 10); //Konvertiert int to char[]

  char orientationY[6];
  itoa(y, orientationY, 10); //Konvertiert int to char[]

  char orientationZ[6];
  itoa(z, orientationZ, 10); //Konvertiert int to char[]

  //_____________________________________________________________________
  //Zusammenfügen der Werte zu dataString

  /* Get a new sensor event */
  bno055_read_euler_hrp(&myEulerData);

  strcat(dataString, count_c);
  strcat(dataString, ",");
  strcat(dataString, timestamp);
  strcat(dataString, ",");

  strcat(dataString, orientationX);
  strcat(dataString, ",");
  strcat(dataString, orientationY);
  strcat(dataString, ",");
  strcat(dataString, orientationZ);
  strcat(dataString, "\n");

  //Abspeichern auf SD nach NUMBER_OF_SAMPLES eingelesenen Daten
  if (counter >= NUMBER_OF_SAMPLES) {
    stopWatch(); //Startet Stoppuhr

    writeStringToSD(dataString, dataFileName); //Speichert auf SD

    Serial.println("DONE  ");

    //Zurücksetzen der Variablen
    memset(dataString, 0, sizeof(dataString)); //Speichert 0er in Datenarray
    counter = 0;

    stopWatch(); //Beendet Stoppuhr
  }
}

// ------------------ writeStringToSD ----------------------------------
//Ein belibiger String wird in ein belibiges Files geschrieben

boolean writeStringToSD(const char *data, const char *filename) {
  Serial.print("Writing to: ");
  Serial.println(filename);
  file = SD.open(filename, FILE_WRITE); //Öffnet file zum Lesen

  if (file) { //Wenn File erfolgreich geöffnet wurde
    file.println(data); //Schreibt Datenstring ans Ende des Files
  }
  else {
    Serial.println("Error while opening the file!");
    return false;
  }

  file.close(); //Schließt File (Daten werden gesichert)
  return true;
}

// ------------------ Stoppuhr ----------------------------------
boolean stopWatch_started = false;
int stopWatch_time;
void stopWatch() {
  if (stopWatch_started == false) {
    stopWatch_time = millis();
    stopWatch_started = true;
  }
  else {
    int duration = millis() - stopWatch_time;
    Serial.print("Duartion: ");
    Serial.print(duration);
    Serial.println(" ms");

    stopWatch_started = false;
  }
}

It works as intended, but I just noticed, that after about 4cycles it stops writing the SD.

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