diff options
Diffstat (limited to 'nanotimer/nanotimer.ino')
-rw-r--r-- | nanotimer/nanotimer.ino | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/nanotimer/nanotimer.ino b/nanotimer/nanotimer.ino new file mode 100644 index 0000000..a5ed6a4 --- /dev/null +++ b/nanotimer/nanotimer.ino @@ -0,0 +1,395 @@ +/************************************************************************* +* OBD-II based performance timer and logger +* Distributed under GPL v2.0 +* Copyright (c) 2014 Stanley Huang <stanleyhuangyc@gmail.com> +* All rights reserved. +*************************************************************************/ + +#include <Arduino.h> +#include <Wire.h> +#include <OBD.h> +#include <SD.h> +#include "MicroLCD.h" +#include "config.h" +#if USE_SOFTSERIAL +#include <SoftwareSerial.h> +#endif +#include "datalogger.h" + +// logger states +#define STATE_SD_READY 0x1 +#define STATE_OBD_READY 0x2 +#define STATE_SLEEPING 0x20 + +static uint32_t lastFileSize = 0; +static int lastSpeed = -1; +static uint32_t lastSpeedTime = 0; +static int speed = 0; +static uint32_t distance = 0; +static uint16_t fileIndex = 0; +static uint32_t startTime = 0; + +#define STAGE_IDLE 0 +#define STAGE_WAIT_START 1 +#define STAGE_MEASURING 2 + +static byte stage = STAGE_IDLE; + +#define SPEED_THRESHOLD_1 60 /* kph */ +#define SPEED_THRESHOLD_2 100 /* kph */ +#define SPEED_THRESHOLD_3 200 /* kph */ +#define DISTANCE_THRESHOLD 400 /* meters */ + +static uint16_t times[4] = {0}; + +class COBDLogger : public COBD, public CDataLogger +{ +public: + COBDLogger():state(0) {} + void setup() + { +#if ENABLE_DATA_LOG + if (!(state & STATE_SD_READY)) { + if (checkSD()) { + state |= STATE_SD_READY; + showStates(); + } + } +#endif + + do { + showStates(); + } while (!init()); + + state |= STATE_OBD_READY; + + showStates(); + +#if ENABLE_DATA_LOG + uint16_t index = openFile(LOG_TYPE_DEFAULT); + lcd.print("File ID:"); + if (index) { + lcd.setFlags(FLAG_PAD_ZERO); + lcd.printInt(index, 5); + lcd.setFlags(0); + } else { + lcd.print("N/A"); + } +#endif + + initTimerScreen(); + } + void loop() + { + static byte index = 0; + static byte index2 = 0; + static byte index3 = 0; + + timerLoop(); + +#if ENABLE_DATA_LOG + // flush SD data every 1KB + if (dataSize - lastFileSize >= 1024 && stage != STAGE_MEASURING) { + flushFile(); + lastFileSize = dataSize; + // display logged data size + } +#endif + + if (errors >= 2) { + reconnect(); + } + } +#if ENABLE_DATA_LOG + bool checkSD() + { + Sd2Card card; + SdVolume volume; + lcd.setCursor(0, 0); + lcd.setFontSize(FONT_SIZE_MEDIUM); + state &= ~STATE_SD_READY; + pinMode(SS, OUTPUT); + 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 = "SDx"; + } + + lcd.clear(); + 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; + + sprintf(buf, "%dGB", (int)((volumesize + 511) / 1000)); + lcd.print(buf); + } else { + lcd.clear(); + lcd.print("SD"); + lcd.draw(cross, 32, 0, 16, 16); + return false; + } + + if (!SD.begin(SD_CS_PIN)) { + lcd.setCursor(48, 0); + lcd.print("Bad SD"); + return false; + } + + state |= STATE_SD_READY; + return true; + } +#endif +private: + void dataIdleLoop() + { + if (state & STATE_SLEEPING) return; + + if (getState() == OBD_CONNECTED) + return; + + // called while initializing + char buf[10]; + unsigned int t = (millis() - startTime) / 1000; + sprintf(buf, "%02u:%02u", t / 60, t % 60); + lcd.setFontSize(FONT_SIZE_SMALL); + lcd.setCursor(97, 7); + lcd.print(buf); + } + void timerLoop() + { + uint32_t elapsed = millis() - startTime; + uint16_t n; + + int speed; + if (!read(PID_SPEED, speed)) + return; + + dataTime = millis(); + logData(0x100 | PID_SPEED, speed); + + int rpm = 0; + if (read(PID_RPM, rpm)) { + dataTime = millis(); + logData(0x100 | PID_RPM, rpm); + } + + lcd.setFontSize(FONT_SIZE_XLARGE); + // estimate distance + distance += (uint32_t)(speed + lastSpeed) * (dataTime - lastSpeedTime) / 2 / 3600; + + if (lastSpeed != speed) { + lcd.setCursor(0, 4); + lcd.printInt((unsigned int)speed % 1000, 3); + lastSpeed = speed; + } + + lastSpeedTime = dataTime; + + if (stage == STAGE_WAIT_START) { + if (speed > 0) { + stage = STAGE_MEASURING; + startTime = lastSpeedTime; + + uint32_t t = dataTime; + dataTime = lastSpeedTime; + logData(0x100 | PID_SPEED, lastSpeed); + dataTime = t; + logData(0x100 | PID_SPEED, speed); + + lastSpeed = 0; + distance = 0; + + memset(times, 0, sizeof(times)); + + initTimerScreen(); + } + } else if (stage == STAGE_MEASURING) { + // display elapsed time (mm:ss:mm) + n = elapsed / 1000; + if (n < 100) { + lcd.setCursor(0, 0); + lcd.printInt(n, 2); + n = (elapsed % 1000) / 100; + lcd.setFontSize(FONT_SIZE_MEDIUM); + lcd.setCursor(32, 1); + lcd.write('.'); + lcd.write('0' + n); + } + if (times[2] == 0 && speed >= SPEED_THRESHOLD_3) { + times[2] = elapsed / 100; + stage = STAGE_IDLE; + lcd.clearLine(0); + lcd.clearLine(1); + lcd.clearLine(2); + showTimerResults(); + lcd.setFontSize(FONT_SIZE_MEDIUM); + lcd.setCursor(0, 0); + lcd.print("DONE!"); + } else if (times[1] == 0 && speed >= SPEED_THRESHOLD_2) { + times[1] = elapsed / 100; + showTimerResults(); + } else if (times[0] == 0 && speed >= SPEED_THRESHOLD_1) { + times[0] = elapsed / 100; + showTimerResults(); + } else if (speed == 0) { + // speed go back to 0 + stage = STAGE_IDLE; + } + if (distance > 0) { + lcd.setFontSize(FONT_SIZE_SMALL); + lcd.setCursor(62, 6); + if (distance >= 400) { + lcd.printInt(400, 3); + if (!times[3]) { + times[3] = elapsed / 100; + showTimerResults(); + } + } else { + lcd.printInt(distance, 3); + } + } + // log speed data + logData(0x100 | PID_SPEED, speed); + // log additional data + int rpm; + if (read(PID_RPM, rpm)) { + dataTime = millis(); + logData(0x100 | PID_RPM, rpm); + } + } else { + if (speed == 0) { + stage = STAGE_WAIT_START; + initTimerScreen(); + lcd.setFontSize(FONT_SIZE_MEDIUM); + lcd.setCursor(0, 0); + lcd.println(" GET"); + lcd.println("READY"); + delay(500); + } + } + } + void reconnect() + { +#if ENABLE_DATA_LOG + closeFile(); +#endif + lcd.clear(); + lcd.setFontSize(FONT_SIZE_MEDIUM); + lcd.print("Reconnecting"); + startTime = millis(); + state &= ~STATE_OBD_READY; + state |= STATE_SLEEPING; + //digitalWrite(SD_CS_PIN, LOW); + for (int i = 0; !init(); i++) { + if (i == 10) lcd.clear(); + } + state &= ~STATE_SLEEPING; + fileIndex++; + setup(); + } + byte state; + + // screen layout related stuff + void showStates() + { + lcd.setFontSize(FONT_SIZE_SMALL); + lcd.setCursor(0, 3); + if (state & STATE_OBD_READY) { + lcd.println("OBD connected! "); + } else { + lcd.println("Connecting OBD..."); + } + } + void showTimerResults() + { + lcd.setFontSize(FONT_SIZE_SMALL); + lcd.setCursor(56, 0); + lcd.print(" 0~60: --"); + lcd.setCursor(56, 2); + lcd.print("0~100: --"); + lcd.setCursor(56, 4); + lcd.print("0~200: --"); + lcd.setCursor(56, 6); + lcd.print(" 400m: --"); + lcd.setFontSize(FONT_SIZE_MEDIUM); + char buf[8]; + if (times[0]) { + sprintf(buf, "%2d.%1d", times[0] / 10, times[0] % 10); + Serial.println(times[0]); + lcd.setCursor(92, 0); + lcd.print(buf); + } + if (times[1]) { + sprintf(buf, "%2d.%1d", times[1] / 10, times[1] % 10); + Serial.println(buf); + lcd.setCursor(92, 2); + lcd.print(buf); + } + if (times[2]) { + sprintf(buf, "%2d.%1d", times[2] / 10, times[2] % 10); + Serial.println(buf); + lcd.setCursor(92, 4); + lcd.print(buf); + } + if (times[3]) { + sprintf(buf, "%2d.%1d", times[3] / 10, times[3] % 10); + Serial.println(buf); + lcd.setCursor(92, 6); + lcd.print(buf); + } + } + void initTimerScreen() + { + lcd.clear(); + showTimerResults(); + lcd.setFontSize(FONT_SIZE_SMALL); + lcd.setCursor(24, 7); + lcd.print("km/h"); + } +}; + +static COBDLogger logger; + +void setup() +{ +#ifdef DEBUG + DEBUG.begin(DEBUG_BAUDRATE); +#endif + + lcd.begin(); + lcd.setFontSize(FONT_SIZE_MEDIUM); + lcd.println("PerformanceBox"); + + logger.begin(); + logger.initSender(); + +#if ENABLE_DATA_LOG + logger.checkSD(); +#endif + logger.setup(); +} + +void loop() +{ + logger.loop(); +} |