Controlling OEM gauges with an Arduino

This forum is for the discussion of other projects on Megasquirt/Microsquirt hardware that don't fit into any of the other forums

Moderators: jsmcortina, muythaibxr

Post Reply
winstonusmc
Experienced MS/Extra'er
Posts: 204
Joined: Sun Jul 31, 2011 7:45 am

Controlling OEM gauges with an Arduino

Post by winstonusmc »

Been working on some arduino integration into my Z for a little while. I have come with the idea of using an Arduino micro controller to give me a level of calibration to the gauges and give the ability to have warning lights that more modern cars have. I also want to broadcast the values on a sort of CANBUS to interact with other systems.


I purchased a oil and temp gauge on eBay to mess with. I was going to try to repurpose it as a boost and afr gauge, but the bi-metal strips are not near fast enough to keep up with a boost or afr signal.


starting out I have coded warning lights on both the oil and temp gauge. I havent designed a circuit yet for either sensors though. Still just playing around with it for now.

Image


I have ordered a few Arduino Pro Minis to mount on custom printed boards that will run the gauges. Here is my code, feel free to let me know what I scewed up on or could improve. I am very, very new to programming.

Code: Select all

const int tempPin    = A0;
const int batteryPin = A1;
const int oilPin     = A2;  
const int tempOutput = 9;
const int oilOutput  = 10;
const int tempLed    = 12;
const int oilLed     = 13;


int oilPressure = 0;
int coolantTemp = 0;
float sourceVoltage = 0.00; 

void setup() {
  Serial.begin(9600);
  pinMode(tempLed, OUTPUT);
  pinMode(oilLed, OUTPUT);
}

void loop() {
  batteryVoltage();
  tempGauge();
  //oilGauge();
  
  // print the results to the serial monitor:
  Serial.print(" ");
  Serial.print(sourceVoltage);
  Serial.print("V");
  Serial.print(" ");
  Serial.print(coolantTemp);
  Serial.print("*F");
  Serial.print(" ");
  Serial.print(oilPressure);
  Serial.println("psi");
  
  delay(100);
}

void batteryVoltage() {
    sourceVoltage = analogRead(batteryPin) * 0.0198817038;
}

void tempGauge() { 
  
  // gauge variables
  const int sensorCalMin = 0; //minimum sensor value 
  const int sensorCalMax = 1023; //maximum sensor value 
  const int valueMin = 15; //gauge value at 120*
  const int valueMax = 88; //gauge value at 250*
  const int warning = 220; // warning level value
  
  int tempSensor = 0;
  int outputValue = 0;
 
  tempSensor = analogRead(tempPin); // read the analog in value:
  coolantTemp = map(tempSensor, sensorCalMin, sensorCalMax, 0.00, 300.00); //calibration of temp sensor
  outputValue = map(coolantTemp, 120, 250, valueMin, valueMax); //calibration of temp gauge
  outputValue = constrain(outputValue, 0, 100); //Gauge limits
  
  analogWrite(tempOutput, outputValue);
  if (coolantTemp >= (warning)) // Low warning light
  {
    digitalWrite(tempLed, HIGH);
  }
  if (coolantTemp <= (warning - 5)) //Hysteresis for light off
  {
    digitalWrite(tempLed, LOW);
  }
  //Serial for DeBug
  //Serial.print(" Temp Sensor Value = ");
  //Serial.print(tempSensor);
  //Serial.print(" output = ");
  //Serial.print(outputValue);
}

