Project website and wiki: http://robotmeter.com/dokuwiki/
All details will be better organized in the wiki.
Project completion % 90:
|----------|----------|----------|----------|----------|----------|---------|-------->|__________|
[Disclaimer sort of thing]
Please understand that all code and info provided is on a experimental only basis. I do not guarantee any of my work as this is a personal project (at this time and subject to change) and in a developmental phase. I am not responsible for any damage to you or your personal property if you choose to use the code provided or follow my examples. As always, work within your skill level, ask for help when necessary, and keep your eyes on the road when driving.
[Consolidated info]
What it is: A kitchen sink digital gauge multi-gauge solution built on the open source arduino platform. It has a focus on in car display and car related sensors however, the code could be easily adapted for other applications (experimental purposes, weather monitoring, pet monitoring, personal health meter, RC car telemetry, robot telemetry, etc.). This is not a an OBD II reader, nor do I intend to hook it into the ECU. However, with this hardware and the proper knowledge it should be possible for a skilled person to tap into the ECU to get info like RPM, speed, etc.
What it does:
-Powers off of the car's 12V power supply.
-Logging mode-logs all parameters via usb to a computer (any os) in a comma delimited text format for easy excel, OO-calc, matlab, etc. consumption.
-Oil temperature
-Oil temperature warmup-starts up in oil temp mode. When the oil temp reaches a set point (ie the car is warmed up) the unit switches to the default mode (default mode is the accelerometer display)
-2 axis 2g accelerometer (I'm using a 1.2g but plan to switch) with a lateral bar graph and discrete display.
-2 x Misc temperature sensors-useful for cabin temp, ambient temp, and various underhood temps. Could also be setup for you to jump out of the car in between laps and test tire temps.
-lap timer with 2 laps/lines-this is persistent and will continue to run even if you switch modes. However, it has a 9 hour time limit based on a limit in the microcontroller used in the arduino.
-turbo (boost) pressure
-peak hold/reset for most sensors
-several types of displays (in code configurable for now). A two sensor display with peaks. A single sensor display with bar graph and peaks. A specialized accelerometer display. (A 4 sensor non-peak display is also a possibility, but has not been coded yet).
-temporary peak bar hold on the accelerometer (looks like a VU meter on a stereo system)
-Warnings-currently setup to flash the line of the sensor that has gone over a set point. In addition (though not currently configured) a warning light could go on or a buzzer could sound. This could be configured at multiple step levels (in code, and not written yet).
-boost referenced (or more generically, sensor referenced) Intercooler water spray. (Planned, not yet implemented).
An older demo video: http://randomcrap.us/otherjustin/modes_demo.mov
The code (v 0.3.0):
A good version/usable. All sensors are set up correctly. Global warn (in every mode it checks all the sensors periodically and warns when necessary) is currently turned off for most sensors (ie commented out). Piezo code is also commented out. Oil temp sensor code has not been checked...but should be close.
Other useful code:
www.randomcrap.us/otherjustin/meter_independent_lines.pde
The start of a version of the interface with independent lines. As in, you can change what one line displays independent of the others. I stopped coding on this because I didn't like how much you had to work to get the display to look exactly like you want while trying to drive a car at the same time (dangerous). But, if you want to work on this version of the code feel free.
www.randomcrap.us/otherjustin/serial_lcd_spash_set.pde
A sketch that can be used to set the splash screen on the lcd. Change the text on the appropriate line. Flash to the board...let run for a sec and you're set.
ERRORS THAT NEED FIXING: Spacing is fixed and so is the boost formula.
Component list:
Parts were sourced from Sparkfun, Digikey, egauges, radioshack (use radioshack only for convenience as parts are cheaper at digikey, newark, sparkfun, etc.), and directly from some manufacturers as samples.
-Arduino (Arduino - HomePage)-I'm using the standard USB version as it allows me to easily log all sensor parameters to a laptop.
-Sparkfun serial lcd (SparkFun Electronics)
-accelerometer (using a sparkfun breakout board...code was written for this one: SparkFun Electronics but will probably switch to this one: SparkFun Electronics)
-Oil temperature sender is a ISSPRO sender (part R8958). They were nice enough to send me their RT curve for this specific sender. All I had to do was call them. In addition I was surprised to get RT curve points from autometer, cyberdyne, and dakota digital. I ended up using the ISSPRO sender because it was the only one that fit my galley plug without an adapter.
-Temperature sensors: 252FG1K from ussensors (U.S. Sensor Thermistor, Manufacturer of NTC thermistors and RTD and thermistor temperature sensors), but you could use whatever thermistor you wanted. The only work involved would be mapping out the RT curve. I plan to insulate these with fiberglass sleeves much like the insulation on an EGT probe.
-Turbo pressure sensor: MPX4250GP from freescale (freescale is very nice in that they provide you with free samples of some of their products. Very good for a prototype. Please don't abuse their sample system.) You will also need tubing, a pilled t-fitting, and a some zip ties if you don't already have them.
-A lot of 18 and 22 gauge wire. I believe that the oil temp sensor requires 18 or 16 gauge wire (I can't recall off the top of my head).
-A piezo or a warning led...I haven't installed this yet but it should be as easy as wiring up an LED to blink. Any one that'll drive off of the 5V the arduino supplies will work.
-A relay and supporting circuitry for the boost referenced IC spray is on its way (watch this space!)
-buttons-any normally open (NO) SPST momentary button will work. I used what they had at radioshack and i'm not entirely pleased with how they held up. For now they work, but I'd suggest getting something better. I may, later switch to conductive touch sensor button sort of thing or "harvest" buttons from an older telephone.
-A 12V voltage regulator-this was an important missing component for a while. After some hard driving the original prototype would stop working and refuse to boot. For the longest time I thought it was a heat issue. Turns out it was a voltage issue and the car was actually providing 14.5V or more at times. This caused the arduino to start flipping bits on the microcontroller. The voltage regulator prevents this.
-An inline fuse was used for safely powering the arduino off the car's battery (I can't recall the specifics off the top of my head)
-A spare USB cable...to leave in the car
-2.5mm power plug to easily put the arduino in and out of the car.
-I used the standard clock location and some creative dremel tool work + a non-flammable glue to hold the display. You may wish to get an enclosure for this. I recommend hunting around for free samples (I personally enjoy the ones I got from PacTec and OKW. Pictures of these are in the thread)
-male header pins-attach these to the signal wires coming off of your sensors and they make swapping components out easy.
-female header pins and small board can be used to make a simple power and ground extender (the arduino does not have enough power and ground pins for every sensor)
-470 Ohm resistors to be used with the above listed thermistors in a voltage divider configuration (Voltage divider - Wikipedia, the free encyclopedia)
-a 220 Ohm resistor to be used in a voltage divider configuration with the oil temperature sender (I'm 90% positive it is a 220 Ohm but will double check).
-a 10k Ohm resistor to fix a bug with the arduino not sending serial signals to the lcd when not USB powered. (This plugs from gnd to RX).
Circuit diagram (using eaglecad http://www.cadsoftusa.com/):
Useful charts and stuff:
RT curve straight from ISSPRO
Graph showing use of multiple fit lines for complex thermistor RT curve
BTW I'm currently looking for a job and would love something related to this project.
A word on licensing, money, etc:
-I'm not a vendor on this site and the more and more I look at it I may never become a vendor on this site. This means, you cannot purchase things or services from me via this website and I cannot advertise things or services on this website.
-Their will be an open source license for this software. It will probably be a GPL license or some derivative. I have used adaptations of example code provided in the arduino forum and general knowledge base to produce this code. The code does not contain any proprietary information. To the best of my knowledge the RT curves for the theremistors are not proprietary knowledge and were given to me freely by the manufacturers.
-To the best of my knowledge I have not infringed on any copyrights, patents, etc. If you believe their is a problem please contact me so we can rectify the situation. However, I believe that what I have produced is a joining of several "public knowledge" ideas into one device. These ideas include the use of sensors with a digital display, logging data to a computer via USB, putting multiple sensors on one device, flashing warning text, etc.
Pictars and more demos:
and a better demo: TinyPic - Share The Experience!™
[The Following is the original first post]
I've finally decided on the electronics project I'm going to start. Just wondering if anyone else would be interested/should I look into getting a vendor account.
I'll be building a digital accelerometer display to go in the place of the clock. When you turn on the car it'll show a custom welcome message and then it'll have two lines of display. The top will be a meter/analog style output and the bottom will have an instant and a max g force reading.
When all is said and done it'll probably be fairly easy to change the custom welcome message via usb and it'll be able to draw power from 12V (with a fuse of course). It should also be backlit and come in several colors.
I'll be using open source software/hardware (arduino, etc.) so if I do become a vendor you'll only be paying for assembly, support, and shipping.
All comments welcome.
(basically I watered down what I wanted to make from this:
track data logger - IWSTI.com: Subaru WRX STI Forums)
Sort of like this idea from a while back (Clock replacement data display / IC Spray controller - ScoobyMods)
I think I'm going to go with this LCD: SparkFun Electronics or maybe this one SparkFun Electronics
All details will be better organized in the wiki.
Project completion % 90:
|----------|----------|----------|----------|----------|----------|---------|-------->|__________|
[Disclaimer sort of thing]
Please understand that all code and info provided is on a experimental only basis. I do not guarantee any of my work as this is a personal project (at this time and subject to change) and in a developmental phase. I am not responsible for any damage to you or your personal property if you choose to use the code provided or follow my examples. As always, work within your skill level, ask for help when necessary, and keep your eyes on the road when driving.
[Consolidated info]
What it is: A kitchen sink digital gauge multi-gauge solution built on the open source arduino platform. It has a focus on in car display and car related sensors however, the code could be easily adapted for other applications (experimental purposes, weather monitoring, pet monitoring, personal health meter, RC car telemetry, robot telemetry, etc.). This is not a an OBD II reader, nor do I intend to hook it into the ECU. However, with this hardware and the proper knowledge it should be possible for a skilled person to tap into the ECU to get info like RPM, speed, etc.
What it does:
-Powers off of the car's 12V power supply.
-Logging mode-logs all parameters via usb to a computer (any os) in a comma delimited text format for easy excel, OO-calc, matlab, etc. consumption.
-Oil temperature
-Oil temperature warmup-starts up in oil temp mode. When the oil temp reaches a set point (ie the car is warmed up) the unit switches to the default mode (default mode is the accelerometer display)
-2 axis 2g accelerometer (I'm using a 1.2g but plan to switch) with a lateral bar graph and discrete display.
-2 x Misc temperature sensors-useful for cabin temp, ambient temp, and various underhood temps. Could also be setup for you to jump out of the car in between laps and test tire temps.
-lap timer with 2 laps/lines-this is persistent and will continue to run even if you switch modes. However, it has a 9 hour time limit based on a limit in the microcontroller used in the arduino.
-turbo (boost) pressure
-peak hold/reset for most sensors
-several types of displays (in code configurable for now). A two sensor display with peaks. A single sensor display with bar graph and peaks. A specialized accelerometer display. (A 4 sensor non-peak display is also a possibility, but has not been coded yet).
-temporary peak bar hold on the accelerometer (looks like a VU meter on a stereo system)
-Warnings-currently setup to flash the line of the sensor that has gone over a set point. In addition (though not currently configured) a warning light could go on or a buzzer could sound. This could be configured at multiple step levels (in code, and not written yet).
-boost referenced (or more generically, sensor referenced) Intercooler water spray. (Planned, not yet implemented).
An older demo video: http://randomcrap.us/otherjustin/modes_demo.mov
The code (v 0.3.0):
A good version/usable. All sensors are set up correctly. Global warn (in every mode it checks all the sensors periodically and warns when necessary) is currently turned off for most sensors (ie commented out). Piezo code is also commented out. Oil temp sensor code has not been checked...but should be close.
Code:
/* Copyright (c) 2008, Justin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Use of this software for commercial gain without written consent of
* above mentioned copyright holder is NOT permitted. This includes, but
* is not limited to products which do not contain/or are not distributed
* with the software but are built with the purpose of using this software.
*
* THIS SOFTWARE IS PROVIDED BY Justin N ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
//also a replacement for gauges
//set up for 2 display
//also set up for 1 and bar graph display
//multi mode selection via the "A" button
//"B" button clears peaks and operates the usb logging as well as the timer function
#include <SoftwareSerial.h>
#define rxPin 4
#define txPin 5
SoftwareSerial LCDSerial = SoftwareSerial(rxPin, txPin);
byte pinState = 0;
int logfilecount = 0;
int xval = 4;
int yval = 5;
int negpeak = 0;
int pospeak = 0;
int pospeakcount = 0;
int negpeakcount = 0;
int zerogy = 512;
int zerogx = 512;
unsigned long T1 = 0;
unsigned long T2 = 0;
int mode = 1;
int buttonApin = 10;
int buttonBpin = 13;
int buttonBval = HIGH;
int sprayTriggerPin = 11;
int t1pin = 2;
int t2pin = 3;
int tempPin = 1;
int boostPin = 0;
int timer_state = 0;
long tempmillisT1 = 0;
long tempmillisT2 = 0;
boolean startuptempswitch = false;
//global peaks...so that you can switch modes and preserve peaks
long boost_peak = 0;
long temp_peak = 0;
long temp1_peak = 0;
long temp2_peak = 0;
void setup(){
//delay(10000);
//setup the accelerometer and screen
Serial.begin(9600);
Serial.print("IPA"); //to vdip logger
Serial.print(13, BYTE); //to vdip logger
//delay(10000);
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
pinMode(sprayTriggerPin, OUTPUT);
digitalWrite(sprayTriggerPin, LOW);
LCDSerial.begin(9600);
//put wait code to look and see if there is a
//response from the VDIP1. If there isn't try connecting again
//read the root dir in the memstick to set the
//log file's name/number (its sequential)
//Serial.print("DIR");
//Serial.print(13, BYTE);
//LCDSerial.print(0xFE, BYTE); //command flag
//LCDSerial.print(128, BYTE);
//while(Serial.available() > 0){
//LCDSerial.print(Serial.read());
//}
//LCDSerial.print(0xFE, BYTE); //command flag
//LCDSerial.print(128, BYTE);
//LCDSerial.print("initialized");
//read
int tfilenum = 0;
/*if(Serial.available() > 0){
//look for "LOG"
int input = Serial.read();
Serial.print(input);
if (input == 76){ //L
input = Serial.read();
if (input == 79){ //O
input = Serial.read();
if (input == 71){ //G
//get the numers after "LOG"
input = Serial.read();
tfilenum = input - 48;
input = Serial.read();
while (input > 48 && input < 58){
input = Serial.read();
tfilenum = tfilenum * 10 + input - 48;
}
}
}
}
}*/
//logfilecount = tfilenum + 1;
Serial.print("OPW LOG555");
Serial.print(13, BYTE);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
//LCDSerial.print("file write");
Serial.print("CFL LOG555");
Serial.print(13,BYTE);
//this next line for debug only
//delay(10000);
LCDSerial.print(0x7C, BYTE);
LCDSerial.print(150, BYTE);
//LCDSerial.print(0xFE, BYTE);
//LCDSerial.print(0x01, BYTE);
//zero the accelerometer...but only for small tilts
int tempreading = analogRead(yval);
if ((tempreading < 600) && (tempreading > 400)){
zerogy = tempreading;
}
else {
zerogy = 512;
}
tempreading = analogRead(xval);
if ((tempreading < 600) && (tempreading > 400)){
zerogx = tempreading;
}
else {
zerogx = 512;
}
//setup the buttons as inputs
pinMode(buttonApin, INPUT);
pinMode(buttonBpin, INPUT);
}
void loop (){
//WHAT MODE
if (digitalRead(buttonApin) == LOW){
while (digitalRead(buttonApin)){
//avoids flipping modes rapidly
}
if (mode == 1){mode=2;}
else if (mode == 2){mode=3;}
else if (mode == 3){mode=4;}
else if (mode == 4){mode=5;}
else if (mode == 5){mode=6;}
else if (mode == 6){mode=7;}
else mode = 1;
}
//ACCELEROMETER
if (mode == 2) {
accelerometer();
}
//LAP TIMER
if (mode == 6){
runTimer();
}
if (mode == 1){
temp_meter();
}
if (mode == 3){
boost_meter();
}
if (mode == 4){
temp_boost_meter();
}
if (mode == 5){
two_temp_meter();
}
if (mode == 7){
usb_logger();
}
}
void test_all_meters(){
/* if ((analogRead(tempPin) > 512) && (mode != 1)){ //oil temp
mode = 1;
temp_meter();
}
if ((analogRead(t1pin) > 512) && (mode != 6)){ //temp1
mode = 6;
two_temp_meter();
}
if ((analogRead(t2pin) > 512) && (mode != 6)){ //temp2
mode = 6;
two_temp_meter();
}*/
if (( (lookup_boost(analogRead(boostPin))) > 140) && (mode != 3)){ //boost
mode = 3;
boost_peak=lookup_boost(analogRead(boostPin));
spray();
boost_meter();
}
return;
}
//trigger something on pin x via the
void spray(){
digitalWrite(sprayTriggerPin, HIGH);
delay(3000); //how long the spray lasts...spray will always last a little longer due to the built in timer in the car
digitalWrite(sprayTriggerPin, LOW);
}
void accelerometer(){
if (startuptempswitch == true){
while (digitalRead(buttonApin) == LOW){}
}
test_all_meters();
startuptempswitch = true;
int accelx = ( getAccelerometerData (xval) );
int accely = ( getAccelerometerData (yval) );
pospeakcount++;
negpeakcount++;
peak(accely);
LCDSerial.print(0xFE, BYTE); //command flag
LCDSerial.print(128, BYTE);
delay(60);
printBarGraph(accely);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
delay(60);
LCDSerial.print("x:");
printAccelerometerReadout(accelx);
LCDSerial.print(" y: ");
printAccelerometerReadout(accely);
}
void runTimer(){
while (digitalRead(buttonApin) == LOW){
}
test_all_meters();
if (timer_state == 0){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
LCDSerial.print(0xFE, BYTE); //command flag
LCDSerial.print(128, BYTE);
delay(20);
LCDSerial.print("T1: 00:00:00.00");
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
LCDSerial.print("T2: 00:00:00.00");
T1 = 0;
T2 = 0;
while (digitalRead(buttonBpin) == HIGH){
//wait for start
if (digitalRead(buttonApin) == LOW){
return;
}
}
while (digitalRead(buttonBpin) == LOW){
//we don't want to skip the start if you hold the button too long
//timing actually happens on button release
timer_state = 1;
tempmillisT1 = millis();
}
}
if (timer_state == 1){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
while (digitalRead(buttonBpin) == HIGH){
test_all_meters();
//timing
if (digitalRead(buttonApin) == LOW){
return;
}
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
T1 = millis() - tempmillisT1;
//printing to the screen
print_T1();
//a short pause
delay(50);
}
while (digitalRead(buttonBpin) == LOW){
//stop T1 timing
test_all_meters();
timer_state = 2;
}
}
if (timer_state == 2){ //timed shown on T1 and zeros on T2 waiting to start T2
//display T1 if we cycle through modes and reach this
//---
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
//printing to the screen
print_T1();
//a short pause
delay(50);
//display T2 as zeros
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
LCDSerial.print("T2: 00:00:00.00");
//---
while (digitalRead(buttonBpin) == HIGH){
//wait for timing T2
if (digitalRead(buttonApin) == LOW){
return;
}
test_all_meters();
}
while (digitalRead(buttonBpin) == LOW){
//clicking to start T2
timer_state = 3;
tempmillisT2 = millis();
}
}
if (timer_state == 3){ //T1 is displayed and T2 is running
//display T1 if this mode is reache durring mode switching
//--
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
//printing to the screen
print_T1();
//--
delay(10);
while (digitalRead(buttonBpin) == HIGH){
test_all_meters();
if (digitalRead(buttonApin) == LOW){
return;
}
//T2 timing
T2 = millis() - tempmillisT2;
//printing to the screen
print_T2();
//a short pause
delay(50);
}
while (digitalRead(buttonBpin) == LOW){
//click to stop
timer_state = 4;
}
}
if (timer_state == 4){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
while (digitalRead(buttonBpin) == HIGH){
//what to do if we've reached this direct thru mode switching
//---
//printing to the screen
print_T1();
//a short pause
delay(50);
//display T2
print_T2();
//a short pause
delay(50);
//---
test_all_meters();
if (digitalRead(buttonApin) == LOW){
return;
}
}
while (digitalRead(buttonBpin) == LOW){}
//on release return to main loop
timer_state = 0;
}
return;
}
void print_T1 (){
//printing to the screen
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
unsigned long T1millis = T1%1000/10;
unsigned long T1seconds = T1%60000/1000;
unsigned long T1minutes = T1%3600000/60000;
unsigned long T1hours = T1/3600000;
LCDSerial.print("T1: ");
printInTens(T1hours);
LCDSerial.print(":");
printInTens(T1minutes);
LCDSerial.print(":");
printInTens(T1seconds);
LCDSerial.print(".");
printInTens(T1millis);
}
void print_T2 (){
//printing to the screen
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
unsigned long T2millis = T2%1000/10;
unsigned long T2seconds = T2%60000/1000;
unsigned long T2minutes = T2%3600000/60000;
unsigned long T2hours = T2/3600000;
LCDSerial.print("T2: ");
printInTens(T2hours);
LCDSerial.print(":");
printInTens(T2minutes);
LCDSerial.print(":");
printInTens(T2seconds);
LCDSerial.print(".");
printInTens(T2millis);
}
void boost_meter(){
while (digitalRead(buttonApin) == LOW){}
meter_splash("Boost", " Meter");
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
LCDSerial.print("Boost Meter");
delay(1000);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
long reading = 0;
while (digitalRead(buttonApin) == HIGH){
test_all_meters();
if (digitalRead(buttonBpin) == LOW) {
boost_peak = 0;
}
//non sensor code
//reading = reading + 1;
//real reading code
//0 psi = 12 Vcount;
reading = lookup_boost( long(analogRead(boostPin)) );
boost_peak = max (reading, boost_peak);
generic_bar_display ("psi", 170, reading, boost_peak, 145);
delay(50);
}
return;
}
void temp_meter(){
if (startuptempswitch == false){
while (digitalRead(buttonApin) == LOW){}
}
meter_splash("Oil Temp", " Meter");
long reading = 0;
while (digitalRead(buttonApin) == HIGH){
test_all_meters();
if (digitalRead(buttonBpin) == LOW) {
temp_peak = 0;
}
//reading = reading + 1;
reading = lookup_oil_temp(analogRead(tempPin));
if ((startuptempswitch == false) && (reading > 145)){
mode = 2;
return;
}
temp_peak = max (temp_peak, reading);
generic_bar_display ("oil", 3500, reading, temp_peak, 2600);
delay(50);
}
return;
}
void temp_boost_meter(){
while (digitalRead(buttonApin) == LOW){}
meter_splash("Temp/Boost Meter","");
long reading1 = 0;
long reading2 = 0;
while (digitalRead(buttonApin) == HIGH){
test_all_meters();
if (digitalRead(buttonBpin) == LOW) {
temp_peak = 0;
boost_peak = 0;
}
//reading1 = random(100,350);
reading1 = lookup_oil_temp(analogRead(tempPin));
reading2 = lookup_boost( long(analogRead(boostPin)) );
temp_peak = max (temp_peak, reading1);
boost_peak = max (boost_peak, reading2);
generic_dual_display ("tmp", 3500, reading1, temp_peak, 2000, "psi", 210, reading2, boost_peak, 140);
delay(50);
}
return;
}
void two_temp_meter(){
while (digitalRead(buttonApin) == LOW){}
meter_splash("2 Temp Meter", "");
long reading1 = 0;
long reading2 = 0;
while (digitalRead(buttonApin) == HIGH){
test_all_meters();
if (digitalRead(buttonBpin) == LOW) {
temp1_peak = 0;
temp2_peak = 0;
}
reading2 = lookup_temp((long)analogRead(t2pin));
reading1 = lookup_temp((long)analogRead(t1pin));
//reading1 = analogRead(t1pin);
temp1_peak = max (temp1_peak, reading1);
temp2_peak = max (temp2_peak, reading2);
generic_dual_display ("F 1", 1024, reading1, temp1_peak, 9999, "F 2", 3500, reading2, temp2_peak, 1000);
delay(20);
}
return;
}
void printInTens(int Tvar){
if (Tvar < 10){
LCDSerial.print("0");
LCDSerial.print(Tvar);
}
else LCDSerial.print(Tvar);
return;
}
void printBarGraph(int y) {
//clear the 1st 8 spacesb
if ( y >= 0){
LCDSerial.print(" ");
for(int i=1; i <= y/16; i++){
LCDSerial.print(0xFF, BYTE);
}
//fill the rest with spacesloo
for(int i=1; i <= (8-y/16); i++){
LCDSerial.print(" ");
}
}
if (y < 0) {
//1 - print spaces at the beginning
for (int i=1; i <= (8-abs(y/16)); i++){
LCDSerial.print(" ");
}
//2 - print blocks till the middle
for (int i=1; i <= abs(y/16); i++){
LCDSerial.print(0xFF, BYTE);
}
//3 - print spaces till the end
for (int i=1; i<=8; i++){
LCDSerial.print(" ");
}
}
//print the peaks if there are any
//negative
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
if (negpeak/16 < 0){
LCDSerial.print(0xFE, BYTE);
int npos = 128 + (8 - abs(negpeak/16));
LCDSerial.print(npos, BYTE);
LCDSerial.print(0xFF, BYTE);
}
//positive
if (pospeak/16 > 0){
LCDSerial.print(0xFE, BYTE);
int ppos = 128 + (8 + (pospeak/16));
LCDSerial.print(ppos, BYTE);
LCDSerial.print(0xFF, BYTE);
}
}
long lookup_boost(long boost){
//boost = ( (boost-106000) / 259000 );
// boost = ( (( boost * 398) / 1000) + 2); //2 is the y intercept
//398 changed to 378 for slope...because slope was too steep
boost = ( (( boost * 378) / 1000) - 4); ///10; //get rid of the divide by ten when adding decimals on display
return boost;
}
long lookup_oil_temp(long tval){
tval = tval * 1000; //added an extra 0
if (tval <= 11500){
return (9999);
}
if (tval >= 68100){
return (0);
}
if ((tval <= 68000)&&(tval > 39600)){
return (long)(((tval-134266)*10)/(-473));
}
if ((tval <= 39600)&&(tval > 28200)){
return (long)(((tval-115600)*10)/(-380));
}
if ((tval <= 28200)&&(tval > 19700)){
return (long)(((tval-93366)*10)/(-283));
}
if ((tval <= 19700)&&(tval > 11600)){
return (long)(((tval-54800)*10)/(-135));
}
}
long lookup_temp(long tval){
tval = tval * 100;
//tval = (long)(tval - (long)117588);
//return tval;
if (tval < 8900){
return (9999);
}
if (tval > 96000){
return (0);
}
if ((tval <= 96000)&&(tval > 93221)){
return (((tval-101577)*10)/(-172));
}
if ((tval <= 93221)&&(tval > 89610)){
return (((tval-104201)*10)/(-226));
}
if ((tval <= 89610)&&(tval > 85125)){
return (((tval-107738)*10)/(-280));
}
if ((tval <= 85125)&&(tval > 79139)){
return (((tval-112264)*10)/(-335));
}
if ((tval <= 79139)&&(tval > 70799)){
return (((tval-117588)*10)/(-388));
}
if ((tval <= 70799)&&(tval > 62470)){
return (((tval-121441)*10)/(-421));
}
if ((tval <= 62470)&&(tval > 53230)){
return (((tval-122367)*10)/(-428));
}
if ((tval <= 53230)&&(tval > 43707)){
return (((tval-118651)*10)/(-405));
}
if ((tval <= 43707)&&(tval > 36471)){
return (((tval-111349)*10)/(-366));
}
if ((tval <= 36471)&&(tval > 30685)){
return (((tval-102232)*10)/(-321));
}
if ((tval <= 30685)&&(tval > 24800)){
return (((tval-9078)*10)/(-270));
}
if ((tval <= 24800)&&(tval > 20000)){
return (((tval-78575)*10)/(-220));
}
if ((tval <= 20000)&&(tval > 15851)){
return (((tval-66507)*10)/(-175));
}
if ((tval <= 15851)&&(tval > 12380)){
return (((tval-55300)*10)/(-137));
}
if ((tval <= 12380)&&(tval > 9085)){
return (((tval-41752)*10)/(-94));
}
}
void printAccelerometerReadout(int reading){
if (reading >= 0 ) {
LCDSerial.print("+");
}
if (reading < 0) {
LCDSerial.print("-");
}
int afterdecimal = reading % 100;
LCDSerial.print(abs(reading/100));
if ( (afterdecimal > 9) || (afterdecimal < -9) ){
LCDSerial.print(".");
}
else {
LCDSerial.print(".0");
}
LCDSerial.print(abs(afterdecimal));
}
int getAccelerometerData (int axis){
int zerog = 512;
if (axis == 5){
zerog = zerogy;
}
if (axis == 3){
zerog = zerogx;
}
int rc = analogRead(axis);
int top =( (zerog - rc) ) ;
float frtrn = (((float)top/(float)158)*100); //158Vint jumps are 1g
int rtrn = (int)frtrn;
return rtrn;
}
void peak(int val){
if ( (val > 0) && (val > pospeak) ){ //pos peak compare and set
pospeak = val;
pospeakcount = 0;
}
if ( (val < 0) && (val < negpeak) ) { //neg peak compare and set
negpeak = val;
negpeakcount = 0;
}
else { //peak mark expires after x time
if (pospeakcount >= 20){
pospeakcount = 0;
pospeak = 0;
}
if (negpeakcount >= 20){
negpeakcount = 0;
negpeak = 0;
}
}
}
int numberofdigits(long value){
int digits = 1;
while (value/10 > 0){
value = value/10;
digits++;
}
if (value < 0){digits ++;}
return digits;
}
//positive only value 0 to X
//only 4 char titles (should be changed soon)
//1234567890123456
//TMP 134.5/ 314.5
//psi 14.5 / 14.5
//oil 1.0 / 1.4
//oil 0.3 / 0.4
void generic_bar_display(char title[ ], long high, long cur_value, long peak, long warn){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
LCDSerial.print(title);
LCDSerial.print(" ");
LCDSerial.print(cur_value/10);
LCDSerial.print(".");
LCDSerial.print(cur_value%10);
int ndigits = numberofdigits(cur_value) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 5 - ndigits; i++) {
LCDSerial.print(" ");
}
LCDSerial.print("/");
ndigits = numberofdigits(peak) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 6 - ndigits; i++){
LCDSerial.print(" ");
}
LCDSerial.print(peak/10);
LCDSerial.print(".");
LCDSerial.print(peak%10);
if (cur_value > warn){ //blink if warning threshold is met
warn_flash();
}
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
unsigned long abar = high/16;
unsigned long n_bars = cur_value/abar;
if (cur_value <= 0){n_bars=0;}
for(int i=1; i< n_bars; i++){
LCDSerial.print(0xFF, BYTE);
}
for (int i=1; i < (16 - n_bars); i++){
LCDSerial.print(" ");
}
delay(60);
}
//use only 4 char titles (should be changed soon)
void generic_dual_display (char title1[ ], long high1, long cur_value1, long peak1, long warn1, char title2[ ], long high2, long cur_value2, long peak2, long warn2){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
LCDSerial.print(title1);
LCDSerial.print(" ");
LCDSerial.print(cur_value1/10);
LCDSerial.print(".");
LCDSerial.print(cur_value1%10);
int ndigits = numberofdigits(cur_value1) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 5 - ndigits; i++) {
LCDSerial.print(" ");
}
LCDSerial.print("/");
ndigits = numberofdigits(peak1) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 6 - ndigits; i++){
LCDSerial.print(" ");
}
LCDSerial.print(peak1/10);
LCDSerial.print(".");
LCDSerial.print(peak1%10);
if (cur_value1 > warn1){ //blink if warning threshold is met
warn_flash();
}
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
LCDSerial.print(title2);
LCDSerial.print(" ");
LCDSerial.print(cur_value2/10);
LCDSerial.print(".");
LCDSerial.print(cur_value2%10);
ndigits = numberofdigits(cur_value2) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 5 - ndigits; i++) {
LCDSerial.print(" ");
}
LCDSerial.print("/");
ndigits = numberofdigits(peak2) + 1;
if (ndigits <= 2){ ndigits = ndigits + 1;}
for (int i = 0; i < 6 - ndigits; i++){
LCDSerial.print(" ");
}
LCDSerial.print(peak2/10);
LCDSerial.print(".");
LCDSerial.print(peak2%10);
if (cur_value2 > warn2){ //blink if warning threshold is met
warn_flash();
}
delay(60);
}
//quad display -- mus use 4 char (incl ":") for each title...ex "PSI:", "TEMP", "POOP", " ETC"
//void generic_quad_display(char title1[], unsigned long reading1, unsigned long warn1, char title2[], unsigned long reading2, unsigned long warn2, char title3[], unsigned long reading3, unsigned long warn3, char title4[], unsigned long reading4, unsigned long warn4){
//LCDSerial.print(0xFE, BYTE);
//LCDSerial.print(128, BYTE);
// LCDSerial.print(title1);
//}
//changed from the regular v4 to log to a serial logging device not
//to usb on a laptop
void usb_logger(){
while (digitalRead(buttonApin) == LOW){}
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE);
LCDSerial.print("USB Logging");
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE);
LCDSerial.print("Press B");
while (digitalRead(buttonBpin) == HIGH){
if (digitalRead(buttonApin)==LOW){
return;
}
}
delay(500);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE);
LCDSerial.print("Logging"); //command flag
//LCDSerial.print(0x08, BYTE); //turn off visual display
//LCDSerial.print("Data logger: \n");
//open a new file based on a millis timestamp, give it a header and start writting
Serial.print("OPW log");
Serial.print(logfilecount);
Serial.print(".txt");
Serial.print(13, BYTE);
Serial.print("WRF ");
Serial.print(42);
Serial.print("T1, T2, Oil Temp, Boost, x accel, y accel\n");
Serial.print(13, BYTE);
while (digitalRead(buttonBpin) == LOW){}
while ( (digitalRead(buttonApin) == HIGH) && (digitalRead(buttonBpin) == HIGH) ){
//logging output
Serial.print("WRF ");
long t1 = ( (lookup_temp(analogRead(t1pin))) );
long t2 = ( (lookup_temp(analogRead(t2pin))) );
long oil = ( lookup_oil_temp(analogRead(tempPin)));
long press = lookup_boost(analogRead(boostPin));
long ax = getAccelerometerData (xval);
long ay = getAccelerometerData (yval);
//calc the number of characters
int linelen = 5 + numberofdigits(t1) + numberofdigits(t2) + numberofdigits(oil) + numberofdigits(press) + numberofdigits(ax) + numberofdigits(ay);
//print the number of characters for this line
Serial.print(linelen);
Serial.print(13, BYTE);
Serial.print(t1);
Serial.print(",");
//get T2 and convert
//LCDSerial.print( lookup_temp(analogRead(t2pin)) );
Serial.print(t2);
Serial.print(",");
//get oil temp and convert
//LCDSerial.print( analogRead(tempPin) );
Serial.print(oil);
Serial.print(",");
//get boost and convert
Serial.print(press);
//LCDSerial.print("###");
Serial.print(",");
//get x accel
Serial.print(ax);
Serial.print(",");
//get y accel
Serial.print(ay);
//Serial.print("\n");
Serial.print(13, BYTE);
}
while ( (digitalRead(buttonApin) == LOW) && (digitalRead(buttonBpin) == LOW) ){}
//close the file
Serial.print("CLF log%");
Serial.print(logfilecount);
Serial.print(".txt");
Serial.print(13, BYTE);
logfilecount++;
//LCDSerial.print(0xFE, BYTE); //command flag
//LCDSerial.print(0x0C, BYTE); //turn on visual display
return;
}
void warn_flash(){
Serial.print(0x7C, BYTE);
Serial.print(128, BYTE); //backlight off
delay(300);
Serial.print(0x7C, BYTE);
Serial.print(157, BYTE); //backlight on
delay(300);
}
void meter_splash(char line1[], char line2[]){
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(0x01, BYTE); //clear
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(128, BYTE); //select line 1
LCDSerial.print(line1);
LCDSerial.print(0xFE, BYTE);
LCDSerial.print(192, BYTE); //select line 2
LCDSerial.print(line2);
delay(1000); //wait
LCDSerial.print(0xFE, BYTE); //clear
LCDSerial.print(0x01, BYTE);
}
www.randomcrap.us/otherjustin/meter_independent_lines.pde
The start of a version of the interface with independent lines. As in, you can change what one line displays independent of the others. I stopped coding on this because I didn't like how much you had to work to get the display to look exactly like you want while trying to drive a car at the same time (dangerous). But, if you want to work on this version of the code feel free.
www.randomcrap.us/otherjustin/serial_lcd_spash_set.pde
A sketch that can be used to set the splash screen on the lcd. Change the text on the appropriate line. Flash to the board...let run for a sec and you're set.
ERRORS THAT NEED FIXING: Spacing is fixed and so is the boost formula.
Component list:
Parts were sourced from Sparkfun, Digikey, egauges, radioshack (use radioshack only for convenience as parts are cheaper at digikey, newark, sparkfun, etc.), and directly from some manufacturers as samples.
-Arduino (Arduino - HomePage)-I'm using the standard USB version as it allows me to easily log all sensor parameters to a laptop.
-Sparkfun serial lcd (SparkFun Electronics)
-accelerometer (using a sparkfun breakout board...code was written for this one: SparkFun Electronics but will probably switch to this one: SparkFun Electronics)
-Oil temperature sender is a ISSPRO sender (part R8958). They were nice enough to send me their RT curve for this specific sender. All I had to do was call them. In addition I was surprised to get RT curve points from autometer, cyberdyne, and dakota digital. I ended up using the ISSPRO sender because it was the only one that fit my galley plug without an adapter.
-Temperature sensors: 252FG1K from ussensors (U.S. Sensor Thermistor, Manufacturer of NTC thermistors and RTD and thermistor temperature sensors), but you could use whatever thermistor you wanted. The only work involved would be mapping out the RT curve. I plan to insulate these with fiberglass sleeves much like the insulation on an EGT probe.
-Turbo pressure sensor: MPX4250GP from freescale (freescale is very nice in that they provide you with free samples of some of their products. Very good for a prototype. Please don't abuse their sample system.) You will also need tubing, a pilled t-fitting, and a some zip ties if you don't already have them.
-A lot of 18 and 22 gauge wire. I believe that the oil temp sensor requires 18 or 16 gauge wire (I can't recall off the top of my head).
-A piezo or a warning led...I haven't installed this yet but it should be as easy as wiring up an LED to blink. Any one that'll drive off of the 5V the arduino supplies will work.
-A relay and supporting circuitry for the boost referenced IC spray is on its way (watch this space!)
-buttons-any normally open (NO) SPST momentary button will work. I used what they had at radioshack and i'm not entirely pleased with how they held up. For now they work, but I'd suggest getting something better. I may, later switch to conductive touch sensor button sort of thing or "harvest" buttons from an older telephone.
-A 12V voltage regulator-this was an important missing component for a while. After some hard driving the original prototype would stop working and refuse to boot. For the longest time I thought it was a heat issue. Turns out it was a voltage issue and the car was actually providing 14.5V or more at times. This caused the arduino to start flipping bits on the microcontroller. The voltage regulator prevents this.
-An inline fuse was used for safely powering the arduino off the car's battery (I can't recall the specifics off the top of my head)
-A spare USB cable...to leave in the car
-2.5mm power plug to easily put the arduino in and out of the car.
-I used the standard clock location and some creative dremel tool work + a non-flammable glue to hold the display. You may wish to get an enclosure for this. I recommend hunting around for free samples (I personally enjoy the ones I got from PacTec and OKW. Pictures of these are in the thread)
-male header pins-attach these to the signal wires coming off of your sensors and they make swapping components out easy.
-female header pins and small board can be used to make a simple power and ground extender (the arduino does not have enough power and ground pins for every sensor)
-470 Ohm resistors to be used with the above listed thermistors in a voltage divider configuration (Voltage divider - Wikipedia, the free encyclopedia)
-a 220 Ohm resistor to be used in a voltage divider configuration with the oil temperature sender (I'm 90% positive it is a 220 Ohm but will double check).
-a 10k Ohm resistor to fix a bug with the arduino not sending serial signals to the lcd when not USB powered. (This plugs from gnd to RX).
Circuit diagram (using eaglecad http://www.cadsoftusa.com/):
Useful charts and stuff:
RT curve straight from ISSPRO
Graph showing use of multiple fit lines for complex thermistor RT curve
BTW I'm currently looking for a job and would love something related to this project.
A word on licensing, money, etc:
-I'm not a vendor on this site and the more and more I look at it I may never become a vendor on this site. This means, you cannot purchase things or services from me via this website and I cannot advertise things or services on this website.
-Their will be an open source license for this software. It will probably be a GPL license or some derivative. I have used adaptations of example code provided in the arduino forum and general knowledge base to produce this code. The code does not contain any proprietary information. To the best of my knowledge the RT curves for the theremistors are not proprietary knowledge and were given to me freely by the manufacturers.
-To the best of my knowledge I have not infringed on any copyrights, patents, etc. If you believe their is a problem please contact me so we can rectify the situation. However, I believe that what I have produced is a joining of several "public knowledge" ideas into one device. These ideas include the use of sensors with a digital display, logging data to a computer via USB, putting multiple sensors on one device, flashing warning text, etc.
Pictars and more demos:
and a better demo: TinyPic - Share The Experience!™
[The Following is the original first post]
I've finally decided on the electronics project I'm going to start. Just wondering if anyone else would be interested/should I look into getting a vendor account.
I'll be building a digital accelerometer display to go in the place of the clock. When you turn on the car it'll show a custom welcome message and then it'll have two lines of display. The top will be a meter/analog style output and the bottom will have an instant and a max g force reading.
When all is said and done it'll probably be fairly easy to change the custom welcome message via usb and it'll be able to draw power from 12V (with a fuse of course). It should also be backlit and come in several colors.
I'll be using open source software/hardware (arduino, etc.) so if I do become a vendor you'll only be paying for assembly, support, and shipping.
All comments welcome.
(basically I watered down what I wanted to make from this:
track data logger - IWSTI.com: Subaru WRX STI Forums)
Sort of like this idea from a while back (Clock replacement data display / IC Spray controller - ScoobyMods)
I think I'm going to go with this LCD: SparkFun Electronics or maybe this one SparkFun Electronics