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.

asked Sep 1, 2014 at 12:18
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! 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. Commented Sep 1, 2014 at 13:24
  • Try swapping count = TCNT1; and TCCR1B = 0;. It could be that stopping the counter also resets TCNT1. 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.

answered Aug 26, 2015 at 15:48
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.

answered Jun 26, 2015 at 5:43

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.