California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« on: January 02, 2011, 04:15:26 am » |
I figured I might as well join the forum and share some info about the speedometer project I've been working on over the last few months.
As of now it's running on a Seeeduino V328 with 2 stacked protoshields and a 20-digit 2-line LCD. Vehicle movement is sensed via an ABS sensor on the left front wheel, which provides 54 pulses per wheel revolution. This allows the displayed speed to be valid down to 1 MPH. There is also a 5+1 digit odometer, a 3+1 digit trip odometer, and a power-fail circuit that saves the odometer data into EEPROM when a power failure is detected. The power fail circuit uses a MAX690 IC to trigger an interrupt when the incoming power supply voltage falls below 10 volts. An electrolytic capacitor keeps the circuit alive just long enough for the data to get written to the EEPROM...almost. I recently discovered that the last byte to get written isn't quite making it. (Time for a bigger capacitor)
Vehicle speed is displayed on the LCD in a 2-line block font, which was inspired by the MPGuino, and uses code that I adapted from an example posted by whosawhatsis.
I've been working on a menu to allow different options. The first is calibration, which can be used to dial in the accuracy while in use. The calibration value is also saved in EEPROM when exiting the calibration function. The second menu item brings up a distance meter that reads in feet. When I discovered that the ABS sensor sends a pulse for every 1.86 inches of travel, I thought "Why not?"
Eventually I plan to make a PCB for a DIP Atmega328 and all the additional circuitry, once I decide that I've tweaked the design enough. I also have a backlit 20-digit 4-line LCD that I intend to use, with a 3-line block font for the speed.
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #1 on: January 02, 2011, 04:16:24 am » |
Here's an early picture with the odometer in its infancy:  Here's a more recent one with updated odometer. No, I wasn't really driving that fast!  I was feeding a fast square wave into the circuit to run a test.  One more showing the "footometer" mode (it rolls over at 100000 feet): 
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
Tacoma, WA
Offline
Full Member
Karma: 3
Posts: 179
Arduino rocks
|
 |
