speedometer program

Hello,
I am using an Arduino to drive a Subaru speedometer. I tried using the pulseIn() function to measure the time between bolts on a driveshaft, and the needle was bouncing around too much at higher speeds... so I rewrote the getSpeed() function to count the number of signals over a twentieth of a second (arbitrary decision there)... but now as soon as the setup function is done, the needle shoots past all the numbers. So I figure either I screwed something up in the programming or the wiring.

Code below; any input is appreciated.

Thanks!
Ted

const float ratio = 4.5; //ratio of input signals to output signals
const int VSS = 2; //pin receiving wheel speed signal
const int SpeedoPin = 8; //Output pin to speedo
unsigned long prevOutTime = 0; //last micros time signal was sent to speedometer
unsigned long prevInTime = 0; //last micros time checkSpeed function was run
unsigned long outputDelay = 0; //time between signals to speedometer, effectively the speed
unsigned long prevSerMonTime = 0; //last MILLIS() numbers were sent to serial
byte SpeedoPinState = LOW;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(VSS, INPUT);
pinMode(SpeedoPin, OUTPUT);
tone(SpeedoPin, 65, 2000); //speedo displays about 57mph for 2 seconds
delay(2000);
tone(SpeedoPin, 131, 2000); //speedo displays about 113 mph for 2 seconds
delay(2000);
}

void speedoOut() {
SpeedoPinState = !SpeedoPinState;
digitalWrite(SpeedoPin, SpeedoPinState);
prevOutTime = micros();
}

void getSpeed() {
long start = micros();
int signals = 0;
byte VSSstate = digitalRead(VSS);
while (micros() - start <= 50000) {
if (VSSstate != digitalRead(VSS)) {
signals++;
VSSstate = !VSSstate;
}
}
if (signals > 0) {
outputDelay = ((micros() - start) / signals) * ratio;
}
prevInTime = micros();
}

void speedToSerMon() {
Serial.println("OutputDelay:");
Serial.println(outputDelay);
prevSerMonTime = millis();
}

void loop() {
// put your main code here, to run repeatedly:
if (micros() - prevInTime >= 250000){
getSpeed();
}
if (micros() - prevOutTime >= outputDelay > 0){
speedoOut();
Serial.print(".");
}
if (millis() - prevSerMonTime >= 3e3){
speedToSerMon();
}
}

Hello, try using an interrupt

Please use code tags instead of the quotation tags for posting your code.

Your input routine (with the tight while loop) is reasonable, although there are some issues, but it is not the root cause of your issue.

I do not think that speedoOut() is applying the pulses you want. You should be able to use tone() to generate the output.

When I apply tone() to your input routine at 60 Hz I see counts of 6 in the 50 ms reading period. This 120 Hz is correct since your routine is reading both edges of the incoming pulse. You may have an issue at lower speeds, but work on that later.

Since tone() at 65 Hz gives 57 mph on the speedometer so the relation ship is tone freq*.877

I think that speedoOut() can be called along with getSpeed() on the 250 ms timer, and could look like this.

void speedoOut() {
  // SpeedoPinState = !SpeedoPinState;
  // digitalWrite(SpeedoPin, SpeedoPinState);
  // prevOutTime = micros();
  tone(SpeedoPin,(signals * 20 * .877));
  }

To do this, change signals to be a global variable. Then getSpeed() is modified to use the new global

void getSpeed() {
  long start = micros();
  // int signals = 0;
  signals = 0;
  byte VSSstate = digitalRead(VSS);