From 9a0730a5d9422441d3cfb168fb35772232a4e385 Mon Sep 17 00:00:00 2001
From: Stanley Huang <stanleyhuangyc@gmail.com>
Date: Fri, 4 Apr 2014 21:42:13 +0800
Subject: add OBD-II data player

---
 utilities/dataplayer/config.h       |  55 +++
 utilities/dataplayer/datalogger.h   | 340 ++++++++++++++++++
 utilities/dataplayer/dataplayer.cbp | 666 ++++++++++++++++++++++++++++++++++++
 utilities/dataplayer/dataplayer.ino | 401 ++++++++++++++++++++++
 4 files changed, 1462 insertions(+)
 create mode 100644 utilities/dataplayer/config.h
 create mode 100644 utilities/dataplayer/datalogger.h
 create mode 100644 utilities/dataplayer/dataplayer.cbp
 create mode 100644 utilities/dataplayer/dataplayer.ino

diff --git a/utilities/dataplayer/config.h b/utilities/dataplayer/config.h
new file mode 100644
index 0000000..8b166fe
--- /dev/null
+++ b/utilities/dataplayer/config.h
@@ -0,0 +1,55 @@
+#ifndef CONFIG_H_INCLUDED
+#define CONFIG_H_INCLUDED
+
+/**************************************
+* OBD-II options
+**************************************/
+#define OBD_MODEL OBD_MODEL_UART
+#define OBD_PROTOCOL 0 /* 0 for auto */
+
+/**************************************
+* Data logging/streaming out
+**************************************/
+#define ENABLE_DATA_OUT 1
+#define ENABLE_DATA_LOG 0
+#define USE_SOFTSERIAL 1
+//this defines the format of log file
+#define LOG_FORMAT FORMAT_CSV
+
+/**************************************
+* Default working mode
+**************************************/
+#define MODE_DEFAULT MODE_LOGGER /* MODE_LOGGER/MODE_TIMER */
+//#define MODE_SWITCH_PIN 8
+
+/**************************************
+* Choose SD pin here
+**************************************/
+//#define SD_CS_PIN SS // generic
+//#define SD_CS_PIN 4 // ethernet shield
+//#define SD_CS_PIN 7 // microduino
+#define SD_CS_PIN 10 // SD breakout
+
+/**************************************
+* Config GPS here
+**************************************/
+#define USE_GPS 0
+#define GPS_BAUDRATE 38400 /* bps */
+//#define GPS_OPEN_BAUDRATE 4800 /* bps */
+
+/**************************************
+* Choose LCD model here
+**************************************/
+LCD_ILI9341 lcd;
+//LCD_SSD1306 lcd;
+//LCD_Null lcd;
+
+/**************************************
+* Other options
+**************************************/
+#define USE_MPU6050 0
+#define GPS_DATA_TIMEOUT 2000 /* ms */
+//#define DEBUG Serial
+#define DEBUG_BAUDRATE 9600
+
+#endif // CONFIG_H_INCLUDED
diff --git a/utilities/dataplayer/datalogger.h b/utilities/dataplayer/datalogger.h
new file mode 100644
index 0000000..467131f
--- /dev/null
+++ b/utilities/dataplayer/datalogger.h
@@ -0,0 +1,340 @@
+typedef enum {
+    LOG_TYPE_DEFAULT = 0,
+    LOG_TYPE_0_60,
+    LOG_TYPE_0_100,
+    LOG_TYPE_100_200,
+    LOG_TYPE_400M,
+    LOG_TYPE_LAPS,
+    LOG_TYPE_ROUTE,
+} LOG_TYPES;
+
+#define FLAG_CAR 0x1
+#define FLAG_CYCLING 0x2
+#define FLAG_OBD 0x10
+#define FLAG_GPS 0x20
+#define FLAG_ACC 0x40
+
+#define FORMAT_BIN 0
+#define FORMAT_CSV 1
+
+typedef struct {
+    uint32_t time;
+    uint16_t pid;
+    uint8_t flags;
+    uint8_t checksum;
+    float value;
+} LOG_DATA;
+
+typedef struct {
+    uint32_t time;
+    uint16_t pid;
+    uint8_t flags;
+    uint8_t checksum;
+    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;
+
+typedef struct {
+    uint32_t time;
+    uint16_t pid;
+    uint8_t message;
+    uint8_t checksum;
+    uint8_t data[12];
+} LOG_DATA_COMMAND;
+
+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 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
+
+#if ENABLE_DATA_OUT
+#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+    SoftwareSerial mySerial(A8, A9); /* for BLE Shield on MEGA*/
+#elif defined(__AVR_ATmega644P__)
+    SoftwareSerial mySerial(9, 10); /* for Microduino */
+#else
+    SoftwareSerial mySerial(A2, A3); /* for BLE Shield on UNO*/
+#endif
+#endif
+
+class CDataLogger {
+public:
+    void initSender()
+    {
+#if ENABLE_DATA_OUT
+        mySerial.begin(9600);
+#endif
+#if ENABLE_DATA_LOG && LOG_FORMAT == FORMAT_CSV
+        m_lastDataTime = 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));
+        mySerial.write((uint8_t*)&info, sizeof(info));
+    }
+    void sendCommand(byte message, void* data = 0, byte bytes = 0)
+    {
+        LOG_DATA_COMMAND msg = {0, PID_MESSAGE, message};
+        if (data) memcpy(msg.data, data, bytes);
+        msg.checksum = getChecksum((char*)&msg, sizeof(msg));
+        mySerial.write((uint8_t*)&msg, sizeof(msg));
+    }
+    bool receiveCommand(LOG_DATA_COMMAND& msg)
+    {
+        if (!mySerial.available())
+            return false;
+
+        if (mySerial.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;
+    }
+#endif
+    void logData(uint16_t pid, int value)
+    {
+        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
+        ld.checksum = getChecksum((char*)&ld, 12);
+#if ENABLE_DATA_OUT
+        mySerial.write((uint8_t*)&ld, 12);
+#endif
+#if ENABLE_DATA_LOG
+#if LOG_FORMAT == FORMAT_BIN
+        sdfile.write((uint8_t*)&ld, 12);
+        dataSize += 12;
+#else
+        dataSize += sdfile.print(dataTime - m_lastDataTime);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(pid, HEX);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value);
+        dataSize += sdfile.write('\n');
+        m_lastDataTime = dataTime;
+#endif
+#endif
+    }
+    void logData(uint16_t pid, float value)
+    {
+        LOG_DATA_COMM ld = {dataTime, pid, 1, 0, value};
+        ld.checksum = getChecksum((char*)&ld, 12);
+#if ENABLE_DATA_OUT
+        mySerial.write((uint8_t*)&ld, 12);
+#endif
+#if ENABLE_DATA_LOG
+#if LOG_FORMAT == FORMAT_BIN
+        sdfile.write((uint8_t*)&ld, 12);
+        dataSize += 12;
+#else
+        dataSize += sdfile.print(dataTime - m_lastDataTime);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(pid, HEX);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value);
+        dataSize += sdfile.write('\n');
+        m_lastDataTime = dataTime;
+#endif
+#endif
+    }
+    void logData(uint16_t pid, float value1, float value2)
+    {
+        LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}};
+        ld.checksum = getChecksum((char*)&ld, 16);
+#if ENABLE_DATA_OUT
+        mySerial.write((uint8_t*)&ld, 16);
+#endif
+#if ENABLE_DATA_LOG
+#if LOG_FORMAT == FORMAT_BIN
+        sdfile.write((uint8_t*)&ld, 16);
+        dataSize += 16;
+#else
+        dataSize += sdfile.print(dataTime - m_lastDataTime);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(pid, HEX);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value1, 6);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value2, 6);
+        dataSize += sdfile.write('\n');
+        m_lastDataTime = dataTime;
+#endif
+#endif
+    }
+    void logData(uint16_t pid, uint32_t value1, uint32_t value2)
+    {
+        LOG_DATA_COMM ld = {dataTime, pid, 2, 0, {value1, value2}};
+        ld.checksum = getChecksum((char*)&ld, 16);
+#if ENABLE_DATA_OUT
+        mySerial.write((uint8_t*)&ld, 16);
+#endif
+#if ENABLE_DATA_LOG
+#if LOG_FORMAT == FORMAT_BIN
+        sdfile.write((uint8_t*)&ld, 16);
+        dataSize += 16;
+#else
+        dataSize += sdfile.print(dataTime - m_lastDataTime);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(pid, HEX);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value1);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value2);
+        dataSize += sdfile.write('\n');
+        m_lastDataTime = dataTime;
+#endif
+#endif
+    }
+    void logData(uint16_t pid, int value1, int value2, int value3)
+    {
+        LOG_DATA_COMM ld = {dataTime, pid, 3, 0, {value1, value2, value3}};
+        ld.checksum = getChecksum((char*)&ld, 20);
+#if ENABLE_DATA_OUT
+        mySerial.write((uint8_t*)&ld, 20);
+#endif
+#if ENABLE_DATA_LOG
+#if LOG_FORMAT == FORMAT_BIN
+        sdfile.write((uint8_t*)&ld, 20);
+        dataSize += 20;
+#else
+        dataSize += sdfile.print(dataTime - m_lastDataTime);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(pid, HEX);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value1);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value2);
+        dataSize += sdfile.write(',');
+        dataSize += sdfile.print(value3);
+        dataSize += sdfile.write('\n');
+        m_lastDataTime = dataTime;
+#endif
+#endif
+    }
+#if ENABLE_DATA_LOG
+    uint16_t openFile(LOG_TYPES logType, uint16_t logFlags = 0, uint32_t dateTime = 0)
+    {
+        uint16_t fileIndex;
+        char filename[24] = "/FRMATICS";
+
+        if (SD.exists(filename)) {
+            for (fileIndex = 1; fileIndex; fileIndex++) {
+                sprintf(filename + 9, FILE_NAME_FORMAT, fileIndex);
+                if (!SD.exists(filename)) {
+                    break;
+                }
+            }
+            if (fileIndex == 0)
+                return 0;
+        } else {
+            SD.mkdir(filename);
+            fileIndex = 1;
+            sprintf(filename + 9, 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, logType, 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
+        return fileIndex;
+    }
+    void closeFile()
+    {
+        sdfile.close();
+    }
+    void flushFile()
+    {
+        sdfile.flush();
+    }
+#endif
+    uint32_t dataTime;
+    uint32_t dataSize;
+private:
+    static byte getChecksum(char* buffer, byte len)
+    {
+        uint8_t checksum = 0;
+        for (byte i = 0; i < len; i++) {
+          checksum ^= buffer[i];
+        }
+        return checksum;
+    }
+#if ENABLE_DATA_LOG
+    File sdfile;
+#if LOG_FORMAT == FORMAT_CSV
+    uint32_t m_lastDataTime;
+#endif
+#endif
+};
diff --git a/utilities/dataplayer/dataplayer.cbp b/utilities/dataplayer/dataplayer.cbp
new file mode 100644
index 0000000..662f664
--- /dev/null
+++ b/utilities/dataplayer/dataplayer.cbp
@@ -0,0 +1,666 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="DataPlayer" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Simulator - Debug">
+				<Option output="build/obdplayer_sim.exe" prefix_auto="1" extension_auto="0" />
+				<Option object_output="obj/Debug/" />
+				<Option type="1" />
+				<Option compiler="GCC" />
+				<Compiler>
+					<Add option="-g" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-DARDUSIM" />
+					<Add option="-D__AVR_ATmega2560__" />
+					<Add option="-x c++" />
+					<Add directory="$(ARDUINO_DIR)/arduino/cores" />
+					<Add directory="$(ARDUINO_DIR)/arduino/variants/standard" />
+					<Add directory="$(ARDUINO_DIR)/include" />
+				</Compiler>
+				<Linker>
+					<Add option="-lardusim" />
+				</Linker>
+				<Environment>
+					<Variable name="ARDUINO_DIR" value="$(APP_PATH)\ardusim" />
+				</Environment>
+			</Target>
+			<Target title="Simulator - Release">
+				<Option output="build/obdplayer_sim.exe" prefix_auto="1" extension_auto="0" />
+				<Option object_output="obj/Release/" />
+				<Option type="1" />
+				<Option compiler="GCC" />
+				<Compiler>
+					<Add option="-O2" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-DARDUSIM" />
+					<Add option="-D__AVR_ATmega2560__" />
+					<Add option="-x c++" />
+					<Add directory="$(ARDUINO_DIR)/arduino/cores" />
+					<Add directory="$(ARDUINO_DIR)/arduino/variants/standard" />
+					<Add directory="$(ARDUINO_DIR)/include" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+					<Add option="-lardusim" />
+				</Linker>
+				<Environment>
+					<Variable name="ARDUINO_DIR" value="$(APP_PATH)\ardusim" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Uno">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-Os" />
+					<Add option="-O2" />
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-DMEMORY_SAVING" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Uno" />
+					<Variable name="BOARD_ID" value="uno" />
+					<Variable name="MCU" value="atmega328p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="115200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Leonardo">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega32U4__" />
+					<Add option="-DUSB_VID=0x2341" />
+					<Add option="-DUSB_PID=0x8036" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/leonardo" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Leonardo" />
+					<Variable name="BOARD_ID" value="leonardo" />
+					<Variable name="MCU" value="atmega32u4" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Esplora">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega32U4__" />
+					<Add option="-DUSB_VID=0x2341" />
+					<Add option="-DUSB_PID=0x8037" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/leonardo" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Esplora" />
+					<Variable name="BOARD_ID" value="esplora" />
+					<Variable name="MCU" value="atmega32u4" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Micro">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega32U4__" />
+					<Add option="-DUSB_VID=0x2341" />
+					<Add option="-DUSB_PID=0x803C" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/micro" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Micro" />
+					<Variable name="BOARD_ID" value="micro" />
+					<Variable name="MCU" value="atmega32u4" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Duemilanove (328)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Duemilanove (328)" />
+					<Variable name="BOARD_ID" value="duemilanove328" />
+					<Variable name="MCU" value="atmega328p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Duemilanove (168)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega168__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Duemilanove (168)" />
+					<Variable name="BOARD_ID" value="duemilanove168" />
+					<Variable name="MCU" value="atmega168" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="19200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Nano (328)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Nano (328)" />
+					<Variable name="BOARD_ID" value="nano328" />
+					<Variable name="MCU" value="atmega328p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Nano (168)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega168__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Nano (168)" />
+					<Variable name="BOARD_ID" value="nano168" />
+					<Variable name="MCU" value="atmega168" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="19200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Mini (328)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Mini (328)" />
+					<Variable name="BOARD_ID" value="mini328" />
+					<Variable name="MCU" value="atmega328p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Mini (168)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega168__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/eightanaloginputs" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Mini (168)" />
+					<Variable name="BOARD_ID" value="mini168" />
+					<Variable name="MCU" value="atmega168" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="19200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Pro Mini (328)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Pro Mini (328)" />
+					<Variable name="BOARD_ID" value="promini328" />
+					<Variable name="MCU" value="atmega328p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Pro Mini (168)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega168__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Pro Mini (168)" />
+					<Variable name="BOARD_ID" value="promini168" />
+					<Variable name="MCU" value="atmega168" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="19200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Mega 2560/ADK">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega2560__" />
+					<Add option="-O2" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/mega" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Mega 2560\ADK" />
+					<Variable name="BOARD_ID" value="mega2560" />
+					<Variable name="MCU" value="atmega2560" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Mega 1280">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega1280__" />
+					<Add option="-O2" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/mega" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Mega 1280" />
+					<Variable name="BOARD_ID" value="mega1280" />
+					<Variable name="MCU" value="atmega1280" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="57600" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Arduino Mega 8">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega328P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/standard" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Arduino Mega 8" />
+					<Variable name="BOARD_ID" value="mega8" />
+					<Variable name="MCU" value="atmega8" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="19200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+			<Target title="Microduino Core+ (644P)">
+				<Option output="build/obdplayer_${BOARD_ID}.elf" prefix_auto="1" extension_auto="0" />
+				<Option object_output="build" />
+				<Option type="1" />
+				<Option compiler="avrgcc" />
+				<Compiler>
+					<Add option="-x c++" />
+					<Add option="-mmcu=$(MCU)" />
+					<Add option="-DF_CPU=16000000L" />
+					<Add option="-D__AVR_ATmega644P__" />
+					<Add option="-O2" />
+					<Add option="-Os" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/cores/arduino" />
+					<Add directory="$(ARDUINO_DIR)/libraries" />
+					<Add directory="$(ARDUINO_DIR)/hardware/arduino/variants/plus" />
+				</Compiler>
+				<Linker>
+					<Add option="-mmcu=$(MCU)" />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option='&quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add option="-s" />
+				</Linker>
+				<ExtraCommands>
+					<Add before='$(TARGET_COMPILER_DIR)ArduinoUploader &quot;$(PROJECT_DIR).&quot; $(BOARD_ID) - $(MCU_CLOCK) 0 &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/lib_${BOARD_ID}_${MCU_CLOCK}.a&quot; &quot;$(PROJECT_DIR)${TARGET_OUTPUT_DIR}/core_${BOARD_ID}_${MCU_CLOCK}.a&quot;' />
+					<Add after='avr-objcopy -O ihex -R .eeprom -R .eesafe &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).hex&quot;' />
+					<Add after='avr-objcopy --no-change-warnings -j .eeprom --change-section-lma .eeprom=0 -O ihex &quot;$(TARGET_OUTPUT_FILE)&quot; &quot;$(TARGET_OUTPUT_FILE).eep.hex&quot;' />
+					<Add after='avr-size --mcu=$(MCU) --format=avr &quot;$(TARGET_OUTPUT_FILE)&quot;' />
+				</ExtraCommands>
+				<Environment>
+					<Variable name="BOARD" value="Microduino Core+ (644P)" />
+					<Variable name="BOARD_ID" value="uduino644p" />
+					<Variable name="MCU" value="atmega644p" />
+					<Variable name="MCU_CLOCK" value="16" />
+					<Variable name="UPLOAD_BAUDRATE" value="115200" />
+					<Variable name="UPLOAD_PORT" value="" />
+				</Environment>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add directory="." />
+		</Compiler>
+		<Unit filename="config.h" />
+		<Unit filename="datalogger.h" />
+		<Unit filename="dataplayer.ino">
+			<Option compile="1" />
+			<Option link="1" />
+		</Unit>
+		<Extensions>
+			<code_completion />
+			<debugger />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>
diff --git a/utilities/dataplayer/dataplayer.ino b/utilities/dataplayer/dataplayer.ino
new file mode 100644
index 0000000..2e7e1f7
--- /dev/null
+++ b/utilities/dataplayer/dataplayer.ino
@@ -0,0 +1,401 @@
+#include <Arduino.h>
+#include <SPI.h>
+#include <MultiLCD.h>
+#include <Wire.h>
+#include <SD.h>
+#include <SoftwareSerial.h>
+#include <OBD.h>
+#include "config.h"
+#include "datalogger.h"
+
+CDataLogger sender;
+
+#define LOG_DATA_LEN sizeof(LOG_DATA)
+#define REPLAY_SPEED 1
+
+///////////////////////////////////////////////////////////////////////////
+#define PID_GPS_LATITUDE 0xF00A
+#define PID_GPS_LONGITUDE 0xF00B
+#define PID_GPS_ALTITUDE 0xF00C
+#define PID_GPS_SPEED 0xF00D
+
+void sendLogFile(File logfile, byte noWait);
+//
+//unsigned int hex2uint16(const char *p)
+//{
+//	char c = *p;
+//	unsigned int i = 0;
+//	for (char n = 0; c && n < 4; c = *(++p)) {
+//		if (c >= 'A' && c <= 'F') {
+//			c -= 7;
+//		} else if (c>='a' && c<='f') {
+//			c -= 39;
+//        } else if (c == ' ') {
+//            continue;
+//        } else if (c < '0' || c > '9') {
+//			break;
+//        }
+//		i = (i << 4) | (c & 0xF);
+//		n++;
+//	}
+//	return i;
+//}
+
+void initScreen()
+{
+    lcd.clear();
+    lcd.backlight(true);
+    lcd.setFont(FONT_SIZE_SMALL);
+    lcd.setTextColor(RGB16_CYAN);
+    lcd.setCursor(4, 0);
+    lcd.print("ENGINE RPM");
+    lcd.setCursor(104, 0);
+    lcd.print("SPEED");
+    lcd.setCursor(164, 0);
+    lcd.print("ENGINE LOAD");
+    lcd.setCursor(248, 0);
+    lcd.print("INTAKE TEMP");
+
+    lcd.setCursor(4, 7);
+    lcd.print("COOLANT TEMP");
+    lcd.setCursor(104, 7);
+    lcd.print("DISTANCE");
+    lcd.setCursor(164, 7);
+    lcd.print("INTAKE MAP");
+
+    lcd.setCursor(260, 7);
+    lcd.print("ELAPSED");
+    lcd.setCursor(260, 10);
+    lcd.print("LOG SIZE");
+
+    lcd.setTextColor(RGB16_YELLOW);
+    lcd.setCursor(24, 5);
+    lcd.print("rpm");
+    lcd.setCursor(110, 5);
+    lcd.print("km/h");
+    lcd.setCursor(216, 4);
+    lcd.print("%");
+    lcd.setCursor(304, 4);
+    lcd.print("C");
+    lcd.setCursor(64, 11);
+    lcd.print("C");
+    lcd.setCursor(110, 12);
+    lcd.print("km");
+    lcd.setCursor(200, 12);
+    lcd.print("kpa");
+    lcd.setCursor(296, 12);
+    lcd.print("KB");
+
+    lcd.setTextColor(RGB16_WHITE);
+
+
+    //lcd.setCursor(0, 5);
+    //lcd.print("THR:   %");
+    //lcd.setCursor(80, 5);
+    //lcd.print("AIR:   C");
+}
+
+void showChart(int value)
+{
+    static uint16_t pos = 0;
+    if (value < 500) return;
+    byte n = (value - 600) / 30;
+    lcd.fill(pos, pos, 239 - n, 239, RGB16_CYAN);
+    pos = (pos + 1) % 320;
+    lcd.fill(pos, pos, 120, 239);
+}
+
+void showData(byte pid, int value)
+{
+    switch (pid) {
+    case PID_RPM:
+        lcd.setCursor(0, 2);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt((unsigned int)value % 10000, 4);
+        showChart(value);
+        break;
+    case PID_SPEED:
+        lcd.setCursor(90, 2);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt((unsigned int)value % 1000, 3);
+        break;
+    case PID_ENGINE_LOAD:
+        lcd.setCursor(164, 2);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt(value % 100, 3);
+        break;
+    case PID_INTAKE_TEMP:
+        if ((uint16_t)value < 1000) {
+            lcd.setCursor(248, 2);
+            lcd.setFont(FONT_SIZE_XLARGE);
+            lcd.printInt(value, 3);
+        }
+        break;
+    case PID_INTAKE_MAP:
+        lcd.setCursor(164, 9);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt((uint16_t)value % 1000, 3);
+        break;
+    case PID_COOLANT_TEMP:
+        lcd.setCursor(8, 9);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt((uint16_t)value % 1000, 3);
+        break;
+    case PID_DISTANCE:
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.setCursor(90, 9);
+        lcd.printInt((uint16_t)value % 1000, 3);
+        break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void setup()
+{
+    // Open serial communications and wait for port to open:
+    Serial.begin(115200);
+
+    lcd.begin();
+    lcd.clear();
+    sender.initSender();
+
+    // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
+    // Note that even if it's not used as the CS pin, the hardware SS pin
+    // (10 on Arduino Uno boards, 53 on the Mega) must be left as an output
+    // or the SD library functions will not work.
+    pinMode(SD_CS_PIN, OUTPUT);
+
+    Sd2Card card;
+
+    if (card.init(SPI_FULL_SPEED, SD_CS_PIN)) {
+    SdVolume volume;
+#if 0
+        char buf[20];
+        if (!volume.init(card)) {
+            Serial.println("No FAT!");
+        } else {
+            uint32_t volumesize = volume.blocksPerCluster();
+            volumesize >>= 1; // 512 bytes per block
+            volumesize *= volume.clusterCount();
+            volumesize >>= 10;
+
+            sprintf(buf, "%dGB", (int)((volumesize + 511) / 1000));
+            Serial.println(buf);
+            lcd.print("SD ");
+            lcd.println(buf);
+        }
+#endif
+        if (!SD.begin(SD_CS_PIN)) {
+            Serial.println("SD error");
+        }
+    } else {
+        lcd.print("No SD");
+        Serial.println("No SD");
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+#if 0
+void ShowSensorData(uint16_t pid, float data)
+{
+    uint16_t value;
+    Serial.print('[');
+    Serial.print(pid, HEX);
+    Serial.print("]=");
+    if (pid < 0xf000) {
+      value = (uint16_t)data;
+      Serial.print(value);
+    }
+
+    char buf[16];
+    switch (pid) {
+    case PID_RPM:
+        lcd.setCursor(64, 0);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt(value, 4);
+        break;
+    case PID_SPEED:
+        lcd.setCursor(0, 0);
+        lcd.setFont(FONT_SIZE_XLARGE);
+        lcd.printInt(value, 3);
+        break;
+    case PID_THROTTLE:
+        lcd.setCursor(24, 5);
+        lcd.setFont(FONT_SIZE_SMALL);
+        lcd.printInt(value, 3);
+        break;
+    case PID_INTAKE_TEMP:
+        lcd.setCursor(104, 5);
+        lcd.setFont(FONT_SIZE_SMALL);
+        lcd.printInt(value, 3);
+        break;
+    case PID_DISTANCE:
+        sprintf(buf, "%5ukm", value);
+        lcd.setFont(FONT_SIZE_SMALL);
+        lcd.setCursor(86, 6);
+        lcd.print(buf);
+        break;
+    case PID_GPS_LATITUDE: {
+        int32_t lat = (int32_t)(data * 100000);
+        sprintf(buf, "LAT:%d.%05ld  ", (int)(lat / 100000), lat % 100000);
+        lcd.setFont(FONT_SIZE_SMALL);
+        lcd.setCursor(0, 6);
+        lcd.print(buf);
+        //Serial.print(buf);
+        break;
+        }
+    case PID_GPS_LONGITUDE: {
+        int32_t lon = (int32_t)(data * 100000);
+        sprintf(buf, "LON:%d.%05ld  ", (int)(lon / 100000), lon % 100000);
+        lcd.setFont(FONT_SIZE_SMALL);
+        lcd.setCursor(0, 7);
+        lcd.print(buf);
+        //Serial.print(buf);
+        break;
+        }
+    }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+uint8_t getDataInt8(LOG_DATA_COMMAND& msg, int offset = 0)
+{
+  return msg.data[offset];
+}
+
+uint16_t getDataInt16(LOG_DATA_COMMAND& msg, int offset = 0)
+{
+  return *(uint16_t *)(&msg.data[offset]);
+}
+
+uint32_t getDataInt32(LOG_DATA_COMMAND& msg, int offset = 0)
+{
+  return *(uint32_t *)(&msg.data[offset]);
+}
+
+void sendLogFile(File logfile, byte noWait = 0)
+{
+    uint32_t timestamp = 0;
+    uint32_t dataSize = 0;
+    int value;
+    int pid;
+    char buf[64];
+    byte n = 0;
+
+    Serial.println("Start");
+    while (logfile.available()) {
+        byte c = logfile.read();
+        dataSize++;
+        if (c <= 13) {
+            // process one line
+            if (n > 0) do {
+                buf[n] = 0;
+                n = 0;
+                // TODO: parse line
+                int t = atoi(buf);
+                char *p = strchr(buf, ',');
+                if (!p++) break;
+                Serial.print(p);
+                Serial.write('\n');
+                int pid = hex2uint16(p);
+                p = strchr(p, ',');
+                if (!p++) break;
+                int value = atoi(p);
+                timestamp += t;
+                showData((byte)pid, value);
+                if (Serial.available()) {
+                    lcd.setCursor(0, 20);
+                    do {
+                        lcd.write(Serial.read());
+                    } while (Serial.available());
+                }
+                if (!noWait && t > 0) delay(t / REPLAY_SPEED);
+            } while(0);
+
+            // show time elapsed
+            uint16_t elapsed = timestamp / 1000;
+            lcd.setFont(FONT_SIZE_MEDIUM);
+            lcd.setCursor(260, 8);
+            lcd.printInt(elapsed / 60, 2);
+            lcd.write(':');
+            lcd.setFlags(FLAG_PAD_ZERO);
+            lcd.printInt(elapsed % 60, 2);
+            lcd.setFlags(0);
+
+            lcd.setCursor(260, 11);
+            lcd.printInt(dataSize >> 10, 4);
+
+            continue;
+        }
+        if (n >= sizeof(buf) - 1) continue;
+        buf[n++] = c;
+
+        //Show on LCD:
+
+        //Serial.print(" T:");
+        //Serial.print(ld.time);
+        //Serial.print(" F:");
+        //Serial.print(ld.flags);
+
+        // display KB counter
+#if 0
+        lcd.setCursor(96, 7);
+        lcd.setFont(FONT_SIZE_SMALL);
+        byte percent = (byte)(dataSendLen * 100 / logsize);
+        lcd.printInt(percent, 3);
+
+        Serial.print(' ');
+        Serial.print(percent);
+        Serial.println('%');
+#endif
+        //checkReceiveCommand();
+    } while (logfile.available());
+}
+
+void enumDirectoryAndSend(File dir)
+{
+    // Begin at the start of the directory
+    dir.rewindDirectory();
+
+    while(true) {
+        File entry =  dir.openNextFile();
+        if (! entry) {
+            // no more files
+            //Serial.println("**nomorefiles**");
+            break;
+        }
+
+        if (entry.isDirectory()) {
+            enumDirectoryAndSend(entry);
+        } else {
+            lcd.clear();
+            lcd.setFont(FONT_SIZE_MEDIUM);
+            lcd.print(entry.name());
+            delay(1000);
+            initScreen();
+            sendLogFile(entry);
+        }
+        entry.close();
+    }
+}
+
+void loop()
+{
+#if 0
+   if (!checkReceiveCommand()) {
+        delay(100);
+   }
+#else
+    File root = SD.open("/FRMATICS");
+    if (!root || !root.isDirectory()) {
+        lcd.println("NO LOG");
+        Serial.println("NO LOG");
+    } else {
+        enumDirectoryAndSend(root);
+        root.close();
+    }
+    delay(5000);
+#endif
+
+}
-- 
cgit v1.2.3