Never Stop Building - Crafting Wood with Japanese Techniques
Crafting Wood with Japanese Techniques
car-dash-title.png

Arduino Controlled Car Dashboard

Author’s Note! This project is defunct, the truck mentioned blew a timing belt and I got a different truck, ultimatly changing the name of the Mopar Mansion to the Ford Flophouse. Since I had a newer truck I no longer needed the dashboard, this information is preserved for the reference of others.

In preparation for a road trip I've been building a tiny house (aka "The Mopar Mansion") to put into my old '77 Dodge Truck. One thing that needed to be addressed before heading out on 5000 plus miles of road is having some way to know when or if the truck is broken. One time, because the oil pressure sensor was busted, I failed to realize that I had run out of oil until I heard terrible clicking. The speedometer is unreliable, there is no tachometer, I'm going to have a transmission cooler, so I'd want a temperature sensor on that. I researched gauges, and along with the cost of wiring all the senders, to get individual gauges, would cost several hundred dollars. Not to mention I'd lack any programmatic control to enhance the functionality.

Design Spec

Enter Arduino, by coupling several readily available parts and sensors, I'll be able to construct a more flexible information display with better control at a lower cost. Below are the initial design criteria:

Required Design Criteria

  • Voltage Gauge
  • Vacuum Gauge
  • Oil Pressure Gauge
  • Coolant Temperature Gauge
  • Transmission Temperature Gauge
  • Fuel Level Gauge
  • Tachometer
  • Speedometer / Odometer

Nice to Have Criteria

  • External Temperature
  • Data Logging of Parameters
  • Entry of Fuel Fill for Millage Calculation
  • Trip Odometer
  • Warning Functionality
  • Display Off

Implementation Summaries

The general planned implementation is to couple an Arduino Uno to a Data Logging and Clock Shield, a 20x4 LCD Display, and a Matrix Keypad for input. Much of the required criteria only need the display and sensors, however I'll be building with an eye toward the requirements of the keypad and logger boards. The planned implementation for each of the design criteria are as follows:

  • Voltage Gauge - Use a voltage divider circuit to map the 11-14 Volts from the truck to a 0-5 Volt range that could be measured by the Arduino analog input.
  • Vacuum Gauge - Attach the manifold vacuum line to this vacuum sensor and reading it through the Arduino.
  • Oil Pressure Gauge - Using the existing oil pressure sender, which I'll be replacing. Assuming it varies some resistance value, it should just be a matter of measuring the voltage tapped out of a divider circuit, using the sender as one resistor, and another value for the second.
  • Coolant Temperature Gauge - There is a pre-existing sender for this as well, so I'd use the same strategy as the Oil Pressure Gauge.
  • Transimission Temperature Gauge - I'm going to use the same sender as for the coolant temperature, but integrated into a transmission cooling unit, where the t-block is grounded the frame. Should require the same divider circuit.
  • Fuel Level Gauge - Again, same as the other pre-existing senders, a divider circuit through the sender to ground.
  • Tachometer - At first I considered taping the ignition somewhere, but didn't want to possibly fry my Arduino, and also I want real crank rotation, not ignition pulse. The current plan is to use one of these Hall effect sensors on either the crank pulley or the alternator pulley with a magnet. The benefit of using the alternator pulley is that if the RPM goes to zero, I get a free indication that the alternator/fan belt has broken.
  • Speedometer - The current transmission cable method is pretty clunky, and GPS was both costly and might stop in tunnels or bad weather. For this I'll probably use another Hall effect sensor on either the drive shaft or one of the front wheels somewhere.
  • External Temperature - Just a nice to have, this would use one of the many Arduino sensors available, I might re-purpose the temp/humidy sensor from the dormant Climate Controller Project.
  • Data Logging - Mostly handled via software and the SD Card Shield I mentioned above. I'd like to regularly log all the gauge parameters with time and climate conditions. This would be even better with geo location, but that might be a far in the future project.
  • Fuel Fill Millage Calculation - The real purpose of the data logging shield is to provide a way to track millage. By providing a facility on the user interface to record a fuel fill up, I could calculate in software the millage since the last fill up and display a recent and average miles per gallon. My thought is that when I fill up (all the way), I could select a "Record Fill Up" option and be prompted to enter the gallons and cost with the keypad.
  • Trip Odometer - Would be handled by software, this is pretty low priority, I have never really cared about this.
  • Warning Functionality - Either specified through the interface, or hard coded into the software, when some parameter above falls outside of a range, the screen could blink or a warning light would flash. I think I'd like to have a bright, "shit just got bad" light on the dash.
  • Display Off - When driving at night, I'd like to have all the lights in the car off. It should be a mater of wiring a switch to one of the digital pins and only displaying output when the switch is on.

Arduino Code

Below is a direct copy of the arduino code from my files as of the last time this project was active. It may not be complete, accurate or running, however if you wish to do something similar, it might be a good reference.

#include <Keypad.h>
#include <Wire.h>
#include <LiquidTWI.h>

LiquidTWI lcd(0);

