3

I recently started programming with Arduino and and currently I'm making a project to read RPM and Speed of my car using Arduino Uno. So far I got everything working but the speed value does not show properly. I have used one hardware counter and two interrupts in this project.

  1. Timer 1 on pin 5 - Count pulses from car's ECU to calculate speed.
  2. Int0 on pin 2 - Read ignition pulse to calculate RPM.
  3. int1 on pin 3 - 5v in from headlight switch to control the brightness of the display (MAX729 7 seg 8 digit).

I can get the proper reading from RPM and also the brightness control is working properly. Here is my codes below.

    #include <LedControl.h>

// inputs: DIN pin, CLK pin, LOAD pin. number of chips
LedControl display = LedControl(12, 10, 11, 1);

//SPEEDO-----------------------------------------------------------
const int hardwareCounterPin = 5;
const int samplePeriod = 1000; //in milliseconds
const float pulsesPerMile = 4000; // this is pulses per mile for Toyota. Other cars are different.
const float convertMph = pulsesPerMile/3600;
unsigned int count;
float mph;
int kph;
int roundedKph;
int previousKph;
int prevCount;
//----------------------------------------------------------------

//RPM-------------------------------------------------------------
//Configuration for the Tachometer variables
const int sensorPin = 2;
const int sensorInterrupt = 0;
const int timeoutValue = 5;
volatile unsigned long lastPulseTime;
volatile unsigned long interval = 0;
volatile int timeoutCounter;
int rpm;
int rpmlast = 3000;
int rpm_interval = 3000;
//---------------------------------------------------------------

//Brightness control---------------------------------------------
int brightnessIndicator = 13;  //Light pin 13 led to show that brightness control is in action.
int brightnessIn = 3;  //Sensor in from headlight positive wire to control the brightness.
//---------------------------------------------------------------

void setup(void) {
  //Serial.begin(9600);

  display.shutdown(0, false);  // turns on display
  display.setIntensity(0, 10); // 15 = brightest
  printTrueno(); //Print my car's name
  delay(3000);
  initToZero(); //Print all zeros before going to the loop

  TCCR1A = 0; //Configure hardware counter 
  TCNT1 = 0;  // Reset hardware counter to zero

  //Config for the Tach
  pinMode(sensorPin, INPUT);
  attachInterrupt(sensorInterrupt, &sensorIsr, RISING);
  lastPulseTime = 0;
  timeoutCounter = 0;

  //Config for brightness control
  pinMode(brightnessIndicator, OUTPUT);  //LED output
  pinMode(brightnessIn,INPUT);  //Sensor in
  attachInterrupt(1, changeBrightness, CHANGE);  //Attach the 2nd interrupt
  changeBrightness();  //Check for head lights on or not.
}

void loop() {
  //SPEEDO-----------------------------------------------------------
  // This uses the hardware pulse counter on the Arduino.
  // Currently it collects samples for one second.
  bitSet(TCCR1B, CS12); // start counting pulses
  bitSet(TCCR1B, CS11); // Clock on rising edge
  delay(samplePeriod); // Allow pulse counter to collect for samplePeriod
  TCCR1B = 0; // stop counting
  count = TCNT1; // Store the hardware counter in a variable
  TCNT1 = 0;     // Reset hardware counter to zero
  mph = (count/convertMph)*10; // Convert pulse count into mph. 10x allows retaining 10th of mph resolution.
  kph = ((unsigned int) mph)*1.6; // Convert to kph and cast to integer. 

  int x = kph / 10;
  int y = kph % 10;

  // Round to whole mile per hour
  if(y >= 5){
    roundedKph = x + 1;
  }
  else {
    roundedKph = x;
  }

  //If kph is less than 1 kph just show 0kph.
  //Readings of 0.9kph or lower are some what erratic and can
  //occasionally be triggered by electrical noise.
  //if(x == 0){
  if(x < 2){  // 1 was triggered by some noise signal.
    roundedKph = 0;
  }

  // Don't display kph readings that are more than 50 kph higher than the previous reading because it is probably a spurious reading.
  if(roundedKph < 181){
    if((roundedKph - previousKph) > 50) {
      Serial.println(previousKph);
      printNumber(previousKph, 0);
    }
    else {
      Serial.println(roundedKph);
      printNumber(roundedKph, 0);
    }
  }

  previousKph = roundedKph; // Set previousMph for use in next loop.
  //-----------------------------------------------------------------

  //RPM--------------------------------------------------------------
  if(rpm >= 0) {  //Remove the minus values   

    //Let's keep this RPMr value under control, between 0 and 9999
    rpm = constrain (rpm, 0, 9999);

    //If the engine is not running, print 0
    if ((micros() - lastPulseTime) < 5e6 ) {
      rpm = rpm;
    }
    else {
      rpm = 0;
    }

    if (rpm < 250)
      rpm = 0;
    //Serial.println(rpm);
    printNumber(rpm, 1);
  }
  //-----------------------------------------------------------------
}

