/************************************************************************* * Telematics Data Logger Class * Distributed under BSD license * Written by Stanley Huang * Visit http://freematics.com for more information *************************************************************************/ // additional custom PID for data logger #define PID_DATA_SIZE 0x80 #if ENABLE_DATA_LOG File sdfile; #endif class CDataLogger { public: CDataLogger():m_lastDataTime(0),dataTime(0),dataSize(0) { #if ENABLE_DATA_CACHE cacheBytes = 0; #endif } void initSender() { #if ENABLE_DATA_OUT Serial.begin(STREAM_BAUDRATE); #endif } byte genTimestamp(char* buf, bool absolute) { byte n; if (absolute || dataTime >= m_lastDataTime + 60000) { // absolute timestamp n = sprintf_P(buf, PSTR("#%lu,"), dataTime); } else { // relative timestamp uint16_t elapsed = (unsigned int)(dataTime - m_lastDataTime); n = sprintf_P(buf, PSTR("%u,"), elapsed); } return n; } void record(const char* buf, byte len) { #if ENABLE_DATA_LOG char tmp[12]; byte n = genTimestamp(tmp, dataSize == 0); dataSize += sdfile.write((uint8_t*)tmp, n); dataSize += sdfile.write(buf, len); sdfile.write('\n'); dataSize++; #endif m_lastDataTime = dataTime; } void dispatch(const char* buf, byte len) { #if ENABLE_DATA_CACHE // reserve some space for timestamp, ending white space and zero terminator int l = cacheBytes + len + 12 - CACHE_SIZE; if (l >= 0) { // cache full #if CACHE_SHIFT // discard the oldest data for (l = CACHE_SIZE / 2; cache[l] && cache[l] != ' '; l++); if (cache[l]) { cacheBytes -= l; memcpy(cache, cache + l + 1, cacheBytes); } else { cacheBytes = 0; } #else return; #endif } // add new data at the end byte n = genTimestamp(cache + cacheBytes, cacheBytes == 0); if (n == 0) { // same timestamp cache[cacheBytes - 1] = ';'; } else { cacheBytes += n; cache[cacheBytes++] = ','; } if (cacheBytes + len < CACHE_SIZE - 1) { memcpy(cache + cacheBytes, buf, len); cacheBytes += len; cache[cacheBytes++] = ' '; } cache[cacheBytes] = 0; #else //char tmp[12]; //byte n = genTimestamp(tmp, dataTime >= m_lastDataTime + 100); //Serial.write(tmp, n); #endif #if ENABLE_DATA_OUT Serial.write((uint8_t*)buf, len); Serial.println(); #endif } void logData(const char* buf, byte len) { dispatch(buf, len); record(buf, len); } void logData(uint16_t pid) { char buf[8]; byte len = translatePIDName(pid, buf); dispatch(buf, len); record(buf, len); } void logData(uint16_t pid, int16_t value) { char buf[16]; byte n = translatePIDName(pid, buf); byte len = sprintf_P(buf + n, PSTR("%d"), value) + n; dispatch(buf, len); record(buf, len); } void logData(uint16_t pid, int32_t value) { char buf[20]; byte n = translatePIDName(pid, buf); byte len = sprintf_P(buf + n, PSTR("%ld"), value) + n; dispatch(buf, len); record(buf, len); } void logData(uint16_t pid, uint32_t value) { char buf[20]; byte n = translatePIDName(pid, buf); byte len = sprintf_P(buf + n, PSTR("%lu"), value) + n; dispatch(buf, len); record(buf, len); } void logData(uint16_t pid, int value1, int value2, int value3) { char buf[24]; byte n = translatePIDName(pid, buf); n += sprintf_P(buf + n, PSTR("%d,%d,%d"), value1, value2, value3); dispatch(buf, n); record(buf, n); } void logCoordinate(uint16_t pid, int32_t value) { char buf[24]; byte len = translatePIDName(pid, buf); len += sprintf_P(buf + len, PSTR("%d.%06lu"), (int)(value / 1000000), abs(value) % 1000000); dispatch(buf, len); record(buf, len); } #if ENABLE_DATA_LOG uint16_t openFile(uint32_t dateTime = 0) { uint16_t fileIndex; char path[20] = "/DATA"; dataSize = 0; if (SD.exists(path)) { if (dateTime) { // using date and time as file name sprintf(path + 5, "/%08lu.CSV", dateTime); fileIndex = 1; } else { // use index number as file name for (fileIndex = 1; fileIndex; fileIndex++) { sprintf(path + 5, "/DAT%05u.CSV", fileIndex); if (!SD.exists(path)) { break; } } if (fileIndex == 0) return 0; } } else { SD.mkdir(path); fileIndex = 1; sprintf(path + 5, "/DAT%05u.CSV", 1); } sdfile = SD.open(path, FILE_WRITE); if (!sdfile) { return 0; } return fileIndex; } void closeFile() { sdfile.close(); dataSize = 0; } void flushFile() { sdfile.flush(); } #endif uint32_t dataTime; uint32_t dataSize; #if ENABLE_DATA_CACHE void purgeCache() { cacheBytes = 0; } char cache[CACHE_SIZE]; int cacheBytes; #endif private: byte translatePIDName(uint16_t pid, char* text) { return sprintf_P(text, PSTR("%X,"), pid); } uint32_t m_lastDataTime; };