#define BACKLIGHT_RED 3
#define BACKLIGHT_GREEN 5
#define BACKLIGHT_BLUE 6

#define MLTIPLX_S0 2
#define MLTIPLX_S1 4
#define MLTIPLX_S2 7

#define ANALOG_SENSOR_INPUT 3

//const byte ROWS = 4; //four rows
//const byte COLS = 3; //three columns
//char keys[ROWS][COLS] = {
//  {'1', '2', '3'},
//  {'4', '5', '6'},
//  {'7', '8', '9'},
//  {'*', '0', '#'}
//};
//byte rowPins[ROWS] = {8, 9, 10, 11}; //connect to the row pinouts of the keypad
//byte colPins[COLS] = {2, 4, 7}; //connect to the column pinouts of the keypad

//Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

// Declare multiplexer input variables;
int mltiplxInputCount = 0;
int mltiplxS0 = 0;
int mltiplxS1 = 0;
int mltiplxS2 = 0;
int rawAnalogValue;

// Declare display values
int waterTemp;
int transTemp;
int vacuumPressure;

byte oil[8] = {
  B00100,
  B00100,
  B01110,
  B01110,
  B11111,
  B11111,
  B01110,
  B00000
};

byte temp[8] = {
  B00110,
  B00100,
  B00110,
  B00100,
  B00110,
  B00100,
  B01110,
  B01110
};

byte volt[8] = {
  B00010,
  B00110,
  B01100,
  B11110,
  B01111,
  B00110,
  B01100,
  B01000
};

byte one_fifth[8] = {
  0b10000,
  0b10000,
  0b10000,
  0b10000,
  0b10000,
  0b10000,
  0b10000,
  0b10000
};

byte two_fifth[8] = {
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000
};

byte three_fifth[8] = {
  0b11100,
  0b11100,
  0b11100,
  0b11100,
  0b11100,
  0b11100,
  0b11100,
  0b11100
};

byte four_fifth[8] = {
  0b11110,
  0b11110,
  0b11110,
  0b11110,
  0b11110,
  0b11110,
  0b11110,
  0b11110
};

byte five_fifth[8] = {
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111
};
void initializeLCD() {
  lcd.begin(20, 4);
  lcd.createChar(0, oil);
  lcd.createChar(1, temp);
  lcd.createChar(2, volt);
  lcd.createChar(3, one_fifth);
  lcd.createChar(4, two_fifth);
  lcd.createChar(5, three_fifth);
  lcd.createChar(6, four_fifth);
  lcd.createChar(7, five_fifth);
}

void displayDashStaticText() {
  // First Line
  lcd.setCursor(0, 0);
  lcd.print("****          ***mph");

  // Second Line
  lcd.setCursor(0, 1);
  lcd.print("E         F  ****mpg");

  //Third Line
  lcd.setCursor(0, 2);
  lcd.print(" **psi  *** F  ****V");
  lcd.setCursor(0, 2);
  lcd.write(byte(0));
  lcd.setCursor(7, 2);
  lcd.write(byte(1));
  lcd.setCursor(11, 2);
  lcd.print((char)223);
  lcd.setCursor(14, 2);
  lcd.write(byte(2));

  //Fourth Line
  lcd.setCursor(0, 3);
  lcd.print("TF *** F  Vac **inHg");
  lcd.setCursor(2, 3);
  lcd.write(byte(1));
  lcd.setCursor(6, 3);
  lcd.print((char)223);
}

void setup() {
  initializeLCD();
  displayDashStaticText();

  //Set Pin Modes
  pinMode(BACKLIGHT_RED, OUTPUT);
  pinMode(BACKLIGHT_GREEN, OUTPUT);
  pinMode(BACKLIGHT_BLUE, OUTPUT);
  pinMode(MLTIPLX_S0, OUTPUT);
  pinMode(MLTIPLX_S1, OUTPUT);
  pinMode(MLTIPLX_S2, OUTPUT);

  setBacklightStandard();

  Serial.begin(9600);
}

