From 7562899738e6d06d13f5c76a9b83574468fc396c Mon Sep 17 00:00:00 2001
From: Stanley Huang <stanleyhuangyc@gmail.com>
Date: Sun, 9 Apr 2017 10:22:56 +1000
Subject: OBD-II data logger on ESP32 or ESP8266

---
 esplogger/datalogger.h  | 456 ++++++++++++++++++------------------------------
 esplogger/esplogger.ino |  14 +-
 2 files changed, 175 insertions(+), 295 deletions(-)

diff --git a/esplogger/datalogger.h b/esplogger/datalogger.h
index 8009a80..6c4b60a 100644
--- a/esplogger/datalogger.h
+++ b/esplogger/datalogger.h
@@ -1,13 +1,12 @@
-#define FORMAT_BIN 0
-#define FORMAT_CSV 1
+/*************************************************************************
+* Freematics Data Logger Class
+* Distributed under GPL v2.0
+* Written by Stanley Huang <stanleyhuangyc@gmail.com>
+* Visit http://freematics.com for more information
+*************************************************************************/
 
-typedef struct {
-    uint32_t time;
-    uint16_t pid;
-    uint8_t flags;
-    uint8_t checksum;
-    float value;
-} LOG_DATA;
+#define FORMAT_BIN 0
+#define FORMAT_TEXT 1
 
 typedef struct {
     uint32_t time;
@@ -17,337 +16,210 @@ typedef struct {
     float value[3];
 } LOG_DATA_COMM;
 
-typedef struct {
-    uint32_t time; /* e.g. 1307281259 */
-    uint16_t pid;
-    uint8_t message;
-    uint8_t checksum;
-    uint16_t fileIndex;
-    uint16_t fileSize; /* KB */
-    uint16_t logFlags;
-    uint8_t logType;
-    uint8_t data[5];
-} LOG_DATA_FILE_INFO;
+#define PID_GPS_LATITUDE 0xA
+#define PID_GPS_LONGITUDE 0xB
+#define PID_GPS_ALTITUDE 0xC
+#define PID_GPS_SPEED 0xD
+#define PID_GPS_HEADING 0xE
+#define PID_GPS_SAT_COUNT 0xF
+#define PID_GPS_TIME 0x10
+#define PID_GPS_DATE 0x11
 
-typedef struct {
-    uint32_t time;
-    uint16_t pid;
-    uint8_t message;
-    uint8_t checksum;
-    uint8_t data[12];
-} LOG_DATA_COMMAND;
+#define PID_ACC 0x20
+#define PID_GYRO 0x21
+#define PID_COMPASS 0x22
+#define PID_MEMS_TEMP 0x23
+#define PID_BATTERY_VOLTAGE 0x24
 
-typedef struct {
-    uint32_t id;
-    uint32_t dataOffset;
-    uint8_t ver;
-    uint8_t logType;
-    uint16_t flags;
-    uint32_t dateTime; //4, YYMMDDHHMM, e.g. 1305291359
-    /*
-    uint8_t devid[8];
-    uint8_t vin[24];
-    uint8_t unused[84];
-    */
-} HEADER;
+#define PID_DATA_SIZE 0x80
 
-#define HEADER_LEN 128 /* bytes */
-
-#define PID_GPS_COORDINATES 0xF00A
-#define PID_GPS_ALTITUDE 0xF00C
-#define PID_GPS_SPEED 0xF00D
-#define PID_GPS_HEADING 0xF00E
-#define PID_GPS_SAT_COUNT 0xF00F
-#define PID_GPS_TIME 0xF010
-
-#define PID_ACC 0xF020
-#define PID_GYRO 0xF021
-
-#define PID_MESSAGE 0xFE00
-#define PID_HEART_BEAT 0xFFEE
-
-#define MSG_FILE_LIST_BEGIN 0x1
-#define MSG_FILE_LIST_END 0x2
-#define MSG_FILE_INFO 0x3
-#define MSG_FILE_REQUEST 0x4
-
-#if LOG_FORMAT == FORMAT_BIN
-#define FILE_NAME_FORMAT "/DAT%05d.LOG"
-#else
-#define FILE_NAME_FORMAT "/DAT%05d.CSV"
-#endif
+#define FILE_NAME_FORMAT "DAT%05d.CSV"
+#define FILE_PATH "/DATA/"
 
 #if ENABLE_DATA_OUT
 
 #if USE_SOFTSERIAL
-
-#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
-    SoftwareSerial SerialBLE(A8, A9); /* for BLE Shield on MEGA*/
-#elif defined(__AVR_ATmega644P__)
-    SoftwareSerial SerialBLE(9, 10); /* for Microduino */
+SoftwareSerial SerialRF(A2, A3);
+#elif defined(RF_SERIAL)
+#define SerialRF RF_SERIAL
 #else
-    SoftwareSerial SerialBLE(A2, A3); /* for BLE Shield on UNO/leonardo*/
+#define SerialRF Serial
 #endif
 
-#define OUTPUT_BAUDRATE 9600
-
-#else
-
-#define SerialBLE Serial
-#define OUTPUT_BAUDRATE 115200
-
 #endif
 
+#if ENABLE_DATA_LOG
+static File sdfile;
 #endif
 
-void btInit(int baudrate);
-void btSend(byte* data, byte length);
+typedef struct {
+    uint8_t pid;
+    char name[3];
+} PID_NAME;
+
+const PID_NAME pidNames[] PROGMEM = {
+{PID_ACC, {'A','C','C'}},
+{PID_GYRO, {'G','Y','R'}},
+{PID_COMPASS, {'M','A','G'}},
+{PID_GPS_LATITUDE, {'L','A','T'}},
+{PID_GPS_LONGITUDE, {'L','N','G'}},
+{PID_GPS_ALTITUDE, {'A','L','T'}},
+{PID_GPS_SPEED, {'S','P','D'}},
+{PID_GPS_HEADING, {'C','R','S'}},
+{PID_GPS_SAT_COUNT, {'S','A','T'}},
+{PID_GPS_TIME, {'U','T','C'}},
+{PID_GPS_DATE, {'D','T','E'}},
+{PID_BATTERY_VOLTAGE, {'B','A','T'}},
+{PID_DATA_SIZE, {'D','A','T'}},
+};
 
 class CDataLogger {
 public:
-    void initSender()
+    CDataLogger()
     {
-#if ENABLE_DATA_OUT
-        SerialBLE.begin(OUTPUT_BAUDRATE);
-#endif
-#if ENABLE_DATA_LOG && LOG_FORMAT == FORMAT_CSV
         m_lastDataTime = 0;
+#if ENABLE_DATA_CACHE
+        cacheBytes = 0;
 #endif
     }
-#if ENABLE_DATA_OUT
-    void sendFileInfo(File& file)
-    {
-        if (file.size() < HEADER_LEN) return;
-
-        LOG_DATA_FILE_INFO info = {0};
-        info.fileIndex = atol(file.name() + 3);
-        if (info.fileIndex == 0) return;
-
-        HEADER hdr;
-        if (file.readBytes((char*)&hdr, sizeof(hdr)) != sizeof(hdr)) return;
-
-        info.pid = PID_MESSAGE;
-        info.message = MSG_FILE_INFO;
-        info.fileSize = file.size();
-        info.time = hdr.dateTime;
-        info.logType = hdr.logType;
-        info.logFlags = hdr.flags;
-        info.checksum = getChecksum((char*)&info, sizeof(info));
-        SerialBLE.write((uint8_t*)&info, sizeof(info));
-    }
-    void sendCommand(byte message, void* data = 0, byte bytes = 0)
+    void initSender()
     {
-        LOG_DATA_COMMAND msg = {0, PID_MESSAGE, message};
-        if (data) memcpy(msg.data, data, bytes);
-        msg.checksum = getChecksum((char*)&msg, sizeof(msg));
-        SerialBLE.write((uint8_t*)&msg, sizeof(msg));
+#if ENABLE_DATA_OUT
+        SerialRF.begin(STREAM_BAUDRATE);
+#endif
     }
-    bool receiveCommand(LOG_DATA_COMMAND& msg)
+    byte genTimestamp(char* buf, bool absolute)
     {
-        if (!SerialBLE.available())
-            return false;
-
-        if (SerialBLE.readBytes((char*)&msg, sizeof(msg)) != sizeof(msg))
-            return false;
-
-        uint8_t checksum = msg.checksum;
-        msg.checksum = 0;
-        if (getChecksum((char*)&msg, sizeof(msg)) != msg.checksum) {
-            return false;
-        }
-        return true;
+      byte n;
+      if (absolute || dataTime >= m_lastDataTime + 60000) {
+        // absolute timestamp
+        n = sprintf(buf, "#%lu,", dataTime);
+      } else {
+        // incremental timestamp
+        n = sprintf(buf, "%u,", (unsigned int)(dataTime - m_lastDataTime));
+      }
+      return n;
     }
-#endif
-    void logData(uint16_t pid, int value)
+    void record(const char* buf, byte len)
     {
-#if LOG_FORMAT == FORMAT_BIN || STREAM_FORMAT == FORMAT_BIN
-        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
-        ld.checksum = getChecksum((char*)&ld, 12);
-#endif
-#if ENABLE_DATA_OUT
-#if STREAM_FORMAT == FORMAT_BIN
-        SerialBLE.write((uint8_t*)&ld, 12);
-#else
-        SerialBLE.print(pid, HEX);
-        SerialBLE.write(',');
-        SerialBLE.print(value);
-        SerialBLE.write('\n');
-#endif
-#endif
 #if ENABLE_DATA_LOG
-        if (!sdfile) return;
-#if LOG_FORMAT == FORMAT_BIN
-        sdfile.write((uint8_t*)&ld, 12);
-        dataSize += 12;
+#if STREAM_FORMAT == FORMAT_BIN
+        dataSize += sdfile.write(buf, len);
 #else
-        dataSize += sdfile.print(dataTime - m_lastDataTime);
-        sdfile.write(',');
-        dataSize += sdfile.print(pid, HEX);
-        sdfile.write(',');
-        dataSize += sdfile.print(value);
-        sdfile.write('\n');
+        char tmp[12];
+        byte n = genTimestamp(tmp, dataSize == 0);
+        dataSize += sdfile.write(tmp, n);
+        dataSize += sdfile.write(buf, len);
+        sdfile.println();
         dataSize += 3;
-        m_lastDataTime = dataTime;
 #endif
 #endif
+        m_lastDataTime = dataTime;
     }
-    void logData(uint16_t pid, float value)
+    void dispatch(const char* buf, byte len)
     {
-#if LOG_FORMAT == FORMAT_BIN || STREAM_FORMAT == FORMAT_BIN
-        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
-        ld.checksum = getChecksum((char*)&ld, 12);
+#if ENABLE_DATA_CACHE
+        if (cacheBytes + len < MAX_CACHE_SIZE - 10) {
+          cacheBytes += genTimestamp(cache + cacheBytes, cacheBytes == 0);
+          memcpy(cache + cacheBytes, buf, len);
+          cacheBytes += len;
+          cache[cacheBytes++] = '\n';
+          cache[cacheBytes] = 0;
+        }
 #endif
 #if ENABLE_DATA_OUT
-#if STREAM_FORMAT == FORMAT_BIN
-        SerialBLE.write((uint8_t*)&ld, 12);
-#else
-        SerialBLE.print(pid, HEX);
-        SerialBLE.write(',');
-        SerialBLE.print(value);
-        SerialBLE.write('\n');
+        SerialRF.write(buf, len);
+        SerialRF.println();
 #endif
-#endif
-#if ENABLE_DATA_LOG
-        if (!sdfile) return;
-#if LOG_FORMAT == FORMAT_BIN
-        sdfile.write((uint8_t*)&ld, 12);
-        dataSize += 12;
-#else
-        dataSize += sdfile.print(dataTime - m_lastDataTime);
-        sdfile.write(',');
-        dataSize += sdfile.print(pid, HEX);
-        sdfile.write(',');
-        dataSize += sdfile.print(value);
-        sdfile.write('\n');
-        dataSize += 3;
-        m_lastDataTime = dataTime;
+    }
+    void logData(const char* buf, byte len)
+    {
+#if ENABLE_DATA_OUT
+#if STREAM_FORMAT != FORMAT_BIN
+        dispatch(buf, len);
 #endif
 #endif
+        record(buf, len);
     }
-    void logData(uint16_t pid, float value1, float value2)
+    void logData(uint16_t pid)
     {
-#if LOG_FORMAT == FORMAT_BIN || STREAM_FORMAT == FORMAT_BIN
-        LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}};
-        ld.checksum = getChecksum((char*)&ld, 16);
-#endif
+        char buf[8];
+        byte len = translatePIDName(pid, buf);
 #if ENABLE_DATA_OUT
-#if STREAM_FORMAT == FORMAT_BIN
-        SerialBLE.write((uint8_t*)&ld, 16);
-#else
-        SerialBLE.print(pid, HEX);
-        SerialBLE.write(',');
-        SerialBLE.print(value1, 6);
-        SerialBLE.write(',');
-        SerialBLE.print(value2, 6);
-        SerialBLE.write('\n');
+#if STREAM_FORMAT != FORMAT_BIN
+        dispatch(buf, len);
 #endif
 #endif
-#if ENABLE_DATA_LOG
-        if (!sdfile) return;
-#if LOG_FORMAT == FORMAT_BIN
-        sdfile.write((uint8_t*)&ld, 16);
-        dataSize += 16;
+        record(buf, len);
+    }
+    void logData(uint16_t pid, int16_t value)
+    {
+        char buf[16];
+        byte n = translatePIDName(pid, buf);
+        byte len = sprintf(buf + n, "%d", value) + n;
+#if STREAM_FORMAT == FORMAT_BIN
+        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
+        ld.checksum = getChecksum((char*)&ld, 12);
+        dispatch((const char*)&ld, 12);
 #else
-        dataSize += sdfile.print(dataTime - m_lastDataTime);
-        sdfile.write(',');
-        dataSize += sdfile.print(pid, HEX);
-        sdfile.write(',');
-        dataSize += sdfile.print(value1, 6);
-        sdfile.write(',');
-        dataSize += sdfile.print(value2, 6);
-        sdfile.write('\n');
-        dataSize += 4;
-        m_lastDataTime = dataTime;
-#endif
+        dispatch(buf, len);
 #endif
+        record(buf, len);
     }