void oilGauge() { 
  
  // gauge variables
  const int sensorCalMin = 0; //minimum sensor value 
  const int sensorCalMax = 1023; //maximum sensor value
  const int valueMin = 41; //gauge value at 0psi
  const int valueMax = 115; //gauge value at 90psi
  const int warning = 15; // warning level value
  
  int oilSensor = 0;
  int outputValue = 0;
 
  // read the analog in value:
  oilSensor = analogRead(oilPin);
  // map it to the range of the analog out:
  oilPressure = map(oilSensor, sensorCalMin, sensorCalMax, 0, 90);
  outputValue = map(oilPressure, 0, 90, valueMin, valueMin);
  outputValue = constrain(outputValue, 0, 120); //Gauge limits
  analogWrite(oilOutput, outputValue);
  if (oilPressure <= (warning)) // Low warning light
  {
    digitalWrite(oilLed, HIGH);
  }
  if (oilPressure >= (warning + 3)) //Hysteresis for light off
  {
    digitalWrite(oilLed, LOW);
  }
}
Nissan Skyline R34 RB26DETT ran MS3/MS3X w/ factory Hitachi CAS (sold)
Nissan Silvia S14 RB25DE ITB/NA ran MS3/MS3X w/ factory Mitsubishi CAS (disassembled)
Datsun 240z RB25DE ITB/NA with MS3/MS3X
Six_Shooter
Super MS/Extra'er
Posts: 1420
Joined: Mon Oct 17, 2011 7:28 pm
Location: South Western Ontario

Re: Controlling OEM gauges with an Arduino

Post by Six_Shooter »

That's interesting..

I too have an S30, so you've definitely piqued my interest here.

I have planned to replace the OEM mechanisms with stepper motors, so that I could set up a boost gauge in the stock housing. When I did some testing with that a while ago I couldn't get the steeper motor movement quick enough without then trying to rip the gears off the mechanisms.

I don't see any details on the hardware set-up (other than the pins for in and out in the code), are you just driving the gauges directly from the Arduino? You may want to look into using some sort of buffer between, such as a BJT amplifier, or OP-AMP.
Tha Toy: 1973 Datsun 240Z Turbocharged, and loads of fun, now MS'd
Tha Otha Toy: 1923 T-bucket Hot Rod, Currently Sniper'd
Tha Daily: 2005 Chevy Blazer
Tha Summer Daily: 1987 Buick Skyhawk hatchback
Tha Long Term Project: 1985 GMC S-10 Jimmy, hasn't been fun for a while
winstonusmc
Experienced MS/Extra'er
Posts: 204
Joined: Sun Jul 31, 2011 7:45 am

Re: Controlling OEM gauges with an Arduino

Post by winstonusmc »

I am running the gauge from a NPN transistor that came from the kit I bought. Those gauges won't run off of 5 volts. I put together a circuit before hooking it up to the Arduino with a potentiometer to kinda get an idea of what it would take. I also want to drive the temp gauge and even the tach later with the CanBus. I already switched the tach out with a more modern stepper type anyway.

As for switching out the guts of the gauge, the ammeter type gauges are pretty quick. Going to look into them for fast actuation. I know companies like DefI use pretty fast motors in their gauges.
Nissan Skyline R34 RB26DETT ran MS3/MS3X w/ factory Hitachi CAS (sold)
Nissan Silvia S14 RB25DE ITB/NA ran MS3/MS3X w/ factory Mitsubishi CAS (disassembled)
Datsun 240z RB25DE ITB/NA with MS3/MS3X
old guy
Experienced MS/Extra'er
Posts: 362
Joined: Thu Jan 24, 2008 9:20 am
Location: North Carolina

Re: Controlling OEM gauges with an Arduino

Post by old guy »

You might want to check out this page.
https://www.tindie.com/products/TheReng ... rod_search
You can also just buy the pcb.
Six_Shooter
Super MS/Extra'er
Posts: 1420
Joined: Mon Oct 17, 2011 7:28 pm
Location: South Western Ontario

Re: Controlling OEM gauges with an Arduino

Post by Six_Shooter »

winstonusmc wrote:I am running the gauge from a NPN transistor that came from the kit I bought. Those gauges won't run off of 5 volts. I put together a circuit before hooking it up to the Arduino with a potentiometer to kinda get an idea of what it would take. I also want to drive the temp gauge and even the tach later with the CanBus. I already switched the tach out with a more modern stepper type anyway.

