Race car dashboard

Thank you!

I find it extremely handy when working with real world data.

I also have one that I"m betting will be very useful for this instrument panel project. It's a non-linear COLOR mapper. Works the same way but instead of (X,Y) points mapping to a float value. It's (X,Color) points mapping to a color. (Does blends between colors).

(maxSafeRPM,green);
(redline,red);

Everything less than maxSafeRPM will map to green. Everything at or above redline maps to red. Anything in between blends from green->red.

-jim lee

1 Like

Nice - I’m intrigued!

A post was split to a new topic: Generator auto start(2)

Hello
The project is progressing well ...... but now another small problem arises, perhaps two small problems.

The car has a ignition electronic and a cable comes out from the ignition to count the engine rpm. That cable is currently connected to an analog rpm tachometer and I would like to implement it in the nextion screen, but how can I correctly read those pulses? I don't have an oscilloscope but it seems that the pulses vary between 12v and 0v.

Any ideas or suggestions?
Thank you all

Hi there!

Just a general question: how fast will the pulses be?

Two ways of reading them spring to mind. Presumably you want to know the pulse rate, but you could also implement a pulse counter in a similar way.

They both involve connecting those pulses to a digital input on your Arduino, obviously via a suitable potential divider to bring them into the acceptable voltage range.

Both will need a way of calculating the time between the latest pulse and the previous one and for that you could make use of a function like micros() which returns the number of microseconds since the Arduino was switched on. (Resolution of 4 microseconds, rolls back to zero after something like 70 mins so this would need to be handled).

In the first method, in the program loop you read the state of the pulse input pin and check if is the same as the last time you checked it. If it is, you just ignore it. But if the state has changed since the last read then you do a couple of things. You need to store the new state (in a variable like lastPulseReadState or some like that). You need to run the micros() function to get the current time and store that (in a variable like currentReadTime). You then need to calculate the time since the last change (something like pulseInterval = currentReadTime - lastReadTime). Then you overwrite lastReadTime with the currentReadTime. At this stage everything is reset and all you’ve done is detected a pulse (half a pulse actually) and measured the time interval since the last one.

[This is the information you need and you can do what you will with in your program. You might decide you want to take the average of the last few time interval measurements to smooth out any resolution errors, for example.]

Once this loop is running it will just work*, but as you probably guessed you just need to set up your variables and initialise them before your program enters the loop.

*The caveat is this will be fine as long as your loop time is shorter than the interval between the pulses. There is a second option you could look into which involves using interrupts. Here, the microprocessor halts what it is doing more or less immediately when an interrupt is triggered, e.g. by a change of state on an input pin, and runs a user-defined function before returning back to the job it was previously doing before it was interrupted.

Personally I would check out the first option first as it’s actually simpler than it sounds, and just satisfy yourself that your pulses are going to be slow enough for this method. Someone else might point to a library or function I’m not aware of but, honestly, it’s quite a simple thing to do. I appreciate there’s quite a lot of info there so please ask questions if you need to :grin:

Hi,

Most electronic ignition systems that provide a tacho pulse, usually output a pulse for each plug ignition pulse.
So depending on the number of cylinders, 4 stroke I would assume, and the maximum RPM of the motor you could easily work out the frequency you would have to detect.

I don't know if the vehicle uses waste spark system or not, which will also influence the calculations.

Tom... :smiley: :+1: :coffee: :australia:

Hi Tom!

Excellent! I suppose the next question is, how long will those pulses be?

Of course, they need to be long enough to detect the state change by the loop method, or else the interrupt system might be required. Therefore it would be really good to get a measurement of the length somehow, or find a datasheet with that info. Or, of course, just try it and see if the results make sense!

[Even if it works I’d prefer to know the length though just be sure there is enough margin for the system to be robust.]

Richard

Just had a quick look at tacho pulses and they’re not likely to be nice square pulses, therefore I’d add an extra check in your program (loop or interrupt function) to ignore the state change from high to low.

So when the state changes from high to low, just update the lastPulseReadState variable but don’t do anything with the time calculation. Then, when the state changes from low to high, update lastPulseReadState, get the current time, calculate the interval, and then set lastReadTime = currentReadTime to finish as described before.