-    void logData(uint16_t pid, uint32_t value1, uint32_t value2)
+    void logData(uint16_t pid, int32_t value)
     {
-#if LOG_FORMAT == FORMAT_BIN || STREAM_FORMAT == FORMAT_BIN
-        LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}};
-        ld.checksum = getChecksum((char*)&ld, 16);
-#endif
+        char buf[20];
+        byte n = translatePIDName(pid, buf);
+        byte len = sprintf(buf + n, "%ld", value) + n;
 #if ENABLE_DATA_OUT
 #if STREAM_FORMAT == FORMAT_BIN
-        SerialBLE.write((uint8_t*)&ld, 16);
-#else
-        SerialBLE.print(pid, HEX);
-        SerialBLE.write(',');
-        SerialBLE.print(value1);
-        SerialBLE.write(',');
-        SerialBLE.print(value2);
-        SerialBLE.write('\n');
-#endif
-#endif
-#if ENABLE_DATA_LOG
-        if (!sdfile) return;
-#if LOG_FORMAT == FORMAT_BIN
-        sdfile.write((uint8_t*)&ld, 16);
-        dataSize += 16;
+        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
+        ld.checksum = getChecksum((char*)&ld, 12);
+        dispatch((const char*)&ld, 12);
 #else
-        dataSize += sdfile.print(dataTime - m_lastDataTime);
-        sdfile.write(',');
-        dataSize += sdfile.print(pid, HEX);
-        sdfile.write(',');
-        dataSize += sdfile.print(value1);
-        sdfile.write(',');
-        dataSize += sdfile.print(value2);
-        sdfile.write('\n');
-        dataSize += 4;
-        m_lastDataTime = dataTime;
+        dispatch(buf, len);
 #endif
 #endif