As for switching out the guts of the gauge, the ammeter type gauges are pretty quick. Going to look into them for fast actuation. I know companies like DefI use pretty fast motors in their gauges.

I see the transistor now. I couldn't spot it when I first looked at the picture.

I ran an aftermarket tach for a long time because the factory tach would just fail to read above about 4500 RPM or so. I discovered that if the duty cycle went above about 37% that's when the problem would show up. I made a monostable 555 timer circuit that would output a pulse short enough to work well beyond the top of the tach range.
Tha Toy: 1973 Datsun 240Z Turbocharged, and loads of fun, now MS'd
Tha Otha Toy: 1923 T-bucket Hot Rod, Currently Sniper'd
Tha Daily: 2005 Chevy Blazer
Tha Summer Daily: 1987 Buick Skyhawk hatchback
Tha Long Term Project: 1985 GMC S-10 Jimmy, hasn't been fun for a while
Six_Shooter
Super MS/Extra'er
Posts: 1420
Joined: Mon Oct 17, 2011 7:28 pm
Location: South Western Ontario

Re: Controlling OEM gauges with an Arduino

Post by Six_Shooter »

old guy wrote:You might want to check out this page.
https://www.tindie.com/products/TheReng ... rod_search
You can also just buy the pcb.
I used the code and some of the circuit layout for my early stepper motor testing. I need to revisit this sometime soon.
Tha Toy: 1973 Datsun 240Z Turbocharged, and loads of fun, now MS'd
Tha Otha Toy: 1923 T-bucket Hot Rod, Currently Sniper'd
Tha Daily: 2005 Chevy Blazer
Tha Summer Daily: 1987 Buick Skyhawk hatchback
Tha Long Term Project: 1985 GMC S-10 Jimmy, hasn't been fun for a while
masterx81
Master MS/Extra'er
Posts: 776
Joined: Mon Oct 25, 2004 7:36 am
Location: Asti - Italy

Re: Controlling OEM gauges with an Arduino

Post by masterx81 »

I used jean/jbperf iox for driving gauges, using a pwm dac and a 2order low pass filter. Really accurate and easy to setup. You define the curve of the gauge and via the table you "linearize" it.
Enrico
Opel/Vauxhall Corsa GSi MS2
Subaru v4 EJ20 MS3
winstonusmc
Experienced MS/Extra'er
Posts: 204
Joined: Sun Jul 31, 2011 7:45 am

Re: Controlling OEM gauges with an Arduino

Post by winstonusmc »

More progress on the gauge setup. I have connected an Arduino to the CANBUS on my Megasquirt 3 to display things that the ECU knows. The whole idea is to run any gauges off of CAN that the ECU provides, like coolant temp, AFR, boost, even oil pressure. This way I simplify wiring and remove extra sensors on the engine.


I have ordered an OLED display to insert into the gauge body and may upgrade to a high def color later. I have been working on coding to display the info from the CanBus on the Megasquirt this weekend and have setup a simple LCD displaying MAP and AFR to demonstrate the concept.


https://www.youtube.com/watch?v=ih-Y0xu8UPo


Also the code is hosted on Github:

https://github.com/winstonusmc/arduino/ ... _map_gauge

Code: Select all

#include <SPI.h>
#include "mcp_can.h"
#include <LiquidCrystal.h>

LiquidCrystal lcd(9, 8, 7, 6, 5, 4);


//raw readings
int seconds = 0;
int pw1 = 0;
int pw2 = 0;
int rpm = 0;

int adv = 0;
float afrtgt1 = 0.0;

float baro = 0;
float MAP = 0;
float mat = 0;
float clt = 0;

int tps = 0;
float bat = 1;
float afr1 = 1;
int cel_status = 0;

unsigned long previousMillis = 0;