This way you’re only measuring full pulse cycles rather than the oddities of any asymmetrical pulse form and the whole thing will be much smoother.

Hi Tom and R11K

Thank you very much for taking your time to answer me.

The car in question is a Renault GT Turbo rally car (Spain Model-Fase II). 1.4ccm3(I don“t know how much it is in inches) and 4 cylinders. It reaches at maximum rpm about 6800rpm. The ignition is electronic with distributor output (I think it is not lost spark, but I don't understand much about mechanics. :thinking:)
The pulse is not clean or a perfect square.

I'm waiting for a friend to lend me an oscilloscope so I can measure it well.

I am trying to write a good code for the rpm and I will present it to you shortly so that you can give me your opinion.

Thank you very much

1 Like

Typically they will have an RPM output that is a square wave. 5 or 12V. ( Early GM dist. had an output curve that emulated the original analog ignition signal. ) Except its was messed up and scaled wrong. I sold a LOT of adapters to change that to a (fixed) 12V square wave back in the day.

BUT.. does this thing not have CANBUS? I've forgotten if this was asked before.

-jim lee

No can bus or OBD :weary: that“s why is so difficult

What are you using for ignition? Stock or aftermarket like MSD or something?

-jim lee

Hi Jim.

For that I use the original car ignition.

I have been testing my code and something is not working. Can you check it? Right now my head can't find what I'm doing wrong and I“m going frustrated.

#include <Wire.h>

#include <doxygen.h>
#include <NexButton.h>
#include <NexCheckbox.h>
#include <NexConfig.h>
#include <NexCrop.h>
#include <NexDualStateButton.h>
#include <NexGauge.h>
#include <NexGpio.h>
#include <NexHardware.h>
#include <NexHotspot.h>
#include <NexNumber.h>
#include <NexObject.h>
#include <NexPage.h>
#include <NexPicture.h>
#include <NexProgressBar.h>
#include <NexRadio.h>
#include <NexRtc.h>
#include <NexScrolltext.h>
#include <NexSlider.h>
#include <NexText.h>
#include <NexTimer.h>
#include <Nextion.h>
#include <NexTouch.h>
#include <NexUpload.h>
#include <NexVariable.h>
#include <NexWaveform.h>



#include <mapper.h>
#include <multimap.h>
                                      ///*****GPS*****/////
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#define rxPin 14
#define txPin 13
SoftwareSerial neogps(rxPin,txPin);
TinyGPSPlus gps;


#define  WATER_TEMP_PIN    A0    // signal from water temp sensor.
#define  OIL_PSI_PIN       A1    // signal from oil PSI sensor.
#define  OIL_TEMP_PIN      A2   // signal from oil temp sensor.
                                                                  ///SENSOR TURBO*******************///
#define  sensorTurbo_Pin   A3   // signal from Turbo sensor.
#define  refTurbo_Pin      A4   // signal from Turbo reference.
//#define WINDOW_SIZE         6


                                                                   ////VOLTAGE////

const int sensorPin = A5;
int sensorValue_VOLTS;
float valorVolts;


                                                                ///FUEL******//
int fuelPin = 6;
int lectura_fuel;
int porcentaje_fuel;
                                                              /// TURBO///

float presionTurboSoplado;
float presionTurboAtmosfera;
float bares;


uint32_t next, MyInt = 0;

                                  //************** LED ALARM************//
                                  
int LED1=13;   // ALARMA TEMP AGUA
int LED2=12;   // ALARMA BATERIA
int LED3=11;   // ALARMA TURBO
int LED4=10;   // ALARMA TEMP ACEITE
int LED5=9;   //ALARMA PRESION ACEITE
int LED6=8;   //ALARMA GASOLINA

int LED_RPM1=7;   //3.5M rpm
int LED_RPM2=6;   //4M rpm
int LED_RPM3=5;   //4.5M rpm
int LED_RPM4=4;   //5M rpm
int LED_RPM5=3;   //5.5M rpm
int LED_RPM6=2;   //6M rpm    

                                          //***************** NEXTION EDITOR **************//
NexNumber gn0 = NexNumber(1, 4, "gn0");
NexNumber vn1 = NexNumber(1, 5, "vn1");
NexNumber wn2 = NexNumber(1, 6, "wn2");
NexNumber fn3 = NexNumber(1, 8, "fn3");
NexNumber rn6 = NexNumber(1, 10, "rn6");
NexNumber rn8 = NexNumber(1, 12, "rn8");
NexNumber rn0 = NexNumber(2, 5, "rn0");
NexNumber an1 = NexNumber(2, 6, "an1");
NexNumber on2 = NexNumber(2, 9, "on2");
NexNumber mwt = NexNumber(3, 4, "mwt");
NexNumber mta = NexNumber(3, 5, "mta");

NexProgressBar j0 = NexProgressBar(2, 2, "j0");

NexText bt4 = NexText(1, 7, "bt4");
NexText rt5 = NexText(1, 9, "rt5");
NexText at7 = NexText(1, 11, "at7");
NexText bt1 = NexText(2, 7, "bt1");
NexText tt2 = NexText(2, 8, "tt2");
NexText ot3 = NexText(2, 10, "ot3");
NexText mpa = NexText(3, 6, "mpa");
NexText mpt = NexText(3, 7, "mpt");

                                         // ************* BUFFER SENSOR **************//
  
char bt [100]={0};
char rt [100]={0};
char at [100]={0};
char ma [100]={0};
char mt [100]={0};

long int Set_background_color_bco;  //cambiar color fondo numeros

                                                                     ///MAPEADO SENSOR********************////////
mapper   aToVMapper(0,1023,0,5); // Analog to volts.
multiMap vToWaterTempMapper;     // This will be volts to temp.
multiMap vToOilPSIMapper;        // This will be volts to Oil PSI.
multiMap vToOilTempMapper;        // This will be volts to Oil TEMP.


                                                                ////RPMspark////

  const int DATA_PIN = 36;
  const int LED_GROUND_PIN = 50;
  
  const int PINS_COUNT = 4;
  const int LED_PINS[PINS_COUNT] = {21,22,23,24};
  const int LED_SWITCH_RPM[PINS_COUNT] ={4000, 4500, 5000};
  const int REV_LIMITER_RPM = 5800;
  const int NUMBER_OF_CYLINDERS = 4;
  const int LED_UPDATE_INTERVAL = 200;

  unsigned long lastUpdateTime = 0;
  volatile int sparkFireCount = 0;
  int lastRpmValue = 0;
  bool revLimiterOn = false;

  void incrementRpmCount() {
    sparkFireCount++;
  }

  void setGlobalState(bool state){
    for (int i = 0; i < PINS_COUNT; i++){
      digitalWrite(LED_PINS[i], state);
    }
  }

  void setLedState(int rpm){
    setGlobalState(LOW);
    
    for (int i = 0; i< PINS_COUNT; i++){
    if (rpm > LED_SWITCH_RPM[i]){
      digitalWrite(LED_PINS[i], HIGH);
    }
  }

    if (rpm>REV_LIMITER_RPM){
      setGlobalState(LOW);
    }
    else{
      setGlobalState(HIGH);
    }
    revLimiterOn= !revLimiterOn;
    return;
  }

  



void setup() {
  Serial.begin(115200);
//   Serial3.begin;
//   Serial1.begin;
   nexSerial.begin(115200);
   neogps.begin(115200);
   
   pinMode(fuelPin, INPUT);

   pinMode(sensorValue_VOLTS, INPUT);
   pinMode(sensorTurbo_Pin, INPUT);
   pinMode(refTurbo_Pin, INPUT);


                                            //////RPMspark

   for (int i=0; i> PINS_COUNT; i++){
    pinMode(LED_PINS[i], OUTPUT);
   }
   pinMode(LED_GROUND_PIN, OUTPUT);
   digitalWrite(LED_GROUND_PIN, LOW);
   setGlobalState(HIGH);
   delay(500);
   setGlobalState(LOW);
   pinMode(DATA_PIN, INPUT_PULLUP);
   attachInterrupt(1, incrementRpmCount, FALLING);                                         

   pinMode (LED1, OUTPUT);
   pinMode (LED2, OUTPUT);
   pinMode (LED3, OUTPUT);
   pinMode (LED4, OUTPUT);
   pinMode (LED5, OUTPUT);
   pinMode (LED6, OUTPUT);

   pinMode (LED_RPM1, OUTPUT);
   pinMode (LED_RPM2, OUTPUT);
   pinMode (LED_RPM3, OUTPUT);
   pinMode (LED_RPM4, OUTPUT);
   pinMode (LED_RPM5, OUTPUT);
   pinMode (LED_RPM6, OUTPUT);
   
   
   vToWaterTempMapper.addPoint(2,0);
   //vToWaterTempMapper.addPoint(4.75,50);// Set datapoints TEMP WATER into the mapper.
   //vToWaterTempMapper.addPoint(3.0,20);
   //vToWaterTempMapper.addPoint(2.9,85);
   //vToWaterTempMapper.addPoint(2.03,90);
   //vToWaterTempMapper.addPoint(5.0,95);
   //vToWaterTempMapper.addPoint(3.0,110);
   vToWaterTempMapper.addPoint(3,120);

   vToOilPSIMapper.addPoint(2,0);       // Set datapoints PRES OIL into the mapper.
   //vToOilPSIMapper.addPoint(1.2,2.5);
   vToOilPSIMapper.addPoint(3,5);
   //vToOilPSIMapper.addPoint(1,7.5);
   vToOilPSIMapper.addPoint(4,10);

   vToOilTempMapper.addPoint(4,20);   // Set datapoints TEMP OIL into the mapper.
   //vToOilTempMapper.addPoint(5,60);
   vToOilTempMapper.addPoint(5.05,90);
   //vToOilTempMapper.addPoint(5.2,130);
   vToOilTempMapper.addPoint(5.25,140);
   vToOilTempMapper.addPoint(5.3,150);
   //vToOilTempMapper.addPoint(0.08,160);
   //vToOilTempMapper.addPoint(0.06,170);
   vToOilTempMapper.addPoint(5.40,190);



 

   }
const int FIRES_PER_REV=(360/(720 / NUMBER_OF_CYLINDERS));

                                                                              // This takes the reading from analogInput() and gives back... temp.
float getTemp(int aVal) {

   float volts;
   float tempWater;
   
   volts = aToVMapper.map(aVal);
   tempWater = vToWaterTempMapper.map(volts);
   return tempWater;
}


                                                                            // This takes the reading from analogInput() and gives back... OIl PSI.
float getOilPSI(int aVal) {

   float volts;
   float PSI;
   
   volts = aToVMapper.map(aVal);
   PSI = vToOilPSIMapper.map(volts);
   return PSI;
}

                                                                            // This takes the reading from analogInput() and gives back... temp.
float getTempOil(int aVal) {

   float volts;
   float tempOil;
   
   volts = aToVMapper.map(aVal);
   tempOil = vToOilTempMapper.map(volts);
   return tempOil;
}









void loop() {

   int   val;
   float tempWater;
   float oilPSI;
   float tempOil;
   
   val = analogRead(WATER_TEMP_PIN);                // read the input pin WATER
   tempWater = getTemp(val);
   
     Serial.print("sensorAGUA = ");
  Serial.println(tempWater);
   
   if (tempWater>100){
    digitalWrite (LED1, HIGH);
    wn2.Set_background_color_bco (63488);
    an1.Set_background_color_bco (63488);
    wn2.setValue (tempWater);
    an1.setValue (tempWater);
   }
   else{
    digitalWrite (LED1, LOW);
    wn2.Set_background_color_bco (48631);
    an1.Set_background_color_bco (48631);
    wn2.setValue (tempWater);
    an1.setValue (tempWater);
   }
   
   val = analogRead(OIL_PSI_PIN);                 // read the input pin OIL PRESS
   oilPSI = getOilPSI(val);
   
     Serial.print("sensorP OIL = ");
  Serial.println(oilPSI);

   if (oilPSI<3.3){
    digitalWrite (LED5, HIGH);
    at7.Set_background_color_bco (63488);
    ot3.Set_background_color_bco (63488);
    dtostrf(oilPSI,5,2,at);
    ot3.setText(at);
    at7.setText(at);
   }
   else{
    digitalWrite (LED5, LOW);
    at7.Set_background_color_bco (48631);
    ot3.Set_background_color_bco (48631);
    dtostrf(oilPSI,5,2,at);
    ot3.setText(at);
    at7.setText(at);
   }

   val = analogRead(OIL_TEMP_PIN);                // read the input pin OIL TEMP
   tempOil = getTempOil(val);

     Serial.print("sensorT OIL = ");
  Serial.println(tempOil);
  
   if (tempOil>155){
    digitalWrite (LED4, HIGH);
    rn6.Set_background_color_bco (63488);
    on2.Set_background_color_bco (63488);
    rn6.setValue(tempOil);
    on2.setValue(tempOil);
   }
   else{
    digitalWrite (LED4, LOW);
    rn6.Set_background_color_bco (48631);
    on2.Set_background_color_bco (48631);
    rn6.setValue(tempOil);
    on2.setValue(tempOil);
   }
                                            //*******FUEL****//
  lectura_fuel = analogRead(fuelPin);
  porcentaje_fuel = map(lectura_fuel,0, 830, 35, 0);
  fn3.setValue(porcentaje_fuel);

    Serial.print("FUEL = ");
  Serial.println(porcentaje_fuel);

    if (porcentaje_fuel<55) {
      digitalWrite (LED6, HIGH);
    }
    else{
      digitalWrite (LED6, LOW);
    }


                                                                          //////VOLTAGE

float voltaje;                                                                          
//sensorValue_VOLTS = analogRead(sensorPin);
  //vin = map(sensorValue_VOLTS, 0, 1023, 0, 26.2);
  sensorValue_VOLTS=analogRead(sensorPin);
  voltaje=sensorValue_VOLTS/4.043;
  valorVolts=(voltaje/10);

  
  Serial.print("VOLTAJE = ");
  Serial.println(voltaje);
  
     if (valorVolts<11,5){
    digitalWrite (LED2, HIGH);
    //bt4.Set_background_color_bco (63488);
    //bt1.Set_background_color_bco (63488);
    dtostrf(valorVolts,5,2,bt);
    bt4.setText(bt);
    bt1.setText(bt);
   }
   else{
    digitalWrite (LED2, LOW);
    //bt4.Set_background_color_bco (48631);
    //bt1.Set_background_color_bco (48631);
    dtostrf(valorVolts,5,2,bt);
    bt4.setText(bt);
    bt1.setText(bt);
   }
                                                    ///*****TURBO****////
  analogRead(sensorTurbo_Pin);
  presionTurboSoplado = map(analogRead(sensorTurbo_Pin),0,1023,0,4.92);
  //int boost1 = map(analogRead(sensorTurbo_Pin),21,961,100,2600);
  analogRead(refTurbo_Pin);
  
  presionTurboAtmosfera = map(analogRead(refTurbo_Pin),0,1023,0,4.92);
  bares = (presionTurboSoplado-presionTurboAtmosfera);

       if (bares>1.5){
    digitalWrite (LED2, HIGH);
    bt4.Set_background_color_bco(63488);
    bt1.Set_background_color_bco(63488);
    dtostrf(bares,5,2,rt);
    rt5.setText(rt);
    tt2.setText(rt);
   }
   else{
    digitalWrite (LED2, LOW);
    bt4.Set_background_color_bco(48631);
    bt1.Set_background_color_bco(48631);
    dtostrf(bares,5,2,rt);
    rt5.setText(rt);
    tt2.setText(rt);
   }
  dtostrf(bares,4,2,rt);
  rt5.setText(rt);
  tt2.setText(rt);

  

                                                          /////GPS Loop////
  boolean newData = false;
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (neogps.available())
    {
      if (gps.encode(neogps.read()))
      {
        newData = true;
      }
    }
  }

 				 //If newData is true
  if(newData == true)
  {
    newData = false;
    print_speed();
  }
 				//endif



 
}

