RedIron:
Aren't there any beginner friendly solution which do not involve 500 line long code ?
I'll give it a short to show how you could architect the code and what it means:
here is a short piece of code which takes an existing buffer (crafted so that you can write a multiple of 10 bytes and it looks nice in the file) and tries to dump it to the SD card every 20ms (50Hz).
const uint8_t SD_CS_PIN = SS;
#include <SdFat.h> // https://github.com/greiman/SdFat
SdFat SD;
const unsigned long period = 20000ul; // in µs
const byte bufferSizeToDump = 70; // make it a multiple of 10 to get the \n (no more than 100)
const unsigned long acceptableError = period + 1000ul; // 1ms error
unsigned long chrono = 0;
unsigned long nbWrites = 0;
unsigned long nbErrors = 0;
unsigned long timeBucket = 0;
const char* logName = "log.txt";
File logFile;
char byteBuffer[] = "HELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\nHELLO YOU\n"; // 10 bytes x n
void setup() {
Serial.begin(115200);
// activate the SD card
if (!SD.begin(SD_CS_PIN)) {
Serial.println(F("SD Card error"));
while (true);
}
// open the file for logging
if (!logFile.open(logName, FILE_WRITE)) {
Serial.println(F("Can't create logfile"));
while (true);
}
// ready to roll
Serial.println("enter something in the console to stop the log");
Serial.flush();
logFile.flush();
delay(1000); // wait a bit before getting going
chrono = micros();
}
void loop() {
unsigned long deltaT = micros() - chrono;
if (deltaT > period) { // 50Hz
logFile.write(byteBuffer, bufferSizeToDump); // write our bytes
logFile.flush(); // dump to card
nbWrites++;
if (deltaT > acceptableError) {
nbErrors++;
Serial.print(nbWrites);
Serial.println("\t>1ms late");
}
chrono += period; // trying to keep up the pace, if OK with the lost time, you can try chrono = micros();
timeBucket += period - (micros() - chrono);
}
if (Serial.read() != -1) { // stop collection as soon as user enters something in the console
logFile.close();
Serial.print(F("Running at "));
Serial.print(1000000ul / period);
Serial.print(F("Hz, "));
Serial.print(nbWrites);
Serial.print(F(" writes, generated "));
Serial.print(nbErrors);
Serial.print(F(" errors and average time bucket: "));
Serial.print(timeBucket / nbWrites);
Serial.println(F(" µs. LOG STOPPED."));
while (true); // die here
}
}
There is a test to see if the dump is more than 1ms late against the expected period in which case I print how many lines were recorded till that point.
You'll notice that I barely do anything else than writing and flushing the 70 byte buffer - the only part of the loop() not related to the file is testing if there is something pending on the Serial line (to correctly close the file and stop logging) and a bit of statistics collection.
The timeBucket variable captures how many microSeconds you have at the end of the write before having to perform another buffer write/flush.
I ran this code on an Arduino Uno with a freshly formatted and empty 2Gb SD card (not UHS-II) that's what I saw on my Serial Monitor (@ 115200 bauds)
[color=purple]
enter something in the console to stop the log
283 >1ms late
284 >1ms late
561 >1ms late
751 >1ms late
1219 >1ms late
1220 >1ms late
1688 >1ms late
Running at 50Hz, 2012 writes, [color=red]generated 7 errors[/color] and [color=blue]average time bucket: 6958 µs[/color]. LOG STOPPED.
[/color]
so I'm skipping a beat from time to time (7 times on 2012 writes here) and can't keep up really at 50hz.
this tells you also that you have less than 7ms to complete your data collection on average
I then modified the code and tried writing only 10 bytes by changingconst byte bufferSizeToDump = 10; // make it a multiple of 10 to get the \nand let it run for a while and got
[color=purple]
enter something in the console to stop the log
996 >1ms late
1001 >1ms late
Running at 50Hz, 2163 writes, generated 2 errors and average time bucket: 7580 µs. LOG STOPPED.
[/color]
So although I did get a couple error, clearly writing less data helps and my time bucket has gained half a ms
I tried then the same code again with 70 bytes but at 25Hz by changing const unsigned long period = 40000ul; // in µsand I left it running for recording for a while and I never saw a report that I was more than 1ms late.
[color=purple]
enter something in the console to stop the log
Running at 25Hz, 2711 writes, generated 0 errors and average time bucket: 26961 µs. LOG STOPPED.
[/color]
so clearly with ~27ms in between two consecutive data dump, it's now plenty enough to avoid running into issues.
==> So - assuming your data collection is say less than 5ms - you could try to reduce the frequency of the write until you find a point where this is acceptable and also work to reduce the size of the data you need to dump as this helps too. Getting an UHS-II SD card might help, I did not have one readily available to test.
feel free to play with the code the two parameters you can change are right at the start
const unsigned long period = 20000ul; // in µs
const byte bufferSizeToDump = 70; // make it a multiple of 10 to get the \n (no more than 100)