void loop() {

  for (int mltiplxInputCount = 0; mltiplxInputCount <= 7; mltiplxInputCount++) {
    mltiplxS0 = bitRead(mltiplxInputCount, 0);
    mltiplxS1 = bitRead(mltiplxInputCount, 1);
    mltiplxS2 = bitRead(mltiplxInputCount, 2);

    digitalWrite(MLTIPLX_S0, mltiplxS0);
    digitalWrite(MLTIPLX_S1, mltiplxS1);
    digitalWrite(MLTIPLX_S2, mltiplxS2);

    rawAnalogValue = analogRead(ANALOG_SENSOR_INPUT);

    switch (mltiplxInputCount) {
      case 1: //trans temp sensor
        transTemp = map(rawAnalogValue, 0, 1023, 207, -1);
        setTransTempTo(transTemp);
        break;
      case 2: //coolant temp sensor
        waterTemp = map(rawAnalogValue, 0, 1023, 207, -1);
        setWaterTempTo(waterTemp);
        break;
      case 5: //vacuum sensor
        vacuumPressure = map(rawAnalogValue, 0, 940, 15, 0);
        setVacTo(vacuumPressure);
        break;
    }
  }


  //  int test_value = analogRead(2);
  //  int rpm = map(test_value, 0, 1023, 0, 6000);
  //  setTachTo(rpm);
  //
  //  int mph = map(test_value, 0, 1023, 0, 120);
  //  setSpeedoTo(mph);
  //
  //  int fuel = map(test_value, 0, 1023, 0, 100);
  //  setFuelTo(fuel);
  //
  //  float mpg = map(test_value, 0, 1023, 0, 200) / 10.0;
  //  setMilageTo(mpg);
  //
  //  int psi = map(test_value, 0, 1023, 0, 99);
  //  setOilPressureTo(psi);
  //
  //  int waterTemp = map(test_value, 0, 1023, 0, 240);
  //  setWaterTempTo(waterTemp);
  //
  //  float volts = map(test_value, 0, 1023, 0, 180) / 10.0;
  //  setVoltageTo(volts);
  //
  //  int transTemp = map(test_value, 0, 1023, 0, 300);
  //  setTransTempTo(transTemp);
  //
  //  int vac = map(test_value, 0, 1023, 0, 16);
  //  setVacTo(vac);



}

void setBacklight(uint8_t r, uint8_t g, uint8_t b) {
  // common anode so invert!
  r = map(r, 0, 255, 255, 0);
  g = map(g, 0, 255, 255, 0);
  b = map(b, 0, 255, 255, 0);
  analogWrite(BACKLIGHT_RED, r);
  analogWrite(BACKLIGHT_GREEN, g);
  analogWrite(BACKLIGHT_BLUE, b);
}

void setBacklightStandard() {
  setBacklight(255, 255, 30);
}

void printPaddedValue(int value, int startCol, int startRow, int maxWidth) {
  lcd.setCursor(startCol, startRow);
  switch (maxWidth) {
    case 2:
      char buffer2[2];
      sprintf(buffer2, "%2d", value);
      lcd.print(buffer2);
      break;
    case 3:
      char buffer3[3];
      sprintf(buffer3, "%3d", value);
      lcd.print(buffer3);
      break;
    case 4:
      char buffer4[4];
      sprintf(buffer4, "%3d", value);
      lcd.print(buffer4);
      break;
  }
}

void printFractionalValue(float value, int startCol, int startRow) {
  char buffer[4];
  dtostrf(value, 4, 1, buffer);
  lcd.setCursor(startCol, startRow);
  lcd.print(buffer);
}


void setGaugeValue(int percent, int startCol, int startRow, int width) {
  lcd.setCursor(startCol, startRow);

  int totalSegments = width * 5;
  int usedSegments = totalSegments * (percent / 100.0);
  int blankCharactars = width;
  while (usedSegments > 0) {
    if (usedSegments > 4) {
      lcd.write(byte(7));
      usedSegments -= 5;
      blankCharactars -= 1;
    } else {
      switch (usedSegments) {
        case 0:
          break;
        case 1:
          lcd.write(byte(3));
          break;
        case 2:
          lcd.write(byte(4));
          break;
        case 3:
          lcd.write(byte(5));
          break;
        case 4:
          lcd.write(byte(6));
          break;

      }
      usedSegments = 0;
      blankCharactars -= 1;
    }
  }

  for (int i = 0; i < blankCharactars; i++) {
    lcd.print(" ");
  }


}

void extract(int value) {
  lcd.setCursor(0, 0);
  char buffer[4];
  sprintf(buffer, "%4d", value);
  Serial.print(buffer);
  Serial.print("\n");
  lcd.print(buffer);
}

void setTachTo(int rpm) {
  float rpmPercent = (rpm / 6000.0) * 100;
  setGaugeValue(rpmPercent, 4, 0, 9);
  printPaddedValue(rpm, 0, 0, 4);
}

void setSpeedoTo(int mph) {
  printPaddedValue(mph, 14, 0, 3);
}

void setMilageTo(float mpg) {
  printFractionalValue(mpg, 13, 1);
}

void setFuelTo(int percent) {
  float fuelPercent = percent / 100.0;
  setGaugeValue(percent, 2, 1, 7);
}

void setOilPressureTo(int psi) {
  printPaddedValue(psi, 1, 2, 2);
}

void setWaterTempTo(int temp) {
  printPaddedValue(temp, 8, 2, 3);
}

void setVoltageTo(float volts) {
  printFractionalValue(volts, 15, 2);
}

void setTransTempTo(int temp) {
  printPaddedValue(temp, 3, 3, 3);
}

void setVacTo(int vac) {
  printPaddedValue(vac, 14, 3, 2);
}

//void someKeypadShit(){
// put your main code here, to run repeatedly:

//  char key = keypad.getKey();
//
//  if (key != NO_KEY) {
//    Serial.println(key);
//
//    value = key - '0';
//    Serial.println(double(key));
//
//    test = ((double(key) - 48) * 10) / 100.0;
//    //    Serial.println(test);
//
//  }
//}