Hi all,
I am working on a personal RC (remote control) project with Teensy 3.6, datalogging information to the builtin micro SD card. I have encountered a small problem I need to try and fine a way to solve. The datalogger is writing approximately 40 KB of data per second (so roughly 2.5 MB per minute) just to put into context how much data is being written - not a huge amount. It is working very well, however there is one small problem I have noticed, every 20 to 30 seconds there is a very momentary 'block' in execution which lasts roughly 300 to 500ms from what I can tell. I can only imagine there is some sort of flushing going on, or some other issue that is causing execution flow to momentarily pause. This 'blocking' doesn't happen if I disable the SD datalogger part of the update routine.
It is quite essential that execution flow is not blocked for the system to be responsive (as you can imagine for a remote control aircraft). I have included the relevant parts of the code below. Essentially the Initialise() function is called at the start and then 20 times per second the Output() function is called to write new data to the log file.
If anyone can provide some assistance, or advice how to improve the code that would be greatly appreciated. The Micro SD card is a Sandisk Ultra 16GB class 10 card.
Thank you very much in advance
I am working on a personal RC (remote control) project with Teensy 3.6, datalogging information to the builtin micro SD card. I have encountered a small problem I need to try and fine a way to solve. The datalogger is writing approximately 40 KB of data per second (so roughly 2.5 MB per minute) just to put into context how much data is being written - not a huge amount. It is working very well, however there is one small problem I have noticed, every 20 to 30 seconds there is a very momentary 'block' in execution which lasts roughly 300 to 500ms from what I can tell. I can only imagine there is some sort of flushing going on, or some other issue that is causing execution flow to momentarily pause. This 'blocking' doesn't happen if I disable the SD datalogger part of the update routine.
It is quite essential that execution flow is not blocked for the system to be responsive (as you can imagine for a remote control aircraft). I have included the relevant parts of the code below. Essentially the Initialise() function is called at the start and then 20 times per second the Output() function is called to write new data to the log file.
If anyone can provide some assistance, or advice how to improve the code that would be greatly appreciated. The Micro SD card is a Sandisk Ultra 16GB class 10 card.
Thank you very much in advance
Code:
#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
//
// Initialise
//
void DataLogger::Initialise() {
if (gc->DATALOG_ENABLED) {
serialdebug->OutputEarlyMessage(String("SD Datalogger: initialising\n"));
// Attempt to initialise the card (BUILTIN_SDCARD)
if (!SD.begin(gc->DATALOG_SD_CHIP_SELECT)) {
serialdebug->OutputEarlyMessage(String("SD Datalogger: card not present or unable to read.\n"));
return;
}
serialdebug->OutputEarlyMessage(String("SD Datalogger: card initialised.\n"));
// Find a free file
datafile = "";
uint8_t strLen = 0;
for (uint32_t i = 0; i < gc->DATALOG_SD_MAX_LOGFILES; i++) {
datafile = String(i + 1) + String(".txt");
strLen = datafile.length() + 1;
char charBuf[strLen];
datafile.toCharArray(charBuf, strLen);
if (!SD.exists(charBuf)) {
serialdebug->OutputEarlyMessage(String("SD Datalogger: outputting log data to ") + String(datafile) + String("\n"));
initialised = HIGH;
break;
}
}
if (!initialised) {
serialdebug->OutputEarlyMessage(String("SD Datalogger: maximum number of log files reached or unable to read filesystem.\n"));
return;
}
serialdebug->OutputEarlyMessage(String("SD Datalogger: initialisation successful\n"));
// Mark
mark_out = LOW;
mark_count = 0;
marker = 0;
// Output header
this->OutputHeader();
}
}
//
// Outputs all logged data to the SD card
//
void DataLogger::Output(uint32_t cycle_time, uint32_t cpu_timer, uint32_t perf_cycles) {
// Output latest datalog data to the SD card
// Don't attempt to perform any operations unless the card is initialised and datalogging is enabled
if (initialised && gc->DATALOG_ENABLED) {
sd_file = this->OpenFile();
if (!sd_file) {
// Error while opening the datalog file
this->DisableDatalogging();
return;
}
// Log marking
if (mark_out) {
marker = 1;
mark_count += 1;
if (mark_count >= _DATALOG_MARK_COUNT) {
// Disable mark, counter is reset on next event
mark_out = LOW;
}
}
else {
marker = 0;
}
String t_str; // Temporary working string
// System
t_str = String("SYS,") + String(cycle_time, DEC) + String(",") + String(cpu_timer, DEC) + String(",") + String(perf_cycles, DEC);
t_str = t_str + String("\r\n");
// Input PWMs
t_str = t_str + String("RX_PWM,");
t_str = t_str + String(marker, DEC);
for (uint8_t i = 0; i < _RX_NUM_CHANNELS; i++) {
t_str = t_str + String(",") + String(rx->GetPWM(i), DEC);
}
t_str = t_str + String("\r\n");
// Input PWM PCT
t_str = t_str + String("RX_PCT,");
t_str = t_str + String(marker, DEC);
for (uint8_t i = 0; i < _RX_NUM_CHANNELS; i++) {
t_str = t_str + String(",") + String(rx->GetPWM_PCT(i), 4);
}
t_str = t_str + String("\r\n");
// SBUS raw
#ifdef _RX_USE_SBUS
float *chdata = rx->GetSBUSChRaw();
t_str = t_str + String("SBUS,");
t_str = t_str + String(marker, DEC);
for (uint8_t i = 0; i < _RX_NUM_CHANNELS; i++) {
t_str = t_str + String(",") + String(chdata[i], 4);
}
t_str = t_str + String("\r\n");
#endif // _RX_USE_SBUS
// Engine data
t_str = t_str + String("ENG,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(LEngine->GetRPM(), DEC) + String(",") + String(REngine->GetRPM(), DEC) + String(",");
t_str = t_str + String(LEngine->GetRPMAcqErrorFlag(), DEC) + String(",") + String(REngine->GetRPMAcqErrorFlag(), DEC) + String(",");
t_str = t_str + String(LEngine->GetCMD_PWMPct(), 3) + String(",") + String(REngine->GetCMD_PWMPct(), 4) + String(",");
t_str = t_str + String(LEngine->GetSyncTrim(), DEC) + String(",") + String(REngine->GetSyncTrim(), DEC) + String(",");
t_str = t_str + String(LEngine->GetOutPWM(), DEC) + String(",") + String(REngine->GetOutPWM(), DEC);
t_str = t_str + String("\r\n");
// Flight Management
t_str = t_str + String("FM,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(fm->GetFlightMode(), DEC) + String(",");
t_str = t_str + String(fm->GetSystemStatus(), DEC) + String(",");
t_str = t_str + String(fm->GetATHRStatus(), DEC) + String(",");
t_str = t_str + String(fm->GetSYNCStatus(), DEC) + String(",");
t_str = t_str + String(fm->GetGearDown(), DEC) + String(",");
t_str = t_str + String(fm->GetFailsafe(), DEC) + String(",");
t_str = t_str + String(fm->GetFlightPhase(), DEC) + String(",");
t_str = t_str + String(fm->GetDataSourceState(_FM_DATA_I2C_PROX), DEC) + String(",");
t_str = t_str + String(fm->GetDataSourceState(_FM_DATA_I2C_RPM_L), DEC) + String(",");
t_str = t_str + String(fm->GetDataSourceState(_FM_DATA_I2C_RPM_R), DEC) + String(",");
t_str = t_str + String(datalink->GetLimCount(_FM_DATA_I2C_PROX), DEC) + String(",");
t_str = t_str + String(datalink->GetLimCount(_FM_DATA_I2C_RPM_L), DEC) + String(",");
t_str = t_str + String(datalink->GetLimCount(_FM_DATA_I2C_RPM_R), DEC);
t_str = t_str + String("\r\n");
// Prop Syncho
t_str = t_str + String("SYNC,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(fm->GetSYNCStatus(), DEC) + String(",");
t_str = t_str + String(fm->GetPropSyncPIDDuty(), DEC) + String(",");
t_str = t_str + String(LEngine->GetSyncTrim(), DEC) + String(",");
t_str = t_str + String(REngine->GetSyncTrim(), DEC) + String(",");
t_str = t_str + String(LEngine->GetSyncTrimOvershoot(), DEC) + String(",");
t_str = t_str + String(REngine->GetSyncTrimOvershoot(), DEC);
t_str = t_str + String("\r\n");
// ASI
t_str = t_str + String("ASI,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(asi->GetSpeedKt(), 3) + String(",");
t_str = t_str + String(asi->GetSpeedMs(), 3) + String(",");
t_str = t_str + String(asi->GetADCRaw(), DEC) + String(",");
t_str = t_str + String(asi->GetVRaw(), 4) + String(",");
t_str = t_str + String(asi->GetPRaw(), DEC);
t_str = t_str + String("\r\n");
// Fuel
t_str = t_str + String("FUEL,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(fuelm->GetTankState(), DEC) + String(",");
t_str = t_str + String(fuelm->GetLowSensor(), DEC) + String(",");
t_str = t_str + String(fuelm->GetHighSensor(), DEC);
t_str = t_str + String("\r\n");
// Proximity
t_str = t_str + String("PROX,");
t_str = t_str + String(marker, DEC) + String(",");
t_str = t_str + String(datalink->GetProximity(), DEC) + String(",");
t_str = t_str + String(fm->GetProxOnGround(), DEC);
t_str = t_str + String("\r\n");
// Write line to file
this->WriteLine(t_str);
// Close the file
sd_file.close();
}
}