//Each time the interrupt receives a rising tach signal, it'll run this subroutine
void sensorIsr() {
  unsigned long now = micros();
  interval = now - lastPulseTime;
  if (interval > 5000){
     rpm = 61000000UL/(interval * 2);
     lastPulseTime = now;
  }
}

void changeBrightness() {
  int val = digitalRead(brightnessIn);  //Read the sensor value

  if(val == 1){  //If the headlights are on, pin goes HIGH.
    digitalWrite(brightnessIndicator, HIGH);   //turn the LED on
    display.setIntensity(0, 6); //Reduce the brightness.
  }
  else {
    digitalWrite(brightnessIndicator, LOW);    //turn the LED off
    display.setIntensity(0, 14);  //increase the brightness.
  }
}

void printNumber(int v, int flag) {
    int ones;
    int tens;
    int hundreds;
    int thousands;

    if (flag==0){
      ones = v%10;
      v = v/10;
      tens = v%10;
      v= v/10;
      hundreds = v;

      //Now print the number digit by digit
      if (hundreds>0)
        display.setDigit(0,0,(byte)hundreds,false);
      else
        display.setChar(0,0,' ',false);

      display.setDigit(0,1,(byte)tens,false);       
      display.setDigit(0,2,(byte)ones,false);
    }
    else if (flag=1) {
      ones = v%10;
      v = v/10;
      tens = v%10;
      v = v/10;
      hundreds = v%10;
      v = v/10;
      thousands = v%10;

      //Now print the number digit by digit
      display.setDigit(0,4,(byte)thousands,false);
      display.setDigit(0,5,(byte)hundreds,false);
      display.setDigit(0,6,(byte)tens,false);
      display.setDigit(0,7,(byte)ones,false);
    }
}

I think the problem is between Timer1 and first interrupt. Can someone please help me to solve this?

Thank you very much.

7
  • 4
    "So far I got everything working but the speed value does not show properly" - Can you explain more: what are you seeing? what are you expecting to see? How are you showing the value. Don't make us work it out from the code!
    – Will
    Commented Sep 1, 2014 at 13:07
  • 1
    Have you tried to reduce the program to the smallest amount of code that either works or has the bug, and nothing extra. For example "the speed value does not show properly" is still in the code, but everything else removed. Please answer Will's question first.
    – gbulmer
    Commented Sep 1, 2014 at 13:24
  • Try swapping count = TCNT1; and TCCR1B = 0;. It could be that stopping the counter also resets TCNT1.
    – Gerben
    Commented Sep 1, 2014 at 18:33
  • @Will - Lets say my current speed is 25kmph. But I'm seeing incorrect random values like 31, 35, 45, 40, etc... Sometimes after every 5-6 seconds I get the correct reading 25kmph. But my RPM values are showing correctly 99% of the time. Commented Sep 1, 2014 at 22:27
  • @gbulmer When I run these two parts of codes separately (RPM and Speedo), they work without any problems and give me the correct values. But when I merge them together, the speedo value becomes incorrect. Commented Sep 1, 2014 at 22:30

2 Answers 2

2

I think you made a mistake in the program design. You have things backwards, and your tach interrupts are messing with program flow because they are happening too fast.

The tachometer runs 0~100 rev/sec. Send the pulses to the counter and, twice a second, multiply the count by 120 and display the RPM. You only care what the RPM is to the nearest hundred, anyway, right?

You get an ECU pulse every ~0.4m traveled. Record the interval (x ms) between pulses in your interrupt routine. 0.4m/x ms * 1 km/1000 m * 3,600,000 ms/hr = ~1448/x km/hr. Do the (expensive) division, using the last interval recorded, twice a second when you update the RPM.

Now your interrupt routine is getting called every 12ms, tops, instead of 8ms typical. And, your main loop is running at a steady 500 ms.

1
else if (flag=1) {

should be

else if (flag==1) {

What else would flag be anyway? Why not just use "else"?


 // Don't display kph readings that are more than 50 kph higher than the previous reading because it is probably a spurious reading.
  if(roundedKph < 181){
    if((roundedKph - previousKph) > 50) {
      Serial.println(previousKph);
      printNumber(previousKph, 0);
    }
    else {
      Serial.println(roundedKph);
      printNumber(roundedKph, 0);
    }
  }

  previousKph = roundedKph; // Set previousMph for use in next loop.

In the code above you admit roundedKph might be wrong, but use it anyway for next time around the loop. That means next time around you will discard a correct reading.


   //If the engine is not running, print 0
    if ((micros() - lastPulseTime) < 5e6 ) {
      rpm = rpm;
    }

What does that achieve?


int rpm;

That should be volatile.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.