« Reply #2 on: January 02, 2011, 04:35:24 am » |
I like it.  Any chance you'll share your sketch?
|
|
|
|
« Last Edit: January 02, 2011, 04:35:54 am by UnaClocker »
|
Logged
|
Brian from Tacoma, WA Arduino evangelist - since Dec, 2010.
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #3 on: January 03, 2011, 01:20:51 am » |
Thanks! I couldn't figure out another way to post the sketch other than cut it up into chunks, so here goes... /* Speedometer / Odometer Arduino Sketch by ElectricWater
Acknowledgements: dfowler / uchobby.com for Timer2 code MPGuino for Large Font inspiration whosawhatsis / Arduino forum for Large Font code
This sketch detects a square wave input using external interrupt 0. The signal's frequency is proportional to the vehicle's speed. The sketch also detects internal Timer2's overflow. The two are used to calculate the vehicle's speed, and the square wave count is also used to report the distance travelled.
Interrupt 0 (Digital pin 2) is connected to the vehicle's speed sensor. An interrupt service routine triggered by a rising edge on Interrupt 0 increments a counter (SensorCount).
Timer2 in the AVR is reconfigured to run at 125 KHz. An interrupt service routine triggered by Timer2's overflow increments a counter (TimerCount) by 8 and tests to see if the counter has reached a predetermined constant value (CalValue - used for calibration). If so, 3 things happen: - The value of SensorCount is copied intothe variable "Freq" for use in the main loop - The "Trigger" flag gets set - SensorCount and TimerCount get reset TimerCount reaches CalValue at a very consistent rate, which is used to calculate the vehicle speed. When CalValue is properly calibrated the value of Freq is equal to the actual speed in Miles per Hour.
The Timer2 ISR aslo causes digital pin 4 to toggle between high and low for testing purposes.
Note: The reason for incrementing TimerCount by 8 is that the sketch when first written originally configured Timer2 to run at 2 MHz, which is 8 times faster than its current 250 KHz. Slowing down Timer2 to 1/8 its original speed and having the ISR add 8 instead of 1 allows the AVR to spend less time servicing interrupts. This change resulted in no noticeable degradation in speedometer function.
The main loop tests to see if the Trigger flag is set. If so, several things happen: - The variable "Speed" is set to the average of the 3 most recent cycles of "Freq" - Speed then gets printed on the LCD in large 2-line block font. - Freq is used to update the odometer - Odometer information is printed to the LCD...as long as we're not in a menu The main odometer uses the variables TotalClicks, TotalTenths, and TotalMiles. The trip odometer uses the variables DistanceClicks, DistanceTenths, and DistanceMiles.
TotalClicks is increased by the value of Freq each time Trigger is set, which means that it is a running total of the speed sensor's pulses. CalDist is a calibration value that when adjusted correctly is equal to the number of sensor pulses per 1/10 mile. Therefore, when TotalClicks reaches CalDist the vehicle has travelled 1/10 mile, so TotalTenths is incremented and CalDist is subtracted from TotalClicks. When TotalTenths reaches 10, TotalMiles is incremented and TotalTenths is reset to zero. When TotalMiles reaches 100000 it gets rolled over back to zero.
The trip odometer then goes through the same process, except that DistanceMiles rolls over at 1000.
A trip odometer reset button is provided via a pushbutton switch connected to digital pin 6 (Button1). Digital pin 6 has its internal pull-up resistor turned on and the pushbutton drives the pin low when pressed. When a low is detected it resets DistanceClicks, DistanceTenths, and DistanceMiles to zero.
Interrupt 1 (Digital pin 3) is connected to a power-fail signal that goes low when the unregulated DC voltage drops below ~10 volts. When ISR1 is called it copies all of the odometer data into EEPROM for use the next time the circuit is powered up. Upon power-up the EEPROM is read back into the odometer variables.
An options menu is beginning to be implemented. Menu navigation is controlled by the 4 pushbutton switches connected to digital pin 6 and analog pins 0-2 (digital pins 14-16). The A0 button (Button2) is used to enter the menu mode, and also to exit menu mode. A1 (Button3) and A2 (Button4) is used to flip through the menus. D6 (Button1) is used to select a menu option. Once inside a menu option, Button3 and Button4 is used to change the data. Button1 is used as an "OK" or "Enter" button, while Button2 is used as an "Exit" or "Escape" button.
The first menu option is to adjust the calibration value (CalDist) for the speed sensor count. (CalValue is calculated from CalDist). When the CalDist is changed and "OK-ed" it will be written into EEPROM, which is read again on power-up.
The second menu option is to display a temporary distance meter that reads in feet.
MenuMode is an integer variable that will change depending on which menu the user is in. MenuMode = 0, no menu MenuMode = 1, calibrate speedometer MenuMode = 2, foot distance display mode
-----The circuit connections-----
Speed sensor input to Digital pin 2 (interrupt 0) Power fail input to Digital pin 3 (interrupt 1) Timer2 test output is on Digital pin 4
LCD hookups: * LCD Vss pin (pin 1) to ground * LCD Vcc pin (pin 2) to +5V * LCD VO pin (pin 3) to ground * LCD RS pin (pin 4) to Digital pin 7 * LCD R/W pin (pin 5) to Digital pin 5 * LCD Enable pin (pin 6) to Digital pin 8 * LCD D4 pin (pin 11) to Digital pin 9 * LCD D5 pin (pin 12) to Digital pin 10 * LCD D6 pin (pin 13) to Digital pin 11 * LCD D7 pin (pin 14) to Digital pin 12 Pushbutton switches: Button1 to Digital pin 6 Button2 to Analog pin 0 (Digital pin 14) Button3 to Analog pin 1 (Digital pin 15) Button4 to Analog pin 2 (Digital pin 16) Physical protoshield layout of the pushbuttons: ---- ---- |Btn4||Btn3| ---- ---- ---- |Btn2| ---- ---- |Btn1| ----
*/
// include the EEPROM library code: #include <EEPROM.h>
// include the library code: #include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins LiquidCrystal lcd(7, 5, 8, 9, 10, 11, 12);
// build 2-line digit font data array // Digits are 3 characters wide. byte bignums[10][2][3] = { // Define which characters to use for each number. 255 is a solid block; 254 is a space // The format is { {TopLeft, TopMiddle, TopRight}, {BottomLeft, BottomMiddle, BottomRight} } { {255, 0, 255}, {255, 1, 255} }, // data to display "0" { {0, 255, 254}, {1, 255, 1} }, // data to display "1" { {2, 2, 255}, {255, 1, 1} }, // data to display "2" { {0, 2, 255}, {1, 1, 255} }, // data to display "3" { {255, 1, 255}, {254, 254, 255} }, // data to display "4" { {255, 2, 2}, {1, 1, 255} }, // data to display "5" { {255, 2, 2}, {255, 1, 255} }, // data to display "6" { {0, 0, 255}, {254, 255, 254} }, // data to display "7" { {255, 2, 255}, {255, 1, 255} }, // data to display "8" { {255, 2, 255}, {254, 254, 255} } // data to display "9" };
int CalDist; // Calibration value for the odometer // Increase to increment slower, decrease to increment faster // Equal to the number of speed sensor pulses per 1/10 mile
int CalValue; // Calibration value for the speedometer // Gets calculated from CalDist // Increase to read faster, decrease to read slower /* ---Time and distance math---
Timer2 runs at 250 KHz and counts from 0 to 255 before overflow. Each overflow triggers an interrupt adding +8 to TimerCount. This happens 976.5625 times per second. Therefore, TimerCount increases by an average of 7812.5 per second. We'll call these increases in value "counts". We'll call one pulse from the speed sensor 1 click. CalValue "counts" = 1 mile/hour Formulas for relating CalValue to CalDist are as follows: 7812.5 counts 1 click 1 hour 3600 sec 1 mile 2812500 CalDist (clicks per 1/10 mile) = ------------- x --------------- x ------ x -------- x ------------ = -------- 1 sec CalValue counts 1 mile 1 hour 10 1/10 mile CalValue 7812.5 counts 3600 sec 1 hour 1 mile 1 1/10 mile 2812500 CalValue (counts) = ------------- x -------- x ------ x ------------ x ----------- = --------- 1 sec 1 hour 1 mile 10 1/10 mile CalDist CalDist */
|
|
|
|
« Last Edit: January 03, 2011, 01:41:48 am by ElectricWater »
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #4 on: January 03, 2011, 01:22:21 am » |
const byte SqWavePin = 4; // Square wave genereated from digital pin 4 const byte Button1 = 6; // Pushbutton switch D6, connected to ground const byte Button2 = 14; // Pushbutton switch A0, connected to ground const byte Button3 = 15; // Pushbutton switch A1, connected to ground const byte Button4 = 16; // Pushbutton switch A2, connected to ground
byte MenuMode = 0; // MenuMode 0 means not in a menu
boolean MenuModeActive = false; // Only goes true when a menu item has been selected
unsigned long ButtonsOffTime; // How long since last button was released
boolean Button1State; // State of Button1 from last cycle boolean Button2State; // State of Button2 from last cycle boolean Button3State; // State of Button3 from last cycle boolean Button4State; // State of Button4 from last cycle boolean ButtonPressed = false; // Becomes true when a button press is detected boolean ButtonPressedPrev = false; // Previous value of ButtonPressed
int CalDistPrev; // Previous value of CalDist during calibration
int SensorCount = 0; // Speed Sensor pulse count int TimerCount = 0; // Timer2 overflow count int Freq; // Copy of SensorCount, and is the actual vehicle speed boolean Trigger = false; // When it becomes true it triggers the print cycle boolean SqWaveOut = false; // Used to toggle output state of pin 4 int FreqPrev1 = 0; // Previous value of Freq, used for averaging speed int FreqPrev2 = 0; // Another previous value of Freq, used for averaging speed int Speed; // Average of all 3 Freq variables, and is what is displayed on LCD
int DistanceMiles; // Cumulative distance travelled in miles int DistanceTenths; // Cumulative distance travelled, tenths digit int DistanceClicks; // Cumulative speed sensor pulses received, reduced by CalDist each 1/10 mile unsigned long TotalMiles; // Total distance travelled in miles int TotalTenths; // Total distance travelled, tenths digit int TotalClicks; // Total speed sensor pulses received, reduced by CalDist each 1/10 mile
long DistanceFeet; // Cumulative distance travelled in feet, temporary display option unsigned int DistanceFeetTenths; // Cumulative distance travelled in feet, tenths digit, temporary display option int FeetScaleFactor; // Scale Factor used to calculate feet from speed sensor pulses
ISR(TIMER2_OVF_vect) { // This is the Interrupt Service Routine, called when Timer2 overflows
TimerCount +=8; // Increment TimerCount by 8 if (TimerCount > CalValue) { // Check to see if it's time to display the count Freq = SensorCount; // Copies the interrupt 0 count into variable "Freq" Trigger = true; // sets "Trigger" to let the main loop know it's time to print data SensorCount = 0; // Reset the interrupt 0 count TimerCount -= CalValue; // Reset Timer2 count, but keep the excess for next time around } SqWaveOut = !SqWaveOut; // Toggle SqWaveOut to its opposite state digitalWrite(SqWavePin, SqWaveOut); // Set pin 4 to the new state of SqWaveOut
}
void setup() { // turn on DigitalPin 13 LED to signal that the custom characters have been loaded into the LCD pinMode(13, OUTPUT); loadchars(); digitalWrite(13, HIGH);
pinMode(SqWavePin, OUTPUT); // Output on pin 4 will be oscillating pinMode(Button1, INPUT); // Set Button1 pin as input pinMode(Button2, INPUT); // Set Button2 pin as input pinMode(Button3, INPUT); // Set Button3 pin as input pinMode(Button4, INPUT); // Set Button4 pin as input digitalWrite(Button1, HIGH); // Turn on Button1's internal pull-up resistor digitalWrite(Button2, HIGH); // Turn on Button2's internal pull-up resistor digitalWrite(Button3, HIGH); // Turn on Button3's internal pull-up resistor digitalWrite(Button4, HIGH); // Turn on Button4's internal pull-up resistor CalDist = (int(EEPROM.read(101)) * 256) + (int(EEPROM.read(102))); // read CalDist out of EEPROM CalValue = int((2812500.0 / CalDist) + .5); // calculate CalValue from CalDist
// set up the LCD's number of columns and rows: lcd.begin(20,2);
// read total mileage out of EEPROM TotalMiles = (EEPROM.read(5) * 65536) + (EEPROM.read(6) * 256) + (EEPROM.read(7)); TotalTenths = EEPROM.read(8); TotalClicks = (EEPROM.read(9) * 256) + (EEPROM.read(10)); // read Distance out of EEPROM DistanceMiles = (EEPROM.read(0) * 256) + (EEPROM.read(1)); DistanceTenths = EEPROM.read(2); DistanceClicks = (EEPROM.read(3) * 256) + (EEPROM.read(4)); // print the speed in big font lcd.setCursor(0, 0); // set the cursor to (13,0) Freq = Freq % 1000; // drop any digits above 999 printbigchar(int(Freq / 100),0); // print the speed hundreds Freq = Freq % 100; // drop any digits above 99 printbigchar(int(Freq / 10),1); // print the speed tens Freq = Freq % 10; // drop any digits above 9 printbigchar(Freq,2); // print the speed ones // write Distance to LCD printmileage(); attachInterrupt(0, AddSensorCount, RISING); // Interrupt 0 is on digital pin 2 attachInterrupt(1, EEPROMwrite, FALLING); // Interrupt 1 is on digital pin 3
// ------------------------------------------------------------------------------------- // This bit of code is adapted from an article by dfowler at uchobby.com // http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/ //Timer2 Settings: Timer Prescaler / 64, mode 0 //Timmer clock = 16 MHz / 64 = 250 KHz or 0.5us TCCR2A = 0; TCCR2B = 1<<CS22 | 0<<CS21 | 0<<CS20; // Set Timer2 frequency to 250 KHz // Used to be 010 for 2 MHz clock
//Timer2 Overflow Interrupt Enable TIMSK2 = 1<<TOIE2; // ------------------------------------------------------------------------------------- ButtonsOffTime = millis(); // Set ButtonStateTime to current time }
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #5 on: January 03, 2011, 01:25:57 am » |
void loop() {
if (Trigger) { // Print data if the "Trigger" is set // calculate average speed for printing Speed = int((Freq + FreqPrev1 + FreqPrev2) / 3);
// print the speed in big font Speed = Speed % 1000; // drop any digits above 999 printbigchar(int(Speed / 100), 0); // print the speed hundreds Speed = Speed % 100; // drop any digits above 99 printbigchar(int(Speed / 10), 1); // print the speed tens Speed = Speed % 10; // drop any digits above 9 printbigchar(Speed, 2); // print the speed ones // Main odometer update TotalClicks += Freq; // Add Freq to TotalClicks while (TotalClicks >= CalDist) { // Check if TotalClicks has reached CalDist (1/10 mile travelled) TotalTenths ++; // increment TotalTenths and TotalClicks -=CalDist; // reduce TotalClicks by CalDist } while (TotalTenths >= 10) { // Check if TotalTenths has reached 10 TotalMiles ++; // if so, increment TotalMiles and TotalTenths -= 10; // decrease TotalTenths by 10 } while (TotalMiles > 99999) { // Check if TotalMiles has reached 100000 TotalMiles -= 100000; // if so, decrease TotalMiles by 100000 (rollover odometer) } // Trip odometer update DistanceClicks += Freq; // Add Freq to DistanceClicks while (DistanceClicks >= CalDist) { // Check if DistanceClicks has reached CalDist (1/10 mile travelled) DistanceTenths ++; // if so, increment DistanceTenths and DistanceClicks -=CalDist; // reduce DistanceClicks by CalDist } while (DistanceTenths >= 10) { // Check if DistanceTenths has reached 10 DistanceMiles ++; // if so, increment DistanceMiles and DistanceTenths -= 10; // decrease DistanceTenths by 10 } while (DistanceMiles > 999) { // Check if DistanceMiles has reached 1000 DistanceMiles -= 1000; // if so, decrease DistanceMiles by 1000 (rollover odometer) } if (MenuMode == 0) { // if we're not in a menu printmileage(); // then print the odometer } // foot distance display mode // DistanceFeetTenths is value is 100 times the actual value. This to allow for // much greater precision while avoiding the use of a float variable. if ((MenuMode == 2) && MenuModeActive) { // are we in foot distance display mode? If so... DistanceFeetTenths += (Freq * FeetScaleFactor); // Update DistanceFeetTenths while (DistanceFeetTenths >= 1000) { // Has the tenths digit overflowed (exceeded 10.00)? DistanceFeet ++; // If so, increment DistanceFeet by 1, DistanceFeetTenths -= 1000; // and decrease tenths digit by 1000 (represending 10.00) } while (DistanceFeet > 99999) { // Has DistanceFeet reached 100000? DistanceFeet -= 100000; // If so, roll it over (drop it by 100000 feet) } printfeet(); // print the distance in feet } // copy speed values for use next cycle FreqPrev2 = FreqPrev1; FreqPrev1 = Freq; Trigger = false; // Reset "Trigger"...until TimerCount reaches CalValue again } // Read the pushbutton switches for user input Button1State = digitalRead(Button1); // Read the state of Button1 Button2State = digitalRead(Button2); // Read the state of Button2 Button3State = digitalRead(Button3); // Read the state of Button3 Button4State = digitalRead(Button4); // Read the state of Button4 // ButtonPressed is true if any buttons are pressed, otherwise it's false ButtonPressed = !(Button1State && Button2State && Button3State && Button4State); // If a button was just pressed for the first time then reset the ButtonsOffTime timer if ((ButtonPressed == false) && (ButtonPressedPrev == true)) { ButtonsOffTime = millis(); }
|
|
|
|
« Last Edit: January 03, 2011, 01:27:30 am by ElectricWater »
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #6 on: January 03, 2011, 01:28:22 am » |
// test to see if it's been at over 50 mS since last button was released // and that a button is being pressed for the first time around if (((millis() - ButtonsOffTime) > 50) && (ButtonPressed == true) && (ButtonPressedPrev == false)) { switch (MenuMode) { // which menu is currently active? case 1: { // MenuMode 1 is the Calibration screen if (Button1State == LOW) { // test for Button1 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected MenuModeActive = false; // Set MenuModeActive to false lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL "); // overwrite total mileage with "CAL" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite CalDist with spaces //write CalDist to EEPROM: EEPROM.write(101, byte((CalDist & 65280)/256)); EEPROM.write(102, byte((CalDist & 255))); } else { MenuModeActive = true; // Set MenuModeActive to true CalDistPrev = CalDist; // copy CalDist to CalDistPrev lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL: "); // overwrite total mileage with "CAL:" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite trip mileage with spaces lcd.setCursor(15, 1); // set the cursor to (13,1) lcd.print(CalDist); // print CalDist } } if (Button2State == LOW) { // test for Button2 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected MenuModeActive = false; // Set MenuModeActive to false lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL "); // overwrite total mileage with "CAL" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite CalDist with spaces CalDist = CalDistPrev; // set CalDist back to CalDistPrev (toss changes) CalValue = int((2812500.0 / CalDist) + .5); // recalculate CalValue from CalDist } else { MenuMode = 0; // Set MenuMode to 0 lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" "); // overwrite "CAL" with spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite CalDist with spaces printmileage(); // display the odometer } } if (Button3State == LOW) { // test for Button3 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected CalDist ++; // Increment CalDist CalValue = int((2812500.0 / CalDist) + .5); // recalculate CalValue from CalDist lcd.setCursor(15, 1); // set the cursor to (13,0) lcd.print(CalDist); // print CalDist } else { MenuMode = 2; // Go to the previous menu lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" FEET "); // overwrite menu item with "FEET:" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite lower menu area with spaces } } if (Button4State == LOW) { // test for Button4 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected CalDist --; // decrement CalDist CalValue = int((2812500.0 / CalDist) + .5); // recalculate CalValue from CalDist lcd.setCursor(15, 1); // set the cursor to (13,0) lcd.print(CalDist); // print CalDist } else { MenuMode = 2; // Go to the previous menu lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" FEET "); // overwrite menu item with "FEET:" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite lower menu area with spaces } } break; } case 2: { // MenuMode 2 is the Foot distance meter if (Button1State == LOW) { // test for Button1 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected DistanceFeet = 0; // Reset DistanceFeet to 0 DistanceFeetTenths = 0; // Reset DistanceFeetTenths to 0 printfeet(); // Print the distance in feet } else { MenuModeActive = true; // Set MenuModeActive to true (enter this mode) lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" FEET: "); // overwrite total mileage with "FEET:" & spaces DistanceFeet = 0; // Reset DistanceFeet to 0 DistanceFeetTenths = 0; // Reset DistanceFeetTenths to 0 FeetScaleFactor = 528000 / CalDist; // Calculate FeetScaleFactor from CalValue printfeet(); // Print the distance in feet } } if (Button2State == LOW) { // test for Button2 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected MenuModeActive = false; // Set MenuModeActive to true (exit this mode) lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" FEET "); // overwrite total mileage with "FEET:" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite feet distance with spaces } else { MenuMode = 0; // Set MenuMode to 0 lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" "); // overwrite "CAL" with spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite CalDist with spaces printmileage(); // display the odometer } } if (Button3State == LOW) { // test for Button3 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected // do nothing } else { MenuMode = 1; // Go to the previous menu lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL "); // overwrite menu item with "CAL" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite lower menu area with spaces } } if (Button4State == LOW) { // test for Button4 being pressed if (MenuModeActive) { // test to see if a menu mode has been selected // do nothing } else { MenuMode = 1; // Go to the previous menu lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL "); // overwrite menu item with "CAL" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite lower menu area with spaces } } break; }
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #7 on: January 03, 2011, 01:29:04 am » |
default: { // MenuMode 0 is the default screen if ((Button1State) == LOW) { // test for Button1 being pressed DistanceMiles = 0; // if so, reset distance miles to zero DistanceTenths = 0; // also, reset the tenths to zero DistanceClicks = 0; // finally, reset the clicks to zero printmileage(); // display the odometer break; }
if ((Button2State) == LOW) { // test for Button2 being pressed MenuMode = 1; // Set MenuMode to 1 CalDistPrev = CalDist; // copy CalDist to CalDistPrev lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" CAL "); // overwrite total mileage with "CAL" & spaces lcd.setCursor(13, 1); // set the cursor to (13,1) lcd.print(" "); // overwrite trip mileage with spaces break; } } } } ButtonPressedPrev = ButtonPressed; // store ButtonPressed in ButtonPressedPrev for test on next loop }
void AddSensorCount() { // This is the subroutine that is called when interrupt 0 goes high SensorCount++; // Increment SensorCount by 1 }
void EEPROMwrite() { // This is the subroutine that is called when interrupt 1 goes low // store mileage variables in EEPROM EEPROM.write(0, byte((DistanceMiles & 65280)/256)); EEPROM.write(1, byte((DistanceMiles & 255))); EEPROM.write(2, byte(DistanceTenths)); EEPROM.write(3, byte((DistanceClicks) & 65280)/256); EEPROM.write(4, byte((DistanceClicks) & 255)); EEPROM.write(5, byte((TotalMiles & 16711680)/65536)); EEPROM.write(6, byte((TotalMiles & 65280)/256)); EEPROM.write(7, byte((TotalMiles & 255))); EEPROM.write(8, byte(TotalTenths)); EEPROM.write(9, byte((TotalClicks) & 65280)/256); EEPROM.write(10, byte((TotalClicks) & 255)); }
void loadchars() { // This subroutine programs the custom character data into the LCD lcd.command(64); // Custom character 0 lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); // Custom character 1 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); // Custom character 2 lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); lcd.write(byte(B11111)); // Custom character 3 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B01110)); lcd.write(byte(B01110)); lcd.write(byte(B01110)); // Custom character 4 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B01110)); lcd.write(byte(B01110)); lcd.write(byte(B01110)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); // Custom character 5 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); // Custom character 6 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); // Custom character 7 lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.write(byte(B00000)); lcd.home(); }
void printmileage() { lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(int((TotalMiles % 100000) / 10000)); // print the total miles ten-thousands digit lcd.print(int((TotalMiles % 10000) / 1000)); // print the total miles thousands digit lcd.print(int((TotalMiles % 1000) / 100)); // print the total miles hundreds digit lcd.print(int((TotalMiles % 100) / 10)); // print the total miles tens digit lcd.print(TotalMiles % 10); // print the total miles ones digit lcd.print("."); // print the decimal point lcd.print(TotalTenths); // print the total miles tenths digit lcd.setCursor(14, 1); // set the cursor to (15,0) lcd.print(int((DistanceMiles % 1000) / 100)); // print the distance miles hundreds digit lcd.print(int((DistanceMiles % 100) / 10)); // print the distance miles hundreds digit lcd.print(DistanceMiles % 10); // print the distance miles hundreds digit lcd.print("."); // print the decimal point lcd.print(DistanceTenths); // print the distance miles tenths digit }
void printfeet() { lcd.setCursor(13, 0); // set the cursor to (13,0) lcd.print(" FEET: "); // print the FEET units lcd.setCursor(13, 1); // set the cursor to (13,0) lcd.print(int((DistanceFeet % 100000) / 10000)); // print the distance feet ten thousands digit lcd.print(int((DistanceFeet % 10000) / 1000)); // print the distance feet thousands digit lcd.print(int((DistanceFeet % 1000) / 100)); // print the distance feet hundreds digit lcd.print(int((DistanceFeet % 100) / 10)); // print the distance feet tens digit lcd.print(DistanceFeet % 10); // print the distance feet ones digit lcd.print("."); // print the decimal point lcd.print(DistanceFeetTenths); // print the distance feet tenths digit }
void printbigchar(byte digit, byte col) { // This subroutine prints the big font characters on the LCD screen if (digit > 9) return; // anything above 9 gets rejected for (int i = 0; i < 2; i++) { // count i from 0 to 1 lcd.setCursor(col*4 , i); // set LCD cursor at correct point for (int j = 0; j < 3; j++) { // count j from 0 to 2 lcd.write(bignums[digit][i][j]); // write proper block to LCD from array } lcd.write(254); // write an empty space } lcd.setCursor(col + 4, 0); // move the cursor to the top line, col + 4 }
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
India
Offline
Full Member
Karma: 1
Posts: 131
The Tech Gamer
|
 |
« Reply #8 on: January 03, 2011, 09:11:33 am » |
I like the way you used the LCD to display large characters. Hey the speedometer works on the principle of Doppler's effect? Is it? I've read it somewhere.
|
|
|
|
|
Logged
|
|I love Technology|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 1
Arduino rocks
|
 |
« Reply #9 on: January 03, 2011, 02:32:53 pm » |
Nice project! I'm working on an automotive Arduino-based project so am interested to see what others are doing. (Maybe we need an automotive or telematics forum/thread?)
Question: How are you powering the Arduino? I know that since most cars use a nominally 12v electrical system, theoretically it should be possible just to route this into the Arduino's power plug. However, the "Practical Arduino" book notes that the actual voltage in auto electrical systems can vary much more widely and suggests using a separate power supply with its own regulator. How are you handling this? Thanks...
|
|
|
|
|
Logged
|
|
|
|
|
Left Coast, CA (USA)
Offline
Brattain Member
Karma: 279
Posts: 15314
Measurement changes behavior
|
 |
« Reply #10 on: January 03, 2011, 06:22:08 pm » |
I know that since most cars use a nominally 12v electrical system, theoretically it should be possible just to route this into the Arduino's power plug. However, the "Practical Arduino" book notes that the actual voltage in auto electrical systems can vary much more widely and suggests using a separate power supply with its own regulator. How are you handling this? Thanks... An auto battery systems voltage can vary quite a lot between depending on state of charge, etc. I think 16vdc is the max one can expect to see. This is not too much for the arduino external power although the closer to 8 volts the cooler the regulator on the arduino will run. The real problem with auto voltage is all the noise and load switching going on can cause lots of problems with some electronics. There is also a phenomenon called a load dump where almost 50volt can be spiked onto the auto's 12volt system. Anyway lots of people suggest to utilize extra filters, transient suppression and over voltage protection for sensitive electronic used in autos. The input filter capacitor on the arduino's voltage regulator is probably the most vulnerable component and I forget it's voltage rating. Lefty
|
|
|
|
« Last Edit: January 03, 2011, 06:24:05 pm by retrolefty »
|
Logged
|
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #11 on: January 04, 2011, 10:24:11 pm » |
I like the way you used the LCD to display large characters. Hey the speedometer works on the principle of Doppler's effect? Is it? I've read it somewhere. Thank you, but I can't take credit for the large characters. I was inspired by this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1245352653I'm not sure if Doppler's principle would apply to this particular design. Maybe more so if I were measuring my speed with a radar. 8-) For power I'm running off the vehicle's battery, but I did take some protective measures. There's a dedicated line that connects very close to the battery itself, which from what I understand makes a pretty significant filter. I plan to use a key-on powered relay in the future, but for now I just plug in the PCB when I'm going to drive somewhere. I also have a simple overvoltage/overcurrent protection circuit that I constructed in the power cable:  So far it's put up with almost daily use for about 9 months with no trouble. 
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
|
|
California
Offline
Newbie
Karma: 0
Posts: 44
What was that?
|
 |
« Reply #13 on: January 07, 2011, 12:31:53 am » |
Wow, those are nice! I can see why they're your favorites!
|
|
|
|
|
Logged
|
Inconveniencing electrons one drop at a time
|
|
|
|
Rhode Island, USA
Offline
Newbie
Karma: 0
Posts: 16
What can't be done with an Arduino?
|
 |
« Reply #14 on: January 07, 2011, 05:23:41 pm » |
a power-fail circuit that saves the odometer data into EEPROM when a power failure is detected I find this to be one of the coolest aspects of the project. I don't think I would have ever considered taking it that far. My solution probably would have been to update the EEPROM whenever the vehicle comes to a stop or if a max allowed amount of time passed since the last write. I also would have shifted the memory address of the updates every write for wear evening. Then at start-up search for the data entry with the highest values. Good work. I have found it to be quite common for microcontroller hobbyists to use multiple lines to print large numbers on alphanumeric LCDs. If you are looking to improve upon that, I have seen others with more natural looking digits consisting of curved and/or tapered edges. I don't know how much more complex it is to implement than the more blocky alternative but it is something you may want to look into.
|
|
|
|
|
Logged
|
|
|
|
|
|