void print_speed()
{
       
  if (gps.location.isValid() == 1)
  {
   //String gps_speed = String(gps.speed.kmph());
    gn0.setValue(gps.speed.kmph());
    vn1.setValue(gps.satellites.value());
    
  }
							/////RPM SPARK
  
if ((millis() - lastUpdateTime)>LED_UPDATE_INTERVAL){
    int currentRpm = (sparkFireCount*(1000/LED_UPDATE_INTERVAL)*60)/FIRES_PER_REV;
    int averagedRpm =(currentRpm+lastRpmValue)/2;

    setLedState(averagedRpm);
    Serial.println(averagedRpm);

    sparkFireCount = 0;
    lastUpdateTime = millis();
    lastRpmValue = currentRpm;
  

   int RpmNextion=map(averagedRpm,0,1023,0,100);                                         
   rn8.setValue(averagedRpm);
   rn0.setValue(averagedRpm);
   j0.setValue(RpmNextion);
   delay (1);           //delay for stability
}


}

the voltage gets with a 5v-25v arduino module

Thanks a lot for your time. Best Regards

1 Like

Sorry, don't check in all that often anymore. Just saw this tonight.

Whats all this nextXXX stuff? It looks like most of the code you are using is in there. So I can't see the lion's share of what you are doing. You have this code in a pile or folder somewhere so it can be viewed?

