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
*/