+        record(buf, len);
     }
-    void logData(uint16_t pid, int value1, int value2, int value3)
+    void logData(uint16_t pid, uint32_t value)
     {
-#if LOG_FORMAT == FORMAT_BIN || STREAM_FORMAT == FORMAT_BIN
-        LOG_DATA_COMM ld = {dataTime, pid, 3, 0, {value1, value2, value3}};
-        ld.checksum = getChecksum((char*)&ld, 20);
-#endif
-#if ENABLE_DATA_OUT
+        char buf[20];
+        byte n = translatePIDName(pid, buf);
+        byte len = sprintf(buf + n, "%lu", value) + n;
 #if STREAM_FORMAT == FORMAT_BIN
-        SerialBLE.write((uint8_t*)&ld, 20);
+        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
+        ld.checksum = getChecksum((char*)&ld, 12);
+        dispatch((const char*)&ld, 12);
 #else
-        SerialBLE.print(pid, HEX);
-        SerialBLE.write(',');
-        SerialBLE.print(value1);
-        SerialBLE.write(',');
-        SerialBLE.print(value2);
-        SerialBLE.write(',');
-        SerialBLE.print(value3);
-        SerialBLE.write('\n');
+        dispatch(buf, len);
 #endif
-#endif
-#if ENABLE_DATA_LOG
-        if (!sdfile) return;
-#if LOG_FORMAT == FORMAT_BIN
-        sdfile.write((uint8_t*)&ld, 20);
-        dataSize += 20;
+        record(buf, len);
+    }
+    void logData(uint16_t pid, int values[])
+    {
+        char buf[24];
+        byte n = translatePIDName(pid, buf);
+        byte len = sprintf(buf + n, "%d,%d,%d", values[0], values[1], values[2]) + n;
+#if STREAM_FORMAT == FORMAT_BIN
+        LOG_DATA_COMM ld = {dataTime, pid, 3, 0, {values[0], values[1], values[2]}};
+        ld.checksum = getChecksum((char*)&ld, 20);
+        dispatch((const char*)&ld, 20);
 #else
-        dataSize += sdfile.print(dataTime - m_lastDataTime);
-        sdfile.write(',');
-        dataSize += sdfile.print(pid, HEX);
-        sdfile.write(',');
-        dataSize += sdfile.print(value1);
-        sdfile.write(',');
-        dataSize += sdfile.print(value2);
-        sdfile.write(',');
-        dataSize += sdfile.print(value3);
-        sdfile.write('\n');
-        dataSize += 5;
-        m_lastDataTime = dataTime;
-#endif
+        dispatch(buf, len);
 #endif
+        record(buf, len);
     }
 #if ENABLE_DATA_LOG
     uint16_t openFile(uint16_t logFlags = 0, uint32_t dateTime = 0)
     {
         uint16_t fileIndex;
-        char filename[24] = "/FRMATICS";
+        char filename[24] = FILE_PATH;
 
+        dataSize = 0;
         if (SD.exists(filename)) {
             for (fileIndex = 1; fileIndex; fileIndex++) {
-                sprintf(filename + 9, FILE_NAME_FORMAT, fileIndex);
+                sprintf(filename + sizeof(FILE_PATH) - 1, FILE_NAME_FORMAT, fileIndex);
                 if (!SD.exists(filename)) {
                     break;
                 }
@@ -357,21 +229,14 @@ public:
         } else {
             SD.mkdir(filename);
             fileIndex = 1;
-            sprintf(filename + 9, FILE_NAME_FORMAT, 1);
+            sprintf(filename + sizeof(FILE_PATH) - 1, FILE_NAME_FORMAT, 1);
         }
 
         sdfile = SD.open(filename, FILE_WRITE);
         if (!sdfile) {
             return 0;
         }
-
-#if LOG_FORMAT == FORMAT_BIN
-        HEADER hdr = {'UDUS', HEADER_LEN, 1, 0, logFlags, dateTime};
-        sdfile.write((uint8_t*)&hdr, sizeof(hdr));
-        for (byte i = 0; i < HEADER_LEN - sizeof(hdr); i++)
-            sdfile.write((uint8_t)0);
-        dataSize = HEADER_LEN;
-#endif
+        m_lastDataTime = dateTime;
         return fileIndex;
     }
     void closeFile()
@@ -380,13 +245,17 @@ public:
     }
     void flushFile()
     {
-        if (sdfile) sdfile.flush();
+        sdfile.flush();
     }
 #endif
     uint32_t dataTime;
     uint32_t dataSize;
