// Copyright (C) 2014 Paul T Middlehurst. // Permission is granted to copy, distribute and/or modify this document // under the terms of the GNU Free Documentation License, Version 1.3 // or any later version published by the Free Software Foundation; // with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. // A copy of the license is included in the section entitled "GNU Free Documentation License". // // // DynoCar Version 0.1 4th July 2012 by Paul Middlehurst (PTM) - beta version for basic testing // DynoCar Version 0.2 1st August 2012 PTM - All configuration routines completed // DynoCar Version 0.3 5th August 2012 PTM - completely reworked calibration method! // DynoCar Version 0.4 24th November 2012 PTM - Added datetime stamp filename and wheel size setup // DynoCar Version 0.5 14th January 2013 PTM - Removed speed averaging + re order interupt // DynoCar Version 0.6 20th April 2013 PTM - Add divide config and divide to speed // DynoCar Version 1.0 6th June 2013 PTM - Add compensation for speed when writing to flash & tidy up! // DynoCar Version 1.1 21st June 2013 PTM - Changed the way clibrate works including changing the calibarate weight from 1K to 10K #include #include #include #include #include #include #include #include /* Some usefull info: WDMES Wheel Diameter = 105mm - Radius = 329.867 1 Mile = 1,609,344mm 4878.766 revs per Mile 1 MPH = 81.312 RPM or 1.355 RPS SD card: ** MOSI - pin 11 ** MISO - pin 12 ** CLK - pin 13 ** CS - pin 5 DS1302 Clock: ** SCLK_PIN - pin 3 ** IO_PIN - pin 4 ** CE_PIN - pin 6 */ const int chipSelect = 5; // SD card select const int WheelIntPin = 2; // Wheel sensor const float MileFract = 2.236936; // 60*60/1609344 * 1000 volatile long StartTime = 0; volatile long EndTime = 0; volatile float MPH = 0; volatile float Pull = 0; volatile int SensorValue = 0; volatile long Duration = 0; volatile long WatchDog = 0; volatile byte Configure = 1; volatile byte Option = 0; volatile long Value = 0; volatile float Zero = 0; volatile byte FileCount = 0; volatile byte SDWrite = 1; volatile long fileSize = 0; volatile byte RevCount = 0; volatile byte check = 0; volatile char SDString[200]; File dataFile; char fn[] = "MMDDHHMM.txt"; int i=0; char DateTimeStamp[50]; String file_name = ""; volatile byte Cal = EEPROM.read(1); // read calibration value from eeprom volatile byte WheelDiameter = EEPROM.read(2); // read wheel diameter from eeprom volatile byte Divide = EEPROM.read(3); // read divide value from eeprom const float WheelCirc = (WheelDiameter * 3.142); // String DataString = ""; // String TempTime = ""; /* DS1302 Clock Pins */ int SCLK_PIN = 3; int IO_PIN = 4; int CE_PIN = 6; // Create a DS1302 object DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN); // Initiate the LCD Display LiquidCrystal lcd(10, 9, 0, 1, 7, 8); int SensorPin = A0; int SDWritePin = A1; int ConfigPin = A2; int UpPin = A3; int DownPin = A4; int EnterPin = A5; void setup() { lcd.begin(16, 2); lcd.print(" WDMES DynoCar"); lcd.setCursor(0, 1); lcd.print("Duncan W,Paul M"); delay(2000); lcd.clear(); lcd.print("Calibrating....."); lcd.setCursor(0, 1); lcd.print("Ensure NO Load!"); delay(5000); lcd.clear(); pinMode(WheelIntPin, INPUT); pinMode(SDWritePin, INPUT); pinMode(ConfigPin, INPUT); pinMode(UpPin, INPUT); pinMode(DownPin, INPUT); pinMode(EnterPin, INPUT); digitalWrite(SensorPin, LOW); digitalWrite(WheelIntPin, HIGH); // turn on pullup resistors digitalWrite(SDWritePin, HIGH); digitalWrite(ConfigPin, HIGH); digitalWrite(UpPin, HIGH); digitalWrite(DownPin, HIGH); digitalWrite(EnterPin, HIGH); attachInterrupt(0, Speed, RISING); analogReference(EXTERNAL); Zero = analogRead(SensorPin); pinMode(10, OUTPUT); // output pint 10, bugfix!!: if (!SD.begin(chipSelect)) { // see if the card is present and can be initialized: lcd.print("* NO SD CARD *"); lcd.setCursor(0, 1); lcd.print("WRITE DISABLED"); delay(3000); lcd.clear(); SDWrite = 0; } else { CountFiles(); delay(100); setup_sd(); } } void CountFiles() { byte x = 1; File entry; File root = SD.open("/"); fileSize = root.size(); while(x == 1) { entry = root.openNextFile(); if (! entry) { x = 0; entry.close(); delay(10); } else { FileCount++; fileSize = fileSize + entry.size(); entry.close(); delay(10); } } root.close(); } void setup_sd() { Time t = rtc.time(); if (t.date < 10) { file_name = String(file_name + '0' + String(t.date, DEC)); } else { file_name = String(file_name + String(t.date, DEC)); } if (t.mon < 10) { file_name = String(file_name + '0' + String(t.mon, DEC)); } else { file_name = String(file_name + String(t.mon, DEC)); } if (t.hr < 10) { file_name = String(file_name + '0' + String(t.hr, DEC)); } else { file_name = String(file_name + String(t.hr, DEC)); } if (t.min < 10) { file_name = String(file_name + '0' + String(t.min, DEC)); } else { file_name = String(file_name + String(t.min, DEC)); } file_name = String(file_name + ".txt"); for (i=0;i<=file_name.length();i++) { fn[i] = file_name.charAt(i); } if (digitalRead(SDWritePin)) { dataFile = SD.open(fn, FILE_WRITE); if (dataFile) { print_time(); dataFile.println("* * * * * * * DynoCar Started * * * * * * *"); dataFile.print(DateTimeStamp); dataFile.print(" "); dataFile.print(WheelDiameter); dataFile.print(" "); dataFile.println(Divide); dataFile.close(); } } } void loop() { backin: if (digitalRead(ConfigPin)) { WatchDogCheck(); MainDisplay(); } else { Config(); } } void Config() { while (1) { CheckButtons(); switch (Configure) { case 1: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Files = "); lcd.print(FileCount); lcd.setCursor(0, 1); lcd.print("FS = "); lcd.print(fileSize); break; case 2: lcd.clear(); lcd.setCursor(0, 0); lcd.print("10K Cal Val = "); lcd.print(Cal); lcd.setCursor(0, 1); lcd.print("Zero = "); lcd.print(Zero); break; case 3: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Check Load Cell"); break; case 4: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Calibrate "); break; case 5: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Set Clock"); print_time(); lcd.setCursor(0, 1); lcd.print(DateTimeStamp); break; case 6: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Set Wheel Diameter"); lcd.setCursor(0, 1); lcd.print(WheelDiameter); break; case 7: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Set Divide"); lcd.setCursor(0, 1); lcd.print(Divide); break; case 8: Configure = 1; } return; } } void CheckButtons () { if (digitalRead(UpPin) == LOW && Configure < 8) Configure++; if (digitalRead(UpPin) == LOW && Configure == 8) Configure = 1; if (digitalRead(DownPin) == LOW && Configure >= 1) Configure--; if (digitalRead(DownPin) == LOW && Configure < 1) Configure = 7; if (digitalRead(EnterPin) == LOW) { if (Configure == 3) CheckLoadCell(); if (Configure == 4) Calibrate(); if (Configure == 5) SetClock(); if (Configure == 6) SetWheelDiameter(); if (Configure == 7) SetDivide(); } delay(250); } void CheckLoadCell() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Load Cell Values"); delay(250); while (digitalRead(EnterPin)) { lcd.setCursor(0, 1); lcd.print(" "); lcd.setCursor(0, 1); SensorValue = analogRead(SensorPin); Pull = ((SensorValue - Zero) / Cal) * 10; lcd.print(SensorValue); lcd.print(" "); SensorValue = (analogRead(SensorPin) - Zero); lcd.print(SensorValue); lcd.print(" "); lcd.print(Pull); delay(250); } } void Calibrate () { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Load with 10K"); lcd.setCursor(0, 1); lcd.print("and press enter"); delay (3000); while (digitalRead(EnterPin)) { delay (250); } Cal = (analogRead(SensorPin) - Zero); EEPROM.write(1, Cal); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Set Cal = "); lcd.setCursor(0, 1); lcd.print(Cal); delay(3000); Cal = EEPROM.read(1); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Cal Val = "); lcd.print(Cal); delay(3000); return; } void SetClock() { Time td = rtc.time(); long syear = td.yr; byte smonth = td.mon; byte sday = td.date; byte shour = td.hr; byte smin = td.min; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Year"); SetValue(syear); syear = Value; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Month"); SetValue(smonth); smonth = Value; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Day"); SetValue(sday); sday = Value; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Hour"); SetValue(shour); shour = Value; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Minute"); SetValue(smin); smin = Value; // rtc.write_protect(false); // needed to start a new cloc // rtc.halt(false); // --------- " ---------- Time t(syear, smonth, sday, shour, smin, 0, 0); rtc.time(t); lcd.clear(); print_time(); lcd.setCursor(0, 1); lcd.print("Clock Set"); delay(2000); return; } void SetWheelDiameter () { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Wheel Diameter"); SetValue(WheelDiameter); EEPROM.write(2, Value); WheelDiameter = EEPROM.read(2); lcd.clear(); lcd.setCursor(0, 0); lcd.print("W/Diameter = "); lcd.print(WheelDiameter); delay(4000); return; } void SetDivide () { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Divide By"); SetValue(Divide); EEPROM.write(3, Value); Divide = EEPROM.read(3); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Divide = "); lcd.print(Divide); delay(4000); return; } void SetValue(long V) { Value = V; delay(250); while (digitalRead(EnterPin)) { if (digitalRead(UpPin) == LOW && Value < 3000) Value++; if (digitalRead(DownPin) == LOW && Value > 1) Value--; lcd.print(" "); lcd.setCursor(0, 1); lcd.print(Value); delay(250); } } void WatchDogCheck () { WatchDog = millis() - StartTime; if (WatchDog > 3000) { MPH = 0; Pull = 0; } } void MainDisplay() { lcd.setCursor(0, 0); lcd.print("MPH = "); lcd.setCursor(8, 0); lcd.print(MPH); if (!digitalRead(SDWritePin)) { lcd.setCursor(15, 0); lcd.print("*"); } lcd.setCursor(0, 1); lcd.print("Load = "); lcd.setCursor(8, 1); lcd.print(Pull); delay(250); } void Speed() { Duration = (millis() - StartTime); if (digitalRead(SDWritePin) && (SDWrite)) Duration = Duration + 30; // flash write compensation StartTime = millis(); SensorValue = analogRead(SensorPin); Pull = ((SensorValue - Zero) / Cal) * 10; MPH = (((WheelCirc * Divide) / Duration) * MileFract); if (digitalRead(SDWritePin)) { if (SDWrite) { WriteSD(); } } } void WriteSD() { dataFile = SD.open(fn, FILE_WRITE); // if the file is available, write to it: if (dataFile) { dataFile.print(MPH); dataFile.print(" "); dataFile.print(Duration); dataFile.print(" "); dataFile.print(SensorValue); dataFile.print(" "); dataFile.println(Pull); } dataFile.close(); } void Initialize_New_Clock() { rtc.write_protect(false); rtc.halt(false); } void print_time() { /* Get the current time and date from the chip */ Time t = rtc.time(); snprintf(DateTimeStamp, sizeof(DateTimeStamp), "%04d-%02d-%02d %02d:%02d:%02d", t.yr, t.mon, t.date, t.hr, t.min, t.sec); } // The end!