summaryrefslogtreecommitdiff
path: root/megaloggerHD/megaloggerHD.ino
diff options
context:
space:
mode:
Diffstat (limited to 'megaloggerHD/megaloggerHD.ino')
-rw-r--r--megaloggerHD/megaloggerHD.ino815
1 files changed, 815 insertions, 0 deletions
diff --git a/megaloggerHD/megaloggerHD.ino b/megaloggerHD/megaloggerHD.ino
new file mode 100644
index 0000000..83987ad
--- /dev/null
+++ b/megaloggerHD/megaloggerHD.ino
@@ -0,0 +1,815 @@
+/*************************************************************************
+* Reference code for Freematics OBD-II UART Adapter
+* Works with Freematics OBD-II Telematics Advanced Kit
+* Visit http://freematics.com for more information
+* Distributed under BSD license
+* Written by Stanley Huang <support@freematics.com.au>
+*************************************************************************/
+
+#include <Arduino.h>
+#include <SPI.h>
+#include <Wire.h>
+#include <OBD.h>
+#include <MultiLCD.h>
+#include <TinyGPS.h>
+#include "config.h"
+#ifdef OBD_ADAPTER_I2C
+#include <I2Cdev.h>
+#include <MPU9150.h>
+#endif
+#if ENABLE_DATA_LOG
+#include <SD.h>
+#endif
+#include "Narcoleptic.h"
+#include "images.h"
+#include "datalogger.h"
+
+// logger states
+#define STATE_SD_READY 0x1
+#define STATE_OBD_READY 0x2
+#define STATE_GPS_CONNECTED 0x4
+#define STATE_GPS_READY 0x8
+#define STATE_MEMS_READY 0x10
+#define STATE_GUI_ON 0x20
+
+#if USE_GPS
+// GPS logging can only be enabled when there is additional hardware serial UART
+#define GPSUART Serial2
+TinyGPS gps;
+#endif
+
+
+static uint8_t lastFileSize = 0;
+static uint32_t lastRefreshTime = 0;
+static uint32_t distance = 0;
+static uint32_t startTime = 0;
+static uint16_t lastSpeed = 0;
+static uint32_t lastSpeedTime = 0;
+static uint32_t gpsDate = 0;
+static uint32_t lastMemsDataTime = 0;
+#if USE_GPS
+static uint32_t lastGPSDataTime = 0;
+static int gpsSpeed = -1;
+#endif
+
+#ifdef OBD_ADAPTER_I2C
+MPU6050 accelgyro;
+#endif
+
+byte state = 0;
+
+void processMEMS();
+void processGPS();
+
+CDataLogger logger;
+
+#ifdef OBD_ADAPTER_I2C
+class CMyOBD : public COBDI2C
+#else
+class CMyOBD : public COBD
+#endif
+{
+public:
+ void dataIdleLoop()
+ {
+ if (!(state & STATE_GUI_ON)) {
+ delay(10);
+ return;
+ }
+
+#if USE_GPS
+ uint32_t t = millis();
+ while (GPSUART.available() && millis() - t < MAX_GPS_PROCESS_TIME) {
+ processGPS();
+ }
+#endif
+ }
+};
+
+CMyOBD obd;
+
+void setColorByValue(int value, int threshold1, int threshold2, int threshold3)
+{
+ if (value < 0) value = -value;
+ if (value < threshold1) {
+ lcd.setColor(RGB16_WHITE);
+ } else if (value < threshold2) {
+ byte n = (uint32_t)(threshold2 - value) * 255 / (threshold2 - threshold1);
+ lcd.setColor(255, 255, n);
+ } else if (value < threshold3) {
+ byte n = (uint32_t)(threshold3 - value) * 255 / (threshold3 - threshold2);
+ lcd.setColor(255, n, 0);
+ } else {
+ lcd.setColor(255, 0, 0);
+ }
+}
+
+void showPIDData(byte pid, int value)
+{
+ char buf[8];
+ switch (pid) {
+ case PID_RPM:
+ lcd.setFontSize(FONT_SIZE_XLARGE);
+ lcd.setCursor(14, 8);
+ if (value >= 10000) break;
+ setColorByValue(value, 2500, 3500, 5000);
+ lcd.printInt(value, 6);
+ break;
+ case PID_SPEED:
+ if (value < 1000) {
+ lcd.setFontSize(FONT_SIZE_XLARGE);
+ lcd.setCursor(50,3);
+ setColorByValue(value, 60, 100, 160);
+ lcd.printInt(value, 3);
+
+#if USE_GPS
+ if (gpsSpeed != -1) {
+ lcd.setFontSize(FONT_SIZE_SMALL);
+ lcd.setCursor(110, 3);
+ lcd.setColor(RGB16_YELLOW);
+ int diff = gpsSpeed - value;
+ if (diff >= 0) {
+ lcd.write('+');
+ lcd.printInt(diff);
+ } else {
+ lcd.write('-');
+ lcd.printInt(-diff);
+ }
+ lcd.write(' ');
+ }
+#endif
+ }
+ break;
+ case PID_ENGINE_LOAD:
+ lcd.setFontSize(FONT_SIZE_XLARGE);
+ lcd.setCursor(50, 13);
+ if (value >= 100) value = 99;
+ setColorByValue(value, 75, 80, 100);
+ lcd.printInt(value, 3);
+ break;
+ case PID_THROTTLE:
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(102, 27);
+ if (value >= 100) value = 99;
+ setColorByValue(value, 50, 75, 100);
+ lcd.printInt(value, 2);
+ break;
+ case PID_ENGINE_FUEL_RATE:
+ if (value < 100) {
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(102, 30);
+ lcd.printInt(value, 2);
+ }
+ break;
+ case PID_INTAKE_TEMP:
+ if (value >= 0 && value < 100) {
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(102, 34);
+ lcd.printInt(value, 2);
+ }
+ break;
+ }
+ lcd.setColor(RGB16_WHITE);
+}
+
+void fadeOutScreen()
+{
+ // fade out backlight
+ for (int n = 254; n >= 0; n--) {
+ lcd.setBackLight(n);
+ delay(3);
+ }
+}
+
+void fadeInScreen()
+{
+ for (int n = 1; n <= 255; n++) {
+ lcd.setBackLight(n);
+ delay(6);
+ }
+}
+
+void initScreen()
+{
+ lcd.clear();
+ lcd.draw2x(frame0[0], 78, 78);
+ lcd.setXY(162, 0);
+ lcd.draw2x(frame0[0], 78, 78);
+ lcd.setXY(324, 0);
+ lcd.draw2x(frame0[0], 78, 78);
+ lcd.setXY(0, 164);
+ lcd.draw2x(frame0[0], 78, 78);
+ lcd.setXY(162, 164);
+ lcd.draw2x(frame0[0], 78, 78);
+ lcd.setXY(324, 164);
+ lcd.draw2x(frame0[0], 78, 78);
+
+ lcd.setColor(RGB16_CYAN);
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(110,4);
+ lcd.print("kph");
+ lcd.setCursor(110, 9);
+ lcd.print("RPM");
+ lcd.setFontSize(FONT_SIZE_SMALL);
+ lcd.setCursor(110, 14);
+ lcd.print("ENGINE");
+ lcd.setCursor(110, 15);
+ lcd.print("LOAD %");
+
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(208, 3);
+ lcd.print("Elapsed");
+ lcd.setCursor(204, 8);
+ lcd.print("Distance");
+ lcd.setCursor(180, 13);
+ lcd.print("Average Speed");
+
+ lcd.setCursor(16, 24);
+ lcd.print("Battery V");
+ lcd.setCursor(16, 27);
+ lcd.print("Throttle %");
+ lcd.setCursor(16, 30);
+ lcd.print("Fuel L/h");
+ lcd.setCursor(16, 33);
+ lcd.print("Intake: C");
+
+ lcd.setCursor(180, 24);
+ lcd.print("UTC:");
+ lcd.setCursor(180, 27);
+ lcd.print("LAT:");
+ lcd.setCursor(180, 30);
+ lcd.print("LNG:");
+ lcd.setCursor(180, 33);
+ lcd.print("ALT:");
+ lcd.setCursor(180, 36);
+ lcd.print("SAT:");
+
+ lcd.setCursor(340, 3);
+ lcd.print("Accelerometer");
+ lcd.setCursor(356, 8);
+ lcd.print("Gyroscope");
+
+ lcd.setCursor(348, 24);
+ lcd.print("OBD Interval");
+
+ lcd.setCursor(348, 29);
+ lcd.print("GPS Interval");
+
+ lcd.setCursor(352, 34);
+ lcd.print("Data Size");
+
+ //lcd.setColor(0xFFFF);
+ /*
+ lcd.setCursor(32, 4);
+ lcd.print("%");
+ lcd.setCursor(68, 5);
+ lcd.print("Intake Air");
+ lcd.setCursor(112, 4);
+ lcd.print("C");
+ */
+
+ state |= STATE_GUI_ON;
+
+ fadeInScreen();
+}
+
+#if ENABLE_DATA_LOG
+bool checkSD()
+{
+ Sd2Card card;
+ SdVolume volume;
+ state &= ~STATE_SD_READY;
+ pinMode(SS, OUTPUT);
+
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ if (card.init(SPI_HALF_SPEED, SD_CS_PIN)) {
+ const char* type;
+ 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";
+ }
+
+ lcd.print(type);
+ lcd.write(' ');
+ if (!volume.init(card)) {
+ lcd.print("No FAT!");
+ return false;
+ }
+
+ uint32_t volumesize = volume.blocksPerCluster();
+ volumesize >>= 1; // 512 bytes per block
+ volumesize *= volume.clusterCount();
+ volumesize >>= 10;
+
+ lcd.print((int)volumesize);
+ lcd.print("MB");
+ } else {
+ lcd.println("No SD Card");
+ return false;
+ }
+
+ if (!SD.begin(SD_CS_PIN)) {
+ lcd.println("Bad SD");
+ return false;
+ }
+
+ state |= STATE_SD_READY;
+ return true;
+}
+#endif
+
+#if USE_GPS
+void processGPS()
+{
+ // process GPS data
+ char c = GPSUART.read();
+ if (!gps.encode(c))
+ return;
+
+ // parsed GPS data is ready
+ uint32_t time;
+ uint32_t date;
+
+ logger.dataTime = millis();
+
+ gps.get_datetime(&date, &time, 0);
+ if (date != gpsDate) {
+ // log date only if it's changed and valid
+ int year = date % 100;
+ if (date < 1000000 && date >= 10000 && year >= 15 && (gpsDate == 0 || year - (gpsDate % 100) <= 1)) {
+ logger.logData(PID_GPS_DATE, (int32_t)date);
+ gpsDate = date;
+ }
+ }
+ logger.logData(PID_GPS_TIME, (int32_t)time);
+
+ int32_t lat, lng;
+ gps.get_position(&lat, &lng, 0);
+
+ byte sat = gps.satellites();
+
+ // show GPS data interval
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ if (lastGPSDataTime) {
+ lcd.setCursor(380, 31);
+ lcd.printInt((uint16_t)logger.dataTime - lastGPSDataTime);
+ lcd.print("ms");
+ lcd.printSpace(2);
+ }
+
+ // keep current data time as last GPS time
+ lastGPSDataTime = logger.dataTime;
+
+ // display UTC date/time
+ lcd.setFontSize(FONT_SIZE_SMALL);
+ lcd.setCursor(216, 24);
+ lcd.setFlags(FLAG_PAD_ZERO);
+ lcd.printLong(date, 6);
+ lcd.setCursor(216, 25);
+ lcd.printLong(time, 8);
+
+ // display latitude
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(216, 27);
+ lcd.print((float)lat / 100000, 5);
+ // display longitude
+ lcd.setCursor(216, 30);
+ lcd.print((float)lng / 100000, 5);
+ // log latitude/longitude
+ logger.logData(PID_GPS_LATITUDE, lat);
+ logger.logData(PID_GPS_LONGITUDE, lng);
+
+ // display altitude
+ int32_t alt = gps.altitude();
+ lcd.setFlags(0);
+ if (alt > -1000000 && alt < 1000000) {
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(216, 33);
+ lcd.print(alt / 100);
+ lcd.print("m ");
+ }
+ // log altitude
+ logger.logData(PID_GPS_ALTITUDE, (int)(alt / 100));
+
+ // display number of satellites
+ if (sat < 100) {
+ lcd.setCursor(216, 36);
+ lcd.printInt(sat);
+ lcd.write(' ');
+ }
+
+ // only log these data when satellite status is good
+ if (sat >= 3) {
+ gpsSpeed = gps.speed() * 1852 / 100000;
+ logger.logData(PID_GPS_SPEED, gpsSpeed);
+ }
+}
+#endif
+
+void processMEMS()
+{
+ int ax = 0, ay = 0, az = 0;
+ int gx = 0, gy = 0, gz = 0;
+
+ if (logger.dataTime - lastMemsDataTime < ACC_DATA_INTERVAL) {
+ return;
+ }
+
+#ifdef OBD_ADAPTER_I2C
+ accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
+#else
+ obd.readAccel(ax, ay, az);
+ obd.readGyro(gx, gy, gz);
+#endif
+
+ logger.dataTime = millis();
+
+ ax /= ACC_DATA_RATIO;
+ ay /= ACC_DATA_RATIO;
+ az /= ACC_DATA_RATIO;
+ gx /= GYRO_DATA_RATIO;
+ gy /= GYRO_DATA_RATIO;
+ gz /= GYRO_DATA_RATIO;
+
+ // display MEMS data
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(362, 5);
+ setColorByValue(ax, 50, 100, 200);
+ lcd.print(ax);
+ setColorByValue(ay, 50, 100, 200);
+ lcd.write('/');
+ lcd.print(ay);
+ setColorByValue(az, 50, 100, 200);
+ lcd.write('/');
+ lcd.print(az);
+ Serial.println(az);
+ lcd.printSpace(8);
+
+ // display gyro data
+ lcd.setCursor(374, 10);
+ lcd.setColor(RGB16_WHITE);
+ lcd.print(gx);
+ lcd.write('/');
+ lcd.print(gy);
+ lcd.write('/');
+ lcd.print(gz);
+ lcd.printSpace(8);
+
+ // log x/y/z of accelerometer
+ logger.logData(PID_ACC, ax, ay, az);
+ // log x/y/z of gyro meter
+ logger.logData(PID_GYRO, gx, gy, gz);
+
+ lastMemsDataTime = logger.dataTime;
+}
+
+void logOBDData(byte pid, int value)
+{
+ char buffer[64];
+ // send query for OBD-II PID
+ logger.dataTime = millis();
+ // display data
+ showPIDData(pid, value);
+
+ // log data to SD card
+ logger.logData(0x100 | pid, value);
+
+ if (pid == PID_SPEED) {
+ // estimate distance travelled since last speed update
+ distance += (uint32_t)(value + lastSpeed) * (logger.dataTime - lastSpeedTime) / 6000;
+ // display speed
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(220, 10);
+ lcd.printInt(distance / 1000);
+ lcd.write('.');
+ lcd.printInt(((uint16_t)distance % 1000) / 100);
+ lcd.print(" km");
+ // calculate and display average speed
+ int avgSpeed = (unsigned long)distance * 3600 / (millis() - startTime);
+ lcd.setCursor(220, 15);
+ lcd.printInt(avgSpeed);
+ lcd.print(" km/h");
+
+ lastSpeed = value;
+ lastSpeedTime = logger.dataTime;
+ }
+#if ENABLE_DATA_LOG
+ // flush SD data every 1KB
+ byte dataSizeKB = logger.dataSize >> 10;
+ if (dataSizeKB != lastFileSize) {
+ logger.flushFile();
+ lastFileSize = dataSizeKB;
+ // display logged data size
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(380, 36);
+ lcd.print((unsigned int)(logger.dataSize >> 10));
+ lcd.print("KB");
+ }
+#endif
+}
+
+void showECUCap()
+{
+ static const byte PROGMEM pidlist[] = {PID_ENGINE_LOAD, PID_COOLANT_TEMP, PID_FUEL_PRESSURE, PID_INTAKE_MAP, PID_RPM, PID_SPEED, PID_TIMING_ADVANCE, PID_INTAKE_TEMP, PID_MAF_FLOW, PID_THROTTLE, PID_AUX_INPUT,
+ PID_EGR_ERROR, PID_COMMANDED_EVAPORATIVE_PURGE, PID_FUEL_LEVEL, PID_CONTROL_MODULE_VOLTAGE, PID_ABSOLUTE_ENGINE_LOAD, PID_AMBIENT_TEMP, PID_COMMANDED_THROTTLE_ACTUATOR, PID_ETHANOL_FUEL,
+ PID_FUEL_RAIL_PRESSURE};
+
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setColor(RGB16_WHITE);
+ for (byte i = 0, n = 0; i < sizeof(pidlist) / sizeof(pidlist[0]); i++) {
+ byte pid = pgm_read_byte(pidlist + i);
+ if (obd.isValidPID(pid)) {
+ lcd.setCursor(348, n);
+ n += 2;
+ lcd.write('0');
+ lcd.print((int)pid | 0x100, HEX);
+ }
+ }
+ int values[sizeof(pidlist)];
+ bool scanned = false;
+ bool touched = false;
+ for (uint32_t t = millis(); millis() - t < 5000; ) {
+ for (byte i = 0, n = 0; i < sizeof(pidlist) / sizeof(pidlist[0]); i++) {
+ byte pid = pgm_read_byte(pidlist + i);
+ if (obd.isValidPID(pid)) {
+ int value;
+ lcd.setCursor(400 , n);
+ n += 2;
+ if (obd.readPID(pid, value)) {
+ if (!scanned || value == values[i])
+ lcd.setColor(RGB16_CYAN);
+ else if (value > values[i])
+ lcd.setColor(RGB16_GREEN);
+ else
+ lcd.setColor(RGB16_RED);
+ byte n = lcd.print(value);
+ for (; n < 4; n++) lcd.print(' ');
+ values[i] = value;
+ } else {
+ lcd.setColor(RGB16_YELLOW);
+ lcd.print("N/A");
+ }
+ }
+ }
+ scanned = true;
+ }
+}
+
+void reconnect()
+{
+ fadeOutScreen();
+#if ENABLE_DATA_LOG
+ logger.closeFile();
+#endif
+ lcd.clear();
+ state &= ~(STATE_OBD_READY | STATE_GUI_ON);
+ //digitalWrite(SD_CS_PIN, LOW);
+ for (;;) {
+ if (!obd.init())
+ continue;
+
+ int value;
+ if (obd.readPID(PID_RPM, value))
+ break;
+
+ obd.sleep();
+ Narcoleptic.delay(4000);
+ }
+ // re-initialize
+ state |= STATE_OBD_READY;
+ startTime = millis();
+ lastSpeedTime = startTime;
+ lastSpeed = 0;
+ distance = 0;
+#if ENABLE_DATA_LOG
+ logger.openFile();
+#endif
+ initScreen();
+}
+
+// screen layout related stuff
+void showStates()
+{
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setColor(RGB16_WHITE);
+ lcd.setCursor(0, 8);
+ lcd.print("MEMS ");
+ lcd.setColor((state & STATE_MEMS_READY) ? RGB16_GREEN : RGB16_RED);
+ lcd.draw((state & STATE_MEMS_READY) ? tick : cross, 16, 16);
+
+#if USE_GPS
+ lcd.setColor(RGB16_WHITE);
+ lcd.setCursor(60, 8);
+ lcd.print(" GPS ");
+ if (state & STATE_GPS_CONNECTED) {
+ lcd.setColor(RGB16_GREEN);
+ lcd.draw(tick, 16, 16);
+ } else {
+ lcd.setColor(RGB16_RED);
+ lcd.draw(cross, 16, 16);
+ }
+#endif
+ lcd.setColor(RGB16_WHITE);
+}
+
+void testOut()
+{
+ static const char PROGMEM cmds[][6] = {"ATZ\r", "ATL1\r", "ATRV\r", "0100\r", "010C\r", "0902\r"};
+ char buf[128];
+ lcd.setFontSize(FONT_SIZE_SMALL);
+ lcd.setCursor(0, 11);
+
+ for (byte i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
+ char cmd[6];
+ memcpy_P(cmd, cmds[i], sizeof(cmd));
+ lcd.setColor(RGB16_WHITE);
+ lcd.print("Sending ");
+ lcd.println(cmd);
+ Serial.println(cmd);
+ lcd.setColor(RGB16_CYAN);
+ if (obd.sendCommand(cmd, buf, sizeof(buf))) {
+ char *p = strstr(buf, cmd);
+ if (p)
+ p += strlen(cmd);
+ else
+ p = buf;
+ Serial.println(p);
+ while (*p == '\r') p++;
+ while (*p) {
+ lcd.write(*p);
+ if (*p == '\r' && *(p + 1) != '\r') {
+ lcd.write('\n');
+ }
+ p++;
+ }
+ } else {
+ lcd.println("Timeout");
+ Serial.println("Timeout");
+ }
+ delay(500);
+ }
+ lcd.println();
+}
+
+void setup()
+{
+ Serial.begin(115200);
+#if USE_GPS
+ GPSUART.begin(GPS_BAUDRATE);
+ lastGPSDataTime = 0;
+#endif
+ logger.initSender();
+
+ lcd.begin();
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setColor(0xFFE0);
+ lcd.println("MEGA LOGGER HD - OBD-II/GPS/MEMS");
+ lcd.println();
+ lcd.setColor(RGB16_WHITE);
+
+#if ENABLE_DATA_LOG
+ if (checkSD()) {
+ uint16_t index = logger.openFile();
+ lcd.println();
+ if (index > 0) {
+ lcd.print("File ID:");
+ lcd.println(index);
+ } else {
+ lcd.println("No File");
+ }
+ }
+#endif
+
+ obd.begin();
+ lcd.print("Adapter Ver. ");
+ lcd.print(obd.version / 10);
+ lcd.print('.');
+ lcd.println(obd.version % 10);
+
+#ifdef OBD_ADAPTER_I2C
+ Wire.begin();
+ accelgyro.initialize();
+ if (accelgyro.testConnection()) state |= STATE_MEMS_READY;
+#else
+ if (obd.version > 10)
+ state |= STATE_MEMS_READY;
+#endif
+
+ showStates();
+
+#if USE_GPS
+ unsigned long t = millis();
+ do {
+ if (GPSUART.available() && GPSUART.read() == '\r') {
+ state |= STATE_GPS_CONNECTED;
+ break;
+ }
+ } while (millis() - t <= 2000);
+ showStates();
+#endif
+
+ // this will send a bunch of commands and display response
+ testOut();
+
+ // initialize the OBD until success
+ while (!obd.init(OBD_PROTOCOL));
+ state |= STATE_OBD_READY;
+
+ char buf[64];
+ if (obd.getVIN(buf, sizeof(buf))) {
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setColor(RGB16_WHITE);
+ lcd.print("VIN:");
+ lcd.setColor(RGB16_YELLOW);
+ lcd.println(buf);
+ }
+
+ showECUCap();
+ lcd.setCursor(0, 28);
+ lcd.setColor(RGB16_YELLOW);
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+
+ fadeOutScreen();
+ initScreen();
+
+ startTime = millis();
+ lastSpeedTime = startTime;
+ lastRefreshTime = millis();
+}
+
+
+void loop()
+{
+ static byte index2 = 0;
+ const byte pids[]= {PID_RPM, PID_SPEED, PID_THROTTLE, PID_ENGINE_LOAD};
+ const byte pids2[] = {PID_COOLANT_TEMP, PID_INTAKE_TEMP, PID_ENGINE_FUEL_RATE};
+ int values[sizeof(pids)] = {0};
+ uint32_t pidTime = millis();
+ // read multiple OBD-II PIDs
+ byte results = obd.readPID(pids, sizeof(pids), values);
+ pidTime = millis() - pidTime;
+ if (results == sizeof(pids)) {
+ for (byte n = 0; n < sizeof(pids); n++) {
+ logOBDData(pids[n], values[n]);
+ }
+ }
+ byte pid = pids2[index2 = (index2 + 1) % sizeof(pids2)];
+ // check validation and read a single OBD-II PID
+ if (obd.isValidPID(pid)) {
+ int value;
+ if (obd.readPID(pid, value)) {
+ logOBDData(pid, value);
+ }
+ }
+
+ if (state & STATE_MEMS_READY) {
+ processMEMS();
+ }
+
+ if (logger.dataTime - lastRefreshTime >= 1000) {
+ float v = obd.getVoltage();
+ if (v > 0) {
+ lcd.setCursor(84, 24);
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.print(v, 1);
+ }
+
+ char buf[12];
+ // display elapsed time
+ unsigned int sec = (logger.dataTime - startTime) / 1000;
+ sprintf(buf, "%02u:%02u", sec / 60, sec % 60);
+ lcd.setFontSize(FONT_SIZE_MEDIUM);
+ lcd.setCursor(220, 5);
+ lcd.print(buf);
+ // display OBD time
+ if (results) {
+ lcd.setCursor(380, 26);
+ lcd.print((uint16_t)(pidTime / results));
+ lcd.print("ms ");
+ }
+ lastRefreshTime = logger.dataTime;
+ }
+
+ if (obd.errors >= 3) {
+ reconnect();
+ }
+
+#if USE_GPS
+ if (millis() - lastGPSDataTime > GPS_DATA_TIMEOUT || gps.satellites() < 3) {
+ // GPS not ready
+ state &= ~STATE_GPS_READY;
+ } else {
+ // GPS ready
+ state |= STATE_GPS_READY;
+ }
+#endif
+}