799 lines
29 KiB
C++
799 lines
29 KiB
C++
#include "globals.h"
|
|
#include BOARD_H
|
|
|
|
#ifdef SD_LOGGING
|
|
#include <SPI.h>
|
|
#ifdef __SD_H__
|
|
#include <SD.h>
|
|
#else
|
|
#include "SdFat.h"
|
|
#endif
|
|
#include "SD_logger.h"
|
|
#include "logger.h"
|
|
#include "rtc_common.h"
|
|
#include "maths.h"
|
|
|
|
//List of logger field names. This must be in the same order and length as logger_updateLogdataCSV()
|
|
constexpr char header_0[] PROGMEM = "secl";
|
|
constexpr char header_1[] PROGMEM = "status1";
|
|
constexpr char header_2[] PROGMEM = "engine";
|
|
constexpr char header_3[] PROGMEM = "Sync Loss #";
|
|
constexpr char header_4[] PROGMEM = "MAP";
|
|
constexpr char header_5[] PROGMEM = "IAT(C)";
|
|
constexpr char header_6[] PROGMEM = "CLT(C)";
|
|
constexpr char header_7[] PROGMEM = "Battery Correction";
|
|
constexpr char header_8[] PROGMEM = "Battery V";
|
|
constexpr char header_9[] PROGMEM = "AFR";
|
|
constexpr char header_10[] PROGMEM = "EGO Correction";
|
|
constexpr char header_11[] PROGMEM = "IAT Correction";
|
|
constexpr char header_12[] PROGMEM = "WUE Correction";
|
|
constexpr char header_13[] PROGMEM = "RPM";
|
|
constexpr char header_14[] PROGMEM = "Accel. Correction";
|
|
constexpr char header_15[] PROGMEM = "Gamma Correction";
|
|
constexpr char header_16[] PROGMEM = "VE1";
|
|
constexpr char header_17[] PROGMEM = "VE2";
|
|
constexpr char header_18[] PROGMEM = "AFR Target";
|
|
constexpr char header_19[] PROGMEM = "TPSdot";
|
|
constexpr char header_20[] PROGMEM = "Advance Current";
|
|
constexpr char header_21[] PROGMEM = "TPS";
|
|
constexpr char header_22[] PROGMEM = "Loops/S";
|
|
constexpr char header_23[] PROGMEM = "Free RAM";
|
|
constexpr char header_24[] PROGMEM = "Boost Target";
|
|
constexpr char header_25[] PROGMEM = "Boost Duty";
|
|
constexpr char header_26[] PROGMEM = "status2";
|
|
constexpr char header_27[] PROGMEM = "rpmDOT";
|
|
constexpr char header_28[] PROGMEM = "Eth%";
|
|
constexpr char header_29[] PROGMEM = "Flex Fuel Correction";
|
|
constexpr char header_30[] PROGMEM = "Flex Adv Correction";
|
|
constexpr char header_31[] PROGMEM = "IAC Steps/Duty";
|
|
constexpr char header_32[] PROGMEM = "testoutputs";
|
|
constexpr char header_33[] PROGMEM = "AFR2";
|
|
constexpr char header_34[] PROGMEM = "Baro";
|
|
constexpr char header_35[] PROGMEM = "AUX_IN 0";
|
|
constexpr char header_36[] PROGMEM = "AUX_IN 1";
|
|
constexpr char header_37[] PROGMEM = "AUX_IN 2";
|
|
constexpr char header_38[] PROGMEM = "AUX_IN 3";
|
|
constexpr char header_39[] PROGMEM = "AUX_IN 4";
|
|
constexpr char header_40[] PROGMEM = "AUX_IN 5";
|
|
constexpr char header_41[] PROGMEM = "AUX_IN 6";
|
|
constexpr char header_42[] PROGMEM = "AUX_IN 7";
|
|
constexpr char header_43[] PROGMEM = "AUX_IN 8";
|
|
constexpr char header_44[] PROGMEM = "AUX_IN 9";
|
|
constexpr char header_45[] PROGMEM = "AUX_IN 10";
|
|
constexpr char header_46[] PROGMEM = "AUX_IN 11";
|
|
constexpr char header_47[] PROGMEM = "AUX_IN 12";
|
|
constexpr char header_48[] PROGMEM = "AUX_IN 13";
|
|
constexpr char header_49[] PROGMEM = "AUX_IN 14";
|
|
constexpr char header_50[] PROGMEM = "AUX_IN 15";
|
|
constexpr char header_51[] PROGMEM = "TPS ADC";
|
|
constexpr char header_52[] PROGMEM = "Errors";
|
|
constexpr char header_53[] PROGMEM = "PW";
|
|
constexpr char header_54[] PROGMEM = "PW2";
|
|
constexpr char header_55[] PROGMEM = "PW3";
|
|
constexpr char header_56[] PROGMEM = "PW4";
|
|
constexpr char header_57[] PROGMEM = "status3";
|
|
constexpr char header_58[] PROGMEM = "Engine Protect";
|
|
constexpr char header_59[] PROGMEM = "";
|
|
constexpr char header_60[] PROGMEM = "Fuel Load";
|
|
constexpr char header_61[] PROGMEM = "Ign Load";
|
|
constexpr char header_62[] PROGMEM = "Dwell Requested";
|
|
constexpr char header_63[] PROGMEM = "Idle Target (RPM)";
|
|
constexpr char header_64[] PROGMEM = "MAP DOT";
|
|
constexpr char header_65[] PROGMEM = "VVT1 Angle";
|
|
constexpr char header_66[] PROGMEM = "VVT1 Target";
|
|
constexpr char header_67[] PROGMEM = "VVT1 Duty";
|
|
constexpr char header_68[] PROGMEM = "Flex Boost Adj";
|
|
constexpr char header_69[] PROGMEM = "Baro Correction";
|
|
constexpr char header_70[] PROGMEM = "VE Current";
|
|
constexpr char header_71[] PROGMEM = "ASE Correction";
|
|
constexpr char header_72[] PROGMEM = "Vehicle Speed";
|
|
constexpr char header_73[] PROGMEM = "Gear";
|
|
constexpr char header_74[] PROGMEM = "Fuel Pressure";
|
|
constexpr char header_75[] PROGMEM = "Oil Pressure";
|
|
constexpr char header_76[] PROGMEM = "WMI PW";
|
|
constexpr char header_77[] PROGMEM = "status4";
|
|
constexpr char header_78[] PROGMEM = "VVT2 Angle";
|
|
constexpr char header_79[] PROGMEM = "VVT2 Target";
|
|
constexpr char header_80[] PROGMEM = "VVT2 Duty";
|
|
constexpr char header_81[] PROGMEM = "outputs";
|
|
constexpr char header_82[] PROGMEM = "Fuel Temp";
|
|
constexpr char header_83[] PROGMEM = "Fuel Temp Correction";
|
|
constexpr char header_84[] PROGMEM = "Advance 1";
|
|
constexpr char header_85[] PROGMEM = "Advance 2";
|
|
constexpr char header_86[] PROGMEM = "SD Status";
|
|
constexpr char header_87[] PROGMEM = "EMAP";
|
|
constexpr char header_88[] PROGMEM = "Fan Duty";
|
|
constexpr char header_89[] PROGMEM = "AirConStatus";
|
|
constexpr char header_90[] PROGMEM = "Dwell Actual";
|
|
/*
|
|
constexpr char header_91[] PROGMEM = "";
|
|
constexpr char header_92[] PROGMEM = "";
|
|
constexpr char header_93[] PROGMEM = "";
|
|
constexpr char header_94[] PROGMEM = "";
|
|
constexpr char header_95[] PROGMEM = "";
|
|
constexpr char header_96[] PROGMEM = "";
|
|
constexpr char header_97[] PROGMEM = "";
|
|
constexpr char header_98[] PROGMEM = "";
|
|
constexpr char header_99[] PROGMEM = "";
|
|
constexpr char header_100[] PROGMEM = "";
|
|
constexpr char header_101[] PROGMEM = "";
|
|
constexpr char header_102[] PROGMEM = "";
|
|
constexpr char header_103[] PROGMEM = "";
|
|
constexpr char header_104[] PROGMEM = "";
|
|
constexpr char header_105[] PROGMEM = "";
|
|
constexpr char header_106[] PROGMEM = "";
|
|
constexpr char header_107[] PROGMEM = "";
|
|
constexpr char header_108[] PROGMEM = "";
|
|
constexpr char header_109[] PROGMEM = "";
|
|
constexpr char header_110[] PROGMEM = "";
|
|
constexpr char header_111[] PROGMEM = "";
|
|
constexpr char header_112[] PROGMEM = "";
|
|
constexpr char header_113[] PROGMEM = "";
|
|
constexpr char header_114[] PROGMEM = "";
|
|
constexpr char header_115[] PROGMEM = "";
|
|
constexpr char header_116[] PROGMEM = "";
|
|
constexpr char header_117[] PROGMEM = "";
|
|
constexpr char header_118[] PROGMEM = "";
|
|
constexpr char header_119[] PROGMEM = "";
|
|
constexpr char header_120[] PROGMEM = "";
|
|
constexpr char header_121[] PROGMEM = "";
|
|
*/
|
|
|
|
constexpr const char* header_table[] PROGMEM = { header_0,\
|
|
header_1,\
|
|
header_2,\
|
|
header_3,\
|
|
header_4,\
|
|
header_5,\
|
|
header_6,\
|
|
header_7,\
|
|
header_8,\
|
|
header_9,\
|
|
header_10,\
|
|
header_11,\
|
|
header_12,\
|
|
header_13,\
|
|
header_14,\
|
|
header_15,\
|
|
header_16,\
|
|
header_17,\
|
|
header_18,\
|
|
header_19,\
|
|
header_20,\
|
|
header_21,\
|
|
header_22,\
|
|
header_23,\
|
|
header_24,\
|
|
header_25,\
|
|
header_26,\
|
|
header_27,\
|
|
header_28,\
|
|
header_29,\
|
|
header_30,\
|
|
header_31,\
|
|
header_32,\
|
|
header_33,\
|
|
header_34,\
|
|
header_35,\
|
|
header_36,\
|
|
header_37,\
|
|
header_38,\
|
|
header_39,\
|
|
header_40,\
|
|
header_41,\
|
|
header_42,\
|
|
header_43,\
|
|
header_44,\
|
|
header_45,\
|
|
header_46,\
|
|
header_47,\
|
|
header_48,\
|
|
header_49,\
|
|
header_50,\
|
|
header_51,\
|
|
header_52,\
|
|
header_53,\
|
|
header_54,\
|
|
header_55,\
|
|
header_56,\
|
|
header_57,\
|
|
header_58,\
|
|
header_59,\
|
|
header_60,\
|
|
header_61,\
|
|
header_62,\
|
|
header_63,\
|
|
header_64,\
|
|
header_65,\
|
|
header_66,\
|
|
header_67,\
|
|
header_68,\
|
|
header_69,\
|
|
header_70,\
|
|
header_71,\
|
|
header_72,\
|
|
header_73,\
|
|
header_74,\
|
|
header_75,\
|
|
header_76,\
|
|
header_77,\
|
|
header_78,\
|
|
header_79,\
|
|
header_80,\
|
|
header_81,\
|
|
header_82,\
|
|
header_83,\
|
|
header_84,\
|
|
header_85,\
|
|
header_86,\
|
|
header_87,\
|
|
header_88,\
|
|
header_89,\
|
|
header_90,\
|
|
/*
|
|
header_91,\
|
|
header_92,\
|
|
header_93,\
|
|
header_94,\
|
|
header_95,\
|
|
header_96,\
|
|
header_97,\
|
|
header_98,\
|
|
header_99,\
|
|
header_100,\
|
|
header_101,\
|
|
header_102,\
|
|
header_103,\
|
|
header_104,\
|
|
header_105,\
|
|
header_106,\
|
|
header_107,\
|
|
header_108,\
|
|
header_109,\
|
|
header_110,\
|
|
header_111,\
|
|
header_112,\
|
|
header_113,\
|
|
header_114,\
|
|
header_115,\
|
|
header_116,\
|
|
header_117,\
|
|
header_118,\
|
|
header_119,\
|
|
header_120,\
|
|
header_121,\
|
|
*/
|
|
};
|
|
#define SD_LOG_NUM_FIELDS 91 /**< The number of fields that are in the log. This is always smaller than the entry size due to some fields being 2 bytes */
|
|
|
|
static_assert(sizeof(header_table) == (sizeof(char*) * SD_LOG_NUM_FIELDS), "Number of header table titles must match number of log fields");
|
|
|
|
SdExFat sd;
|
|
ExFile logFile;
|
|
RingBuf<ExFile, RING_BUF_CAPACITY> rb;
|
|
uint8_t SD_status = SD_STATUS_OFF;
|
|
uint16_t currentLogFileNumber;
|
|
bool manualLogActive = false;
|
|
uint32_t logStartTime = 0; //In ms
|
|
|
|
void initSD()
|
|
{
|
|
//Set default state to ready. If any stage of the init fails, this will be changed
|
|
SD_status = SD_STATUS_READY;
|
|
|
|
//Set the RTC callback. This is used to set the correct timestamp on file creation and sync operations
|
|
FsDateTime::setCallback(dateTime);
|
|
|
|
// Initialise the SD.
|
|
if (!sd.begin(SD_CONFIG))
|
|
{
|
|
//sd.initErrorHalt(&Serial);
|
|
//if (sdErrorCode() == SD_CARD_ERROR_CMD0) { SD_status = SD_STATUS_ERROR_NO_CARD;
|
|
SD_status = SD_STATUS_ERROR_NO_CARD;
|
|
}
|
|
|
|
//Set the TunerStudio status variable
|
|
setTS_SD_status();
|
|
}
|
|
|
|
bool createLogFile()
|
|
{
|
|
//TunerStudio only supports 8.3 filename format.
|
|
char filenameBuffer[13]; //8 + 1 + 3 + 1
|
|
bool returnValue = false;
|
|
|
|
//Saving this in case we ever go back to the datestamped filename
|
|
/*
|
|
//Filename format is: YYYY-MM-DD_HH.MM.SS.csv
|
|
char intBuffer[5];
|
|
itoa(rtc_getYear(), intBuffer, 10);
|
|
strcpy(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, "-");
|
|
itoa(rtc_getMonth(), intBuffer, 10);
|
|
strcat(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, "-");
|
|
itoa(rtc_getDay(), intBuffer, 10);
|
|
strcat(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, "_");
|
|
itoa(rtc_getHour(), intBuffer, 10);
|
|
strcat(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, ".");
|
|
itoa(rtc_getMinute(), intBuffer, 10);
|
|
strcat(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, ".");
|
|
itoa(rtc_getSecond(), intBuffer, 10);
|
|
strcat(filenameBuffer, intBuffer);
|
|
strcat(filenameBuffer, ".csv");
|
|
*/
|
|
|
|
//Lookup the next available file number
|
|
currentLogFileNumber = getNextSDLogFileNumber();
|
|
|
|
//Create the filename
|
|
//sprintf(filenameBuffer, "%s%04d.%s", LOG_FILE_PREFIX, currentLogFileNumber, LOG_FILE_EXTENSION);
|
|
if(currentLogFileNumber > MAX_LOG_FILES) { currentLogFileNumber = 1; } //If we've run out of file numbers, start again from 1
|
|
snprintf(filenameBuffer, 13, "%s%04d.%s", LOG_FILE_PREFIX, currentLogFileNumber, LOG_FILE_EXTENSION);
|
|
|
|
logFile.close();
|
|
if (logFile.open(filenameBuffer, O_RDWR | O_CREAT | O_TRUNC))
|
|
{
|
|
returnValue = true;
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
uint16_t getNextSDLogFileNumber()
|
|
{
|
|
uint16_t nextFileNumber = 1;
|
|
char filenameBuffer[13]; //8 + 1 + 3 + 1
|
|
sprintf(filenameBuffer, "%s%04d.%s", LOG_FILE_PREFIX, nextFileNumber, LOG_FILE_EXTENSION);
|
|
|
|
//Lookup the next available file number
|
|
while( (nextFileNumber < MAX_LOG_FILES) && (sd.exists(filenameBuffer)) )
|
|
{
|
|
nextFileNumber++;
|
|
sprintf(filenameBuffer, "%s%04d.%s", LOG_FILE_PREFIX, nextFileNumber, LOG_FILE_EXTENSION);
|
|
}
|
|
|
|
return nextFileNumber;
|
|
}
|
|
|
|
bool getSDLogFileDetails(uint8_t* buffer, uint16_t logNumber)
|
|
{
|
|
bool fileFound = false;
|
|
|
|
if(logFile.isOpen()) { endSDLogging(); }
|
|
|
|
char filenameBuffer[13]; //8 + 1 + 3 + 1
|
|
if(logNumber > MAX_LOG_FILES) { logNumber = MAX_LOG_FILES; } //If we've run out of file numbers, start again from 1
|
|
snprintf(filenameBuffer, 13, "%s%04d.%s", LOG_FILE_PREFIX, logNumber, LOG_FILE_EXTENSION);
|
|
|
|
if(sd.exists(filenameBuffer))
|
|
{
|
|
fileFound = true;
|
|
|
|
logFile = sd.open(filenameBuffer, O_RDONLY);
|
|
//Copy the filename into the buffer. Note we do not copy the termination character or the fullstop
|
|
for(byte i=0; i<12; i++)
|
|
{
|
|
//We don't copy the fullstop to the buffer
|
|
//As TS requires 8.3 filenames, it's always in the same spot
|
|
if(i < 8) { buffer[i] = filenameBuffer[i]; } //Everything before the fullstop
|
|
else if(i > 8) { buffer[i-1] = filenameBuffer[i]; } //Everything after the fullstop
|
|
}
|
|
|
|
//Maintenance check, truncate the file. This will usually do nothing, but in the case where a prior log was interrupted, this will truncate the file
|
|
//Due to overhead, only bother doing this if the engine isn't running
|
|
if(currentStatus.RPM == 0) { logFile.truncate(); }
|
|
|
|
//Is File or ignore
|
|
buffer[11] = 1;
|
|
|
|
//No idea
|
|
buffer[12] = 0;
|
|
|
|
//5 bytes for FAT creation date/time
|
|
uint16_t pDate = 0;
|
|
uint16_t pTime = 0;
|
|
logFile.getCreateDateTime(&pDate, &pTime);
|
|
buffer[13] = 0; //Not sure what this byte is for yet
|
|
buffer[14] = lowByte(pTime);
|
|
buffer[15] = highByte(pTime);
|
|
buffer[16] = lowByte(pDate);
|
|
buffer[17] = highByte(pDate);
|
|
|
|
//Sector number (4 bytes) - This byte order might be backwards
|
|
uint32_t sector = logFile.firstSector();
|
|
buffer[18] = ((sector) & 255);
|
|
buffer[19] = ((sector >> 8) & 255);
|
|
buffer[20] = ((sector >> 16) & 255);
|
|
buffer[21] = ((sector >> 24) & 255);
|
|
|
|
//Unsure on the below 6 bytes, possibly last accessed or modified date/time?
|
|
buffer[22] = 0;
|
|
buffer[23] = 0;
|
|
buffer[24] = 0;
|
|
buffer[25] = 0;
|
|
buffer[26] = 0;
|
|
buffer[27] = 0;
|
|
|
|
//File size (4 bytes). Little endian
|
|
uint32_t size = logFile.fileSize();
|
|
buffer[28] = ((size) & 255);
|
|
buffer[29] = ((size >> 8) & 255);
|
|
buffer[30] = ((size >> 16) & 255);
|
|
buffer[31] = ((size >> 24) & 255);
|
|
|
|
}
|
|
|
|
return fileFound;
|
|
}
|
|
|
|
void readSDSectors(uint8_t* buffer, uint32_t sectorNumber, uint16_t sectorCount)
|
|
{
|
|
sd.card()->readSectors(sectorNumber, buffer, sectorCount);
|
|
}
|
|
|
|
// Forward declare
|
|
void writeSDLogHeader();
|
|
|
|
void beginSDLogging()
|
|
{
|
|
if(SD_status == SD_STATUS_READY)
|
|
{
|
|
SD_status = SD_STATUS_ACTIVE; //Set the status as being active so that entries will begin to be written. This will be updated below if there is an error
|
|
|
|
// Open or create file - truncate existing file.
|
|
if (!createLogFile())
|
|
{
|
|
SD_status = SD_STATUS_ERROR_NO_WRITE;
|
|
setTS_SD_status();
|
|
return;
|
|
}
|
|
|
|
//Perform pre-allocation on card. This dramatically improves write speed
|
|
if (!logFile.preAllocate(SD_LOG_FILE_SIZE))
|
|
{
|
|
SD_status = SD_STATUS_ERROR_NO_SPACE;
|
|
setTS_SD_status();
|
|
return;
|
|
}
|
|
|
|
//initialise the RingBuf.
|
|
rb.begin(&logFile);
|
|
|
|
//Write a header row
|
|
writeSDLogHeader();
|
|
|
|
//Note the start time
|
|
logStartTime = millis();
|
|
}
|
|
}
|
|
|
|
void endSDLogging()
|
|
{
|
|
if(SD_status == SD_STATUS_ACTIVE)
|
|
{
|
|
// Write any RingBuf data to file.
|
|
rb.sync();
|
|
logFile.truncate();
|
|
logFile.rewind();
|
|
logFile.close();
|
|
logFile.sync(); //This is required to update the sd object. Without this any subsequent logfiles will overwrite this one
|
|
|
|
SD_status = SD_STATUS_READY;
|
|
setTS_SD_status();
|
|
}
|
|
}
|
|
|
|
// Forward declare
|
|
void checkForSDStart();
|
|
void checkForSDStop();
|
|
|
|
void writeSDLogEntry()
|
|
{
|
|
//Check if we're already running a log
|
|
if(SD_status == SD_STATUS_READY)
|
|
{
|
|
//Log not currently running, check if it should be
|
|
checkForSDStart();
|
|
}
|
|
|
|
if(SD_status == SD_STATUS_ACTIVE)
|
|
{
|
|
//Write the timestamp (x.yyy seconds format)
|
|
uint32_t duration = millis() - logStartTime;
|
|
uint32_t seconds = duration / 1000;
|
|
uint32_t milliseconds = duration % 1000;
|
|
rb.print(seconds);
|
|
rb.print('.');
|
|
if (milliseconds < 100) { rb.print("0"); }
|
|
if (milliseconds < 10) { rb.print("0"); }
|
|
rb.print(milliseconds);
|
|
rb.print(',');
|
|
|
|
//Write the line to the ring buffer
|
|
for(byte x=0; x<SD_LOG_NUM_FIELDS; x++)
|
|
{
|
|
#if FPU_MAX_SIZE >= 32
|
|
float entryValue = getReadableFloatLogEntry(x);
|
|
if(IS_INTEGER(entryValue)) { rb.print((uint16_t)entryValue); }
|
|
else { rb.print(entryValue); }
|
|
#else
|
|
rb.print(getReadableLogEntry(x));
|
|
#endif
|
|
if(x < (SD_LOG_NUM_FIELDS - 1)) { rb.print(","); }
|
|
}
|
|
rb.println("");
|
|
|
|
//Check if write to SD from ringbuffer is needed
|
|
//We write to SD when there is more than 1 sector worth of data in the ringbuffer and there is not already a write being performed
|
|
if( (rb.bytesUsed() >= SD_SECTOR_SIZE) && !logFile.isBusy() )
|
|
{
|
|
uint16_t bytesWritten = rb.writeOut(SD_SECTOR_SIZE);
|
|
//Make sure that the entire sector was written successfully
|
|
if (SD_SECTOR_SIZE != bytesWritten)
|
|
{
|
|
SD_status = SD_STATUS_ERROR_WRITE_FAIL;
|
|
}
|
|
}
|
|
|
|
//Check whether we should stop logging
|
|
checkForSDStop();
|
|
|
|
//Check whether the file is full (IE When there is not enough room to write 1 more sector)
|
|
if( (logFile.dataLength() - logFile.curPosition()) < SD_SECTOR_SIZE)
|
|
{
|
|
//Provided the conditions for logging are still met, a new file will be created the next time writeSDLogEntry is called
|
|
endSDLogging();
|
|
beginSDLogging();
|
|
}
|
|
}
|
|
setTS_SD_status();
|
|
}
|
|
|
|
void writeSDLogHeader()
|
|
{
|
|
//Write header for Time field
|
|
rb.print("Time,");
|
|
|
|
//WRite remaining fields based on log definitions
|
|
for(byte x=0; x<SD_LOG_NUM_FIELDS; x++)
|
|
{
|
|
#ifdef CORE_AVR
|
|
//This will probably never be used
|
|
char buffer[30];
|
|
strcpy_P(buffer, (char *)pgm_read_word(&(header_table[x])));
|
|
rb.print(buffer);
|
|
#else
|
|
rb.print(header_table[x]);
|
|
#endif
|
|
if(x < (SD_LOG_NUM_FIELDS - 1)) { rb.print(","); }
|
|
}
|
|
rb.println("");
|
|
}
|
|
|
|
//Sets the status variable for TunerStudio
|
|
void setTS_SD_status()
|
|
{
|
|
if( SD_status == SD_STATUS_ERROR_NO_CARD ) { BIT_CLEAR(currentStatus.TS_SD_Status, SD_STATUS_CARD_PRESENT); } // CARD is not present
|
|
else { BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_PRESENT); } // CARD present
|
|
|
|
BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_TYPE); // CARD is SDHC
|
|
|
|
BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_READY); // CARD is ready
|
|
|
|
if( SD_status == SD_STATUS_ACTIVE ) { BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_LOGGING); }// CARD is logging
|
|
else { BIT_CLEAR(currentStatus.TS_SD_Status, SD_STATUS_CARD_LOGGING); }// CARD is not logging
|
|
|
|
if( (SD_status >= SD_STATUS_ERROR_NO_FS) ) { BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_ERROR); }// CARD has an error
|
|
else { BIT_CLEAR(currentStatus.TS_SD_Status, SD_STATUS_CARD_ERROR); }// CARD has no error
|
|
|
|
BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_FS); // CARD has a FAT32 filesystem (Though this will be exFAT)
|
|
BIT_CLEAR(currentStatus.TS_SD_Status, SD_STATUS_CARD_UNUSED); //Unused bit is always 0
|
|
}
|
|
|
|
/**
|
|
* Checks whether the SD logging should be started based on the logging trigger conditions
|
|
*/
|
|
void checkForSDStart()
|
|
{
|
|
//Logging can only start if we're in the ready state
|
|
//We must check the SD_status each time to prevent trying to init a new log file multiple times
|
|
|
|
if(configPage13.onboard_log_file_style > 0)
|
|
{
|
|
//Check for enable at boot
|
|
if( (configPage13.onboard_log_trigger_boot) && (SD_status == SD_STATUS_READY) )
|
|
{
|
|
//Check that we're not already finished the logging
|
|
if((millis() / 1000) <= configPage13.onboard_log_tr1_duration)
|
|
{
|
|
beginSDLogging(); //Setup the log file, preallocation, header row
|
|
}
|
|
}
|
|
|
|
//Check for RPM based Enable
|
|
if( (configPage13.onboard_log_trigger_RPM) && (SD_status == SD_STATUS_READY) )
|
|
{
|
|
if( (currentStatus.RPMdiv100 >= configPage13.onboard_log_tr2_thr_on) && (currentStatus.RPMdiv100 <= configPage13.onboard_log_tr2_thr_off) ) //Need to check both on and off conditions to prevent logging starting and stopping continually
|
|
{
|
|
beginSDLogging(); //Setup the log file, preallocation, header row
|
|
}
|
|
}
|
|
|
|
//Check for engine protection based enable
|
|
if((configPage13.onboard_log_trigger_prot) && (SD_status == SD_STATUS_READY) )
|
|
{
|
|
if(currentStatus.engineProtectStatus > 0)
|
|
{
|
|
beginSDLogging(); //Setup the log file, preallocation, header row
|
|
}
|
|
}
|
|
|
|
if( (configPage13.onboard_log_trigger_Vbat) && (SD_status == SD_STATUS_READY) )
|
|
{
|
|
|
|
}
|
|
|
|
if((configPage13.onboard_log_trigger_Epin) && (SD_status == SD_STATUS_READY) )
|
|
{
|
|
if(digitalRead(pinSDEnable) == LOW)
|
|
{
|
|
beginSDLogging(); //Setup the log file, preallocation, header row
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether the SD logging should be stopped, based on the logging trigger conditions
|
|
*/
|
|
void checkForSDStop()
|
|
{
|
|
//Check the various conditions to see if we should stop logging
|
|
bool log_boot = false;
|
|
bool log_RPM = false;
|
|
bool log_prot = false;
|
|
bool log_Vbat = false;
|
|
bool log_Epin = false;
|
|
|
|
//Logging only needs to be stopped if already active
|
|
if(SD_status == SD_STATUS_ACTIVE)
|
|
{
|
|
//Check for enable at boot
|
|
if(configPage13.onboard_log_trigger_boot)
|
|
{
|
|
//Check if we're past the logging duration
|
|
if((millis() / 1000) <= configPage13.onboard_log_tr1_duration)
|
|
{
|
|
log_boot = true;
|
|
}
|
|
}
|
|
if(configPage13.onboard_log_trigger_RPM)
|
|
{
|
|
if( (currentStatus.RPMdiv100 >= configPage13.onboard_log_tr2_thr_on) && (currentStatus.RPMdiv100 <= configPage13.onboard_log_tr2_thr_off) )
|
|
{
|
|
log_RPM = true;
|
|
}
|
|
}
|
|
if(configPage13.onboard_log_trigger_prot)
|
|
{
|
|
if(currentStatus.engineProtectStatus > 0)
|
|
{
|
|
log_prot = true;
|
|
}
|
|
}
|
|
if(configPage13.onboard_log_trigger_Vbat)
|
|
{
|
|
|
|
}
|
|
|
|
//External Pin
|
|
if(configPage13.onboard_log_trigger_Epin)
|
|
{
|
|
if(digitalRead(pinSDEnable) == LOW)
|
|
{
|
|
log_Epin = true;
|
|
}
|
|
}
|
|
|
|
//Check all conditions to see if we should stop logging
|
|
if( (log_boot == false) && (log_RPM == false) && (log_prot == false) && (log_Vbat == false) && (log_Epin == false) && (manualLogActive == false) )
|
|
{
|
|
endSDLogging();
|
|
}
|
|
//ALso check whether logging has been disabled entirely
|
|
if(configPage13.onboard_log_file_style == 0) { endSDLogging(); }
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void syncSDLog()
|
|
{
|
|
if( (SD_status == SD_STATUS_ACTIVE) && (!logFile.isBusy()) && (!sd.isBusy()) )
|
|
{
|
|
logFile.sync();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will perform a complete format of the SD card to ExFAT.
|
|
* This will delete all files and create a new empty file system.
|
|
* The SD status will be set to busy when this happens to prevent any other operations
|
|
*/
|
|
void formatExFat()
|
|
{
|
|
bool result = false;
|
|
|
|
//Set the SD status to busy
|
|
BIT_CLEAR(currentStatus.TS_SD_Status, SD_STATUS_CARD_READY);
|
|
|
|
logFile.close();
|
|
|
|
if (sd.cardBegin(SD_CONFIG))
|
|
{
|
|
if(sd.format())
|
|
{
|
|
if (sd.volumeBegin())
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(result == false) { SD_status = SD_STATUS_ERROR_FORMAT_FAIL; }
|
|
else { BIT_SET(currentStatus.TS_SD_Status, SD_STATUS_CARD_READY); }
|
|
}
|
|
|
|
/**
|
|
* @brief Deletes a log file from the SD card
|
|
*
|
|
* Log files all have the same name with a 4 digit number at the end (Eg SPD_0001.csv). TS sends the 4 digits as ASCII characters and they are combined here with the logfile prefix
|
|
*
|
|
* @param log1
|
|
* @param log2
|
|
* @param log3
|
|
* @param log4
|
|
*/
|
|
void deleteLogFile(char log1, char log2, char log3, char log4)
|
|
{
|
|
char logFileName[13];
|
|
strcpy(logFileName, LOG_FILE_PREFIX);
|
|
logFileName[4] = log1;
|
|
logFileName[5] = log2;
|
|
logFileName[6] = log3;
|
|
logFileName[7] = log4;
|
|
logFileName[8] = '.';
|
|
strcpy(logFileName + 9, LOG_FILE_EXTENSION);
|
|
//logFileName[8] = '\0';
|
|
|
|
if(sd.exists(logFileName))
|
|
{
|
|
sd.remove(logFileName);
|
|
}
|
|
}
|
|
|
|
// Call back for file timestamps. Only called for file create and sync().
|
|
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
|
|
|
|
// Return date using FS_DATE macro to format fields.
|
|
//*date = FS_DATE(year(), month(), day());
|
|
*date = FS_DATE(rtc_getYear(), rtc_getMonth(), rtc_getDay());
|
|
|
|
// Return time using FS_TIME macro to format fields.
|
|
*time = FS_TIME(rtc_getHour(), rtc_getMinute(), rtc_getSecond());
|
|
|
|
// Return low time bits in units of 10 ms.
|
|
*ms10 = rtc_getSecond() & 1 ? 100 : 0;
|
|
}
|
|
|
|
uint32_t sectorCount()
|
|
{
|
|
return sd.card()->sectorCount();
|
|
}
|
|
|
|
#endif
|