From f4377c79916d7a3094cdcbe213be8fa2c64c0aa0 Mon Sep 17 00:00:00 2001 From: Stanley Huang Date: Thu, 9 May 2013 01:33:05 +0800 Subject: OBD-II/GPS/G-force logger --- obdlogger/obdlogger.ino | 560 +++++++++++++++++++++++++++--------------------- 1 file changed, 320 insertions(+), 240 deletions(-) (limited to 'obdlogger/obdlogger.ino') diff --git a/obdlogger/obdlogger.ino b/obdlogger/obdlogger.ino index 1f6bd2b..0366600 100644 --- a/obdlogger/obdlogger.ino +++ b/obdlogger/obdlogger.ino @@ -1,5 +1,5 @@ /************************************************************************* -* Arduino OBD-II Data Logger +* Arduino GPS/OBD-II/G-Force Data Logger * Distributed under GPL v2.0 * Copyright (c) 2013 Stanley Huang * All rights reserved. @@ -14,314 +14,408 @@ #include "MPU6050.h" //#define SD_CS_PIN 4 // ethernet shield with SD -//#define SD_CS_PIN 7 // microduino -#define SD_CS_PIN 10 // SD breakout +#define SD_CS_PIN 7 // microduino +//#define SD_CS_PIN 10 // SD breakout -#define GPS_BAUDRATE 4800 /* bps */ +#define GPS_BAUDRATE 38400 /* bps */ -// addition PIDs (non-OBD) +#define STATE_SD_READY 0x1 +#define STATE_OBD_READY 0x2 +#define STATE_GPS_CONNECTED 0x4 +#define STATE_GPS_READY 0x8 +#define STATE_ACC_READY 0x10 + +// additional PIDs (non-OBD) #define PID_GPS_DATETIME 0xF01 #define PID_GPS_COORDINATE 0xF02 #define PID_GPS_ALTITUDE 0xF03 #define PID_GPS_SPEED 0xF04 -#define DATASET_INTERVAL 250 /* ms */ +#define DATASET_INTERVAL 100 /* ms */ +#define FILE_NAME_FORMAT "OBD%05d.CSV" -// GPS logging can only be enabled when there is additional serial UART -#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega644P__) -#define ENABLE_GPS +// GPS logging can only be enabled when there is additional hardware serial UART +#if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) +#define GPSUART Serial3 +#elif defined(__AVR_ATmega644P__) +#define GPSUART Serial1 #endif -// GPS -#ifdef ENABLE_GPS +#ifdef GPSUART TinyGPS gps; -#endif +#endif // GPSUART // SD card Sd2Card card; SdVolume volume; File sdfile; -// LCD +// enable one LCD LCD_OLED lcd; /* for I2C OLED module */ //LCD_PCD8544 lcd; /* for LCD4884 shield or Nokia 5100 screen module */ //LCD_1602 lcd; /* for LCD1602 shield */ -static uint32_t filesize = 0; -static uint32_t datacount = 0; - -void ProcessGPSData(char c); +static uint32_t fileSize = 0; +static uint32_t lastFileSize = 0; +static uint32_t lastDataTime; +static int startDistance = 0; +static uint16_t fileIndex = 0; -class COBD2 : public COBD +class CLogger : public COBD { public: - void DataTimeout() + void InitIdleLoop() { -#ifdef ENABLE_GPS + // called while initializing +#ifdef GPSUART // detect GPS signal - while (Serial1.available()) { - if (gps.encode(Serial1.read())) { - if (datacount == 0) { - lcd.setCursor(9, 3); - lcd.print("GPS:Yes"); - } + if (GPSUART.available()) { + if (gps.encode(GPSUART.read())) { + state |= STATE_SD_READY; } } + if (state & STATE_ACC_READY) { + accel_t_gyro_union data; + MPU6050_readout(&data); + char buf[8]; + sprintf(buf, "X:%4d", data.value.x_accel / 190); + lcd.setCursor(9, 1); + lcd.print(buf); + sprintf(buf, "Y:%4d", data.value.y_accel / 190); + lcd.setCursor(9, 2); + lcd.print(buf); + sprintf(buf, "Z:%4d", data.value.z_accel / 190); + lcd.setCursor(9, 3); + lcd.print(buf); + delay(50); + } #endif } -}; - -COBD2 obd; - -bool ShowCardInfo() -{ - if (card.init(SPI_HALF_SPEED, SD_CS_PIN)) { - const char* type; - char buf[20]; - - switch(card.type()) { - case SD_CARD_TYPE_SD1: - type = "SD1"; - break; - case SD_CARD_TYPE_SD2: - type = "SD2"; - break; - case SD_CARD_TYPE_SDHC: - type = "SDHC"; - break; - default: - type = "N/A"; - } - - sprintf(buf, "SD Type: %s", type); - lcd.setCursor(0, 0); - lcd.print(buf); - if (!volume.init(card)) { - lcd.setCursor(0, 1); - lcd.print("No FAT!"); - return false; - } - - uint32_t volumesize = volume.blocksPerCluster(); - volumesize >>= 1; // 512 bytes per block - volumesize *= volume.clusterCount(); - volumesize >>= 10; - - sprintf(buf, "SD Size: %dMB", (int)volumesize); - lcd.setCursor(0, 1); - lcd.print(buf); - return true; - } else { + void DataIdleLoop() + { + // called while waiting for OBD-II response +#ifdef GPSUART + if (GPSUART.available()) + ProcessGPS(); +#endif + } + void ShowStates() + { lcd.setCursor(0, 1); - lcd.print("No SD Card "); - return false; - } -} + lcd.print("OBD:"); + lcd.print((state & STATE_OBD_READY) ? "Yes" : "No"); + lcd.setCursor(0, 2); + lcd.print("GPS:"); + if (state & STATE_GPS_READY) + lcd.print("Yes"); + else if (state & STATE_GPS_CONNECTED) + lcd.print("--"); + else + lcd.print("No"); + + lcd.setCursor(0, 3); + lcd.print("ACC:"); + lcd.print((state & STATE_ACC_READY) ? "Yes" : "No"); + } + void Setup() + { + state = 0; -static uint32_t lastTime; -static int startDistance = 0; + lcd.clear(); -static void CheckSD() -{ - uint16_t fileidx = 0; - char filename[13]; - for (;;) { - if (!ShowCardInfo()) { - delay(1000); - continue; - } + // init SD card + if (CheckSD()) state |= STATE_SD_READY; + ShowStates(); - delay(1000); - SD.begin(SD_CS_PIN); + if (MPU6050_init() == 0) state |= STATE_ACC_READY; + ShowStates(); - // determine file name - for (uint16_t index = 1; index < 65535; index++) { - sprintf(filename, "OBD%05d.CSV", index); - if (!SD.exists(filename)) { - fileidx = index; +#ifdef GPSUART + unsigned long t = millis(); + do { + if (GPSUART.available()) { + state |= STATE_GPS_CONNECTED; break; } - } - if (fileidx) break; - lcd.setCursor(0, 2); - lcd.print("SD error "); - } + } while (millis() - t <= 1000); +#endif - lcd.clearLine(2); - lcd.setCursor(0, 2); - lcd.print(filename); + do { + ShowStates(); + } while (!Init()); - filesize = 0; - sdfile = SD.open(filename, FILE_WRITE); - if (sdfile) { - return; - } + state |= STATE_OBD_READY; + ReadSensor(PID_DISTANCE, startDistance); - lcd.setCursor(0, 2); - lcd.print("File error"); -} + ShowStates(); + delay(1000); -void InitScreen() -{ - byte n = lcd.getLines() <= 2 ? 6 : 9; - lcd.clear(); - lcd.backlight(true); - lcd.setCursor(n, 0); - lcd.print("kph"); - lcd.setCursor(n, 1); - lcd.print("rpm"); -} + // open file for logging + if (!(state & STATE_SD_READY)) { + do { + delay(1000); + } while (!CheckSD()); + state |= STATE_SD_READY; + ShowStates(); + } -void setup() -{ - lcd.begin(); - lcd.clear(); - lcd.backlight(true); - lcd.print("OBD/GPS Logger"); - lcd.setCursor(0, 1); - lcd.print("Initializing..."); - delay(2000); + fileSize = 0; + for (;;) { + char filename[13]; + sprintf(filename, FILE_NAME_FORMAT, fileIndex); + sdfile = SD.open(filename, FILE_WRITE); + if (sdfile) break; + lcd.setCursor(0, 2); + lcd.print("SD Error"); + delay(1000); + CheckSD(); + } - // start serial communication at the adapter defined baudrate - OBDUART.begin(OBD_SERIAL_BAUDRATE); + InitScreen(); + lastDataTime = millis(); + } + void LogData(byte pid) + { + uint32_t start = millis(); - // init SD card - pinMode(SS, OUTPUT); - CheckSD(); + // send a query to OBD adapter for specified OBD-II pid + int value; + if (ReadSensor(pid, value)) { + ProcessOBD(pid, value); + } - // initiate OBD-II connection until success - lcd.clearLine(3); - lcd.setCursor(0, 3); - lcd.print("OBD:No"); + // flush SD data every 1KB + if (fileSize - lastFileSize >= 1024) { + sdfile.flush(); + // display logged data size + char buf[7]; + sprintf(buf, "%4uKB", (int)(fileSize >> 10)); + lcd.setCursor(10, 3); + lcd.print(buf); + lastFileSize = fileSize; + } -#ifdef ENABLE_GPS - Serial1.begin(GPS_BAUDRATE); - unsigned long t = millis(); - do { - if (Serial1.available()) { - lcd.setCursor(9, 3); - lcd.print("GPS:No"); - break; + if (state & STATE_ACC_READY) { + ProcessAccelerometer(); } - } while (millis() - t <= 1000); + + // if OBD response is very fast, go on processing GPS data for a while + while (millis() - start < DATASET_INTERVAL) { +#ifdef GPSUART + if (GPSUART.available()) + ProcessGPS(); #endif + } + } + void Reconnect() + { + sdfile.close(); + lcd.clear(); + lcd.print("Reconnecting"); + state = 0; + digitalWrite(SD_CS_PIN, LOW); + for (int i = 0; !Init(); i++) { + if (i == 10) lcd.clear(); + } + Setup(); + } +private: + bool CheckSD() + { + lcd.setCursor(0, 0); + if (card.init(SPI_HALF_SPEED, SD_CS_PIN)) { + const char* type; + char buf[20]; - while (!obd.Init()); + switch(card.type()) { + case SD_CARD_TYPE_SD1: + type = "SD1"; + break; + case SD_CARD_TYPE_SD2: + type = "SD2"; + break; + case SD_CARD_TYPE_SDHC: + type = "SDHC"; + break; + default: + type = "SDx"; + } - obd.ReadSensor(PID_DISTANCE, startDistance); + lcd.print(type); + lcd.write(' '); + if (!volume.init(card)) { + lcd.print("No FAT!"); + return false; + } - lcd.setCursor(0, 3); - lcd.setCursor(4, 3); - lcd.print("Yes"); - delay(1000); + uint32_t volumesize = volume.blocksPerCluster(); + volumesize >>= 1; // 512 bytes per block + volumesize *= volume.clusterCount(); + volumesize >>= 10; - InitScreen(); - lastTime = millis(); -} + sprintf(buf, "%dG", (int)(volumesize + 511) >> 10); + lcd.print(buf); + } else { + lcd.print("No SD Card "); + return false; + } -static char databuf[32]; -static int len = 0; -static int value = 0; + if (!SD.begin(SD_CS_PIN)) { + lcd.setCursor(8, 0); + lcd.print("Bad SD"); + return false; + } -#ifdef ENABLE_GPS -void ProcessGPSData(char c) -{ - if (!gps.encode(c)) - return; + char filename[13]; + // now determine log file name + for (fileIndex = 1; fileIndex; fileIndex++) { + sprintf(filename, FILE_NAME_FORMAT, fileIndex); + if (!SD.exists(filename)) { + break; + } + } + if (!fileIndex) { + lcd.setCursor(8, 8); + lcd.print("Bad File"); + return false; + } - // parsed GPS data is ready - uint32_t curTime = millis(); - unsigned long fix_age; + filename[8] = 0; + lcd.setCursor(11, 0); + lcd.print(filename + 3); + return true; + } + void InitScreen() { + lcd.clear(); + lcd.backlight(true); + lcd.setCursor(lcd.getLines() <= 2 ? 4 : 7, 0); + lcd.print("kph"); + lcd.setCursor(5, 1); + lcd.print("rpm"); + lcd.setCursor(9, 1); + lcd.print("THR: %"); + } + void ProcessGPS() + { + char buf[32]; + // process GPS data + char c = GPSUART.read(); + if (!gps.encode(c)) return; + + // parsed GPS data is ready + uint32_t dataTime = millis(); + uint16_t elapsed = (uint16_t)(dataTime - lastDataTime); + unsigned long fix_age; + int len; + unsigned long date, time; gps.get_datetime(&date, &time, &fix_age); - len = sprintf(databuf, "%d,F01,%ld %ld\n", (int)(curTime - lastTime), date, time); - sdfile.write((uint8_t*)databuf, len); - } + len = sprintf(buf, "%u,F01,%ld %ld\n", elapsed, date, time); + sdfile.write((uint8_t*)buf, len); - { long lat, lon; gps.get_position(&lat, &lon, &fix_age); - len = sprintf(databuf, "%d,F02,%ld %ld\n", (int)(curTime - lastTime), lat, lon); - sdfile.write((uint8_t*)databuf, len); + len = sprintf(buf, "%u,F02,%ld %ld\n", elapsed, lat, lon); + sdfile.write((uint8_t*)buf, len); + // display LAT/LON if screen is big enough if (lcd.getLines() > 3) { - sprintf(databuf, "%3d.%ld", (int)(lat / 100000), lat % 100000); + if (((unsigned int)dataTime / 1000) & 1) + sprintf(buf, "LAT: %d.%ld ", (int)(lat / 100000), lat % 100000); + else + sprintf(buf, "LON: %d.%ld ", (int)(lon / 100000), lon % 100000); lcd.setCursor(0, 2); - lcd.print(databuf); - sprintf(databuf, "%3d.%ld", (int)(lon / 100000), lon % 100000); - lcd.setCursor(0, 3); - lcd.print(databuf); + lcd.print(buf); } - } - len = sprintf(databuf, "%d,F03,%ld %ld\n", (int)(curTime - lastTime), gps.speed() * 1852 / 100); - sdfile.write((uint8_t*)databuf, len); - len = sprintf(databuf, "%d,F04,%ld %ld\n", (int)(curTime - lastTime), gps.altitude()); - sdfile.write((uint8_t*)databuf, len); - lastTime = curTime; -} -#endif - -void RetrieveData(byte pid) -{ - // issue a query for OBD - obd.Query(pid); + len = sprintf(buf, "%u,F03,%ld\n", elapsed, gps.speed() * 1852 / 100); + sdfile.write((uint8_t*)buf, len); - // flush data in the buffer - if (len > 0) { - sdfile.write((uint8_t*)databuf, len); + len = sprintf(buf, "%u,F04,%ld\n", elapsed, gps.altitude()); + sdfile.write((uint8_t*)buf, len); + lastDataTime = dataTime; + } - char* buf = databuf; // data in buffer saved, free for other use - if (datacount % 100 == 99) { - sdfile.flush(); - sprintf(buf, "%4uKB", (int)(filesize >> 10)); - lcd.setCursor(10, 3); - lcd.print(buf); - } + void ProcessAccelerometer() + { + accel_t_gyro_union data; + MPU6050_readout(&data); + char buf[20]; + sprintf(buf, "%d %d %d ", data.value.x_accel / 190, data.value.y_accel / 190, data.value.z_accel / 190); + lcd.setCursor(0, 2); + lcd.print(buf); + } + void ProcessOBD(byte pid, int value) + { + char buf[32]; + uint32_t dataTime = millis(); + uint16_t elapsed = (uint16_t)(dataTime - lastDataTime); + byte len = sprintf(buf, "%u,%X,%d\n", elapsed, pid, value); + // log OBD data + sdfile.write((uint8_t*)buf, len); + fileSize += len; + + // display OBD data switch (pid) { case PID_RPM: sprintf(buf, "%4u", (unsigned int)value % 10000); - lcd.setCursor(0, 0); - lcd.printLarge(buf); + lcd.setCursor(0, 1); + lcd.print(buf); break; case PID_SPEED: sprintf(buf, "%3u", (unsigned int)value % 1000); - lcd.setCursor(2, 1); + lcd.setCursor(0, 0); lcd.printLarge(buf); break; + case PID_THROTTLE: + sprintf(buf, "%2d", value % 100); + lcd.setCursor(13, 1); + lcd.print(buf); + break; case PID_DISTANCE: if (value >= startDistance) { - sprintf(buf, "%4dkm", value - startDistance); - lcd.setCursor(10, 2); + sprintf(buf, "%4ukm", ((uint16_t)value - startDistance) % 1000); + lcd.setCursor(10, 0); lcd.print(buf); } break; } + lastDataTime = dataTime; } + byte state; +}; -#ifdef ENABLE_GPS - while (Serial1.available()) { - ProcessGPSData(Serial1.read()); - } +CLogger logger; + +void setup() +{ + lcd.begin(); + lcd.clear(); + lcd.backlight(true); + lcd.print("OBD/GPS Logger"); + lcd.setCursor(0, 1); + lcd.print("Initializing..."); + + Wire.begin(); + + // start serial communication at the adapter defined baudrate + OBDUART.begin(OBD_SERIAL_BAUDRATE); +#ifdef GPSUART + GPSUART.begin(GPS_BAUDRATE); #endif - if (obd.GetResponse(pid, value)) { - uint32_t curTime = millis(); - len = sprintf(databuf, "%d,%X,%d\n", (int)(curTime - lastTime), pid, value); - filesize += len; - datacount++; - lastTime = curTime; - return; - } - len = 0; + pinMode(SS, OUTPUT); + delay(1000); + + logger.Setup(); } void loop() { - static char count = 0; + static byte count = 0; unsigned long t = millis(); switch (count++) { @@ -329,37 +423,23 @@ void loop() case 64: case 128: case 192: - RetrieveData(PID_DISTANCE); + logger.LogData(PID_DISTANCE); break; case 4: - RetrieveData(PID_COOLANT_TEMP); + logger.LogData(PID_COOLANT_TEMP); break; case 20: - RetrieveData(PID_INTAKE_TEMP); + logger.LogData(PID_INTAKE_TEMP); break; } - RetrieveData(PID_RPM); - RetrieveData(PID_SPEED); - //RetrieveData(PID_THROTTLE); - //RetrieveData(PID_ABS_ENGINE_LOAD); + logger.LogData(PID_RPM); + logger.LogData(PID_SPEED); + logger.LogData(PID_THROTTLE); + //LogData(PID_ABS_ENGINE_LOAD); - if (obd.errors >= 5) { - sdfile.close(); - lcd.clear(); - lcd.print("Reconnecting..."); - digitalWrite(SD_CS_PIN, LOW); - for (int i = 0; !obd.Init(); i++) { - if (i == 10) lcd.clear(); - } - digitalWrite(SD_CS_PIN, HIGH); - CheckSD(); - delay(1000); - InitScreen(); + if (logger.errors >= 5) { + logger.Reconnect(); count = 0; - return; } - - t = millis() - t; - if (t < DATASET_INTERVAL) delay(DATASET_INTERVAL - t); } -- cgit v1.2.3