Actually here's some stuff that looked odd to me. I added comments..

const int PINS_COUNT = 4;
const int LED_PINS[PINS_COUNT] = {21, 22, 23, 24};
const int LED_SWITCH_RPM[PINS_COUNT] = {4000, 4500, 5000}; // << Where is the 4th value?

// OK this sets all the pins either HIGH or LOW.
void setGlobalState(bool state) {

  for (int i = 0; i < PINS_COUNT; i++) {
    digitalWrite(LED_PINS[i], state);
  }
}


void setLedState(int rpm) {

  setGlobalState(LOW);                       // Set all the pins to LOW. ok all off..

  for (int i = 0; i < PINS_COUNT; i++) {     // Loop around all the pins..
    if (rpm > LED_SWITCH_RPM[i]) {           // If the rpm is greater than WAIT last one is undefined.
      digitalWrite(LED_PINS[i], HIGH);       // The last pin is undefined.
    }
  }
  if (rpm > REV_LIMITER_RPM) {               // OK, now if rpm > REV_LIMITER_RPM..
    setGlobalState(LOW);                     // Turn all the pins LOW
  } else {                                   // Else it wasn't > REV_LIMITER_RPM..
    setGlobalState(HIGH);                    // Set all the pins HIGH. WHY DID WE BOTHER WITH THE LOOP?
  }
  revLimiterOn = !revLimiterOn;              // Toggle the rev limiter and I don't know why.
  return;                                    // Supwefolus return statement. Your going to return anyway.
}```

-jim lee

Thanks Jim for your reply.

I certainly have errors in the rpm code. I try to correct it and show a better code again.

NextXXX is the code associated with the nextion screen to display the information. tonight I make a clean code to show you.

Now another thing. I already managed to read the rpm signal of the car with an oscilloscope. How can I get the arduino to read this signal?
Processing: IMG_8668.MOV...
Thank you all

1 Like

corte RPM2.zip (2.0 MB)
Captura_01

1 Like

Is there no RPM signal available in the car already? Its not uncommon to have a "cleaned up" square wave for RPM somewhere coming out of the ignition system. If not, I'd start working on cleaning up the signal you have. Basically smooth it out then voltage limit it so you can hook an input pin to the resulting signal. Do a good job cleaning it up so you can use an interrupt for this.

As I said before. I'm not a wire guy so I'm not a lot of help with how to clean up a signal. Basically when you see the spike, I want (as the processor) to see a high. (JUST ONE LOW TO HIGH PER SPIKE) Then, at some time after, go to a low and hold there 'till the next spike. You can't have a lot of noise on the line because you really can't have this running a digital filter (debounce) with doing other things like running a display. You'll only get one shot so it needs to be a solid on or off.

Hope this helps.

-jim lee

Hi Jim.

The signal you see is the one the car delivers from its own rpm signal. That signal that I show you is obtained directly from the cable that the car has for the tachometer and comes directly from the original ignition coil of the car. Unfortunately, it is a very noisy signal.

The E part is the ignition coil
The A part is the conector for the tacho
The P part is the crank sensor
The crank sensor gives the correct ignition signal to the coil

I posted a question for you on the sensors & filtering page : Guy needs help filtering a tach signal for race car Go have a look and maybe someone can set you up with the proper circuit to filter this.

-jim lee