unsigned char Flag_Recv = 0;
unsigned char len = 0;
unsigned char buf[8];
INT32U canId = 0x000;
char str[20];


MCP_CAN CAN(10);                                            // Set CS to pin 10

void setup()
{
    Serial.begin(115200);
    
    lcd.begin(16, 2);
    lcd.setCursor(0,0);
    lcd.print("MAP ");
    lcd.setCursor(6,0);
    lcd.print("AFR");
    CAN.begin(CAN_500KBPS);                       // init can bus : baudrate = 500k
    CAN.init_Mask(0,0,0x7ff);                      
    CAN.init_Mask(1,0,0x7ff);                     
    //CAN.init_Filt(0,0,0x5f0);                      
    //CAN.init_Filt(1,0,0x5f2);                      
    //CAN.init_Filt(2,0,0x5f3);                      
    //CAN.init_Filt(3,0,0x0);                      
    //CAN.init_Filt(4,0,0x0);                      
    //CAN.init_Filt(5,0,0x0);                     

}


void loop() {
  canRead();
  //rpmSmoothing();
  canDisplay();
}   

void canRead() {
  if(CAN_MSGAVAIL == CAN.checkReceive()) {         // check if data coming
    CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
    switch (CAN.getCanId()) {
    case 0x5f0:
      seconds = word(buf[0] , buf[1]);
      pw1 = word(buf[2] , buf[3]);
      pw2 = word(buf[4] , buf[5]);
      rpm = word(buf[6] , buf[7]);
      break; 
    
    case 0x5f1:  // Group 1 
      adv = word(buf[0] , buf[1]);
      afrtgt1 = buf[4];
      //mat = word(buf[4] , buf[5]);
      //clt = word(buf[6] , buf[7]);
      break;
      
    case 0x5f2:  // Group 2 
      baro = word(buf[0] , buf[1]);
      MAP = word(buf[2] , buf[3]);
      mat = word(buf[4] , buf[5]);
      clt = word(buf[6] , buf[7]);
      break;
    
    case 0x5f3:   // Group 3 
      tps = ((buf[0] * 256) + buf[1]);
      bat = ((buf[2] * 256) + buf[3]);
      afr1 = ((buf[4] * 256) + buf[5]);
      //afr2 = (((buf[6] *256) + buf[7]); 
      break;
    }    
  }        
}


void canDisplay() {

  const int interval = 50;
  unsigned long currentMillis = millis();
  //refresh rate
  if (currentMillis - previousMillis >= interval) { //if time elapsed is greater than the signal interval
    previousMillis = currentMillis; //then reset time
    
    // Display to LCD and Serial 
    lcd.setCursor (0,1);
    lcd.print((MAP/10), 1);

    lcd.setCursor (6,1);
    lcd.print(afr1/10, 1);
    lcd.setCursor (10,1);
    lcd.print("/1");

  

    Serial.print(" Snds ");
    Serial.print(seconds);
    Serial.print(" RPM ");
    Serial.print(rpm);
    Serial.print(" PW1 ");
    Serial.print(pw1/1000, 2);
    Serial.print(" ADV ");
    Serial.print(adv/10, 1);
    Serial.print(" AFRtgt ");
    Serial.print(afrtgt1/10, 1);
    Serial.print(" AFR ");
    Serial.print(afr1/10, 1);
    Serial.print(" MAP ");
    Serial.print(MAP/10, 1);
    Serial.print(" CLT ");
    Serial.print(clt/10, 1);
    Serial.print(" TPS ");
    Serial.print(tps/10);
    Serial.println(" ");
  
  }
}
Nissan Skyline R34 RB26DETT ran MS3/MS3X w/ factory Hitachi CAS (sold)
Nissan Silvia S14 RB25DE ITB/NA ran MS3/MS3X w/ factory Mitsubishi CAS (disassembled)
Datsun 240z RB25DE ITB/NA with MS3/MS3X
Post Reply