+#if ENABLE_DATA_CACHE
+    char cache[MAX_CACHE_SIZE];
+    int cacheBytes;
+#endif
 private:
-    static byte getChecksum(char* buffer, byte len)
+    byte getChecksum(char* buffer, byte len)
     {
         uint8_t checksum = 0;
         for (byte i = 0; i < len; i++) {
@@ -394,10 +263,19 @@ private:
         }
         return checksum;
     }
-#if ENABLE_DATA_LOG
-    File sdfile;
-#if LOG_FORMAT == FORMAT_CSV
-    uint32_t m_lastDataTime;
-#endif
+    byte translatePIDName(uint16_t pid, char* text)
+    {
+#if STREAM_FORMAT == FORMAT_TEXT && USE_FRIENDLY_PID_NAME
+        for (uint16_t n = 0; n < sizeof(pidNames) / sizeof(pidNames[0]); n++) {
+            uint16_t id = pgm_read_byte(&pidNames[n].pid);
+            if (pid == id) {
+                memcpy_P(text, pidNames[n].name, 3);
+                text[3] = ',';
+                return 4;
+            }
+        }
 #endif
+        return sprintf(text, "%X,", pid);
+    }
+    uint32_t m_lastDataTime;
 };
diff --git a/esplogger/esplogger.ino b/esplogger/esplogger.ino
index f8895a5..5cc1e7b 100644
--- a/esplogger/esplogger.ino
+++ b/esplogger/esplogger.ino
@@ -1,13 +1,12 @@
 /*************************************************************************
-* OBD-II Data Logger based on ESP8266
+* OBD-II Data Logger based on ESP8266 or ESP32
 * Distributed under GPL v2.0
-* Developed by Stanley Huang <stanleyhuangyc@gmail.com>
+* Developed by Stanley Huang <support@freematics.com.au>
 *************************************************************************/
 
 #include <Arduino.h>
 #include <Wire.h>
-#include <OBD.h>
-#include <MPU6050.h>
+#include <OBD2UART.h>
 #include "SH1106.h"
 #include "images.h"
 #include "config.h"
@@ -225,7 +224,7 @@ private:
     {
         int value = 0;
         // send a query to OBD adapter for specified OBD-II pid
-        if (read(pid, value)) {
+        if (readPID(pid, value)) {
             dataTime = millis();
             // log data to SD card
             logData(0x100 | pid, value);
@@ -253,7 +252,7 @@ private:
             }
             if (init()) {
                 int value;
-                if (read(PID_RPM, value) && value > 0)
+                if (readPID(PID_RPM, value) && value > 0)
                     break;
             }
         }
@@ -359,9 +358,12 @@ static COBDLogger logger;
 
 void setup()
 {
+    Serial.begin(115200);
+    Serial.println("Freematics");
     lcd.begin();
     lcd.setFontSize(FONT_SIZE_MEDIUM);
     lcd.println("ESPLogger");
+    delay(1000);
 
     logger.begin();
     logger.initSender();
-- 
cgit v1.2.3