Is there a better way to capture vehicle speed sensor data?

I’m new to physical computing and am working my way up a very steep learning curve making lots of mistakes as I go. I’m hoping you can help me with a specific problem I’m struggling with…

I’ve fitted a digital vehicle speed sensor in-line with the original speedo cable on my 1967 Triumph Herald. The supplier of the sensor is unable to give me any information on the manufacturer or model number of the sensor so I have no datasheet to refer to but experimentation confirms that it returns 8 pulses per rotation of the speedo cable.

Using an Arduino Uno, I’ve created a sketch that increments a counter each time interrupt 0 (pin 2) triggers. The sketch reports the number of pulses each second to the serial line where they’re picked up by a Raspberry Pi for further processing.

To give myself something to calibrate against, I’ve also connected a blox NEO-6M-0-001 GPS unit to report speed in KMpH alongside pulses per second.

When I graph the pulses and the GPS speed detail on the same graph I’m seeing a lot of variance in the pulse figures so figure making it impossible to directly correlate pulses per second against speed (image attached).

I figure that there’s I’m collecting the pulses incorrectly and am struggling to understand where I’ve gone wrong. I’m hoping you might be able to help me understand where I’ve gone wrong.

// Global Setup

  // Load libraries

    #include <TinyGPS++.h>  // GPS Library
    #include <SoftwareSerial.h> // Allows connection to GPS hardware

  // Declare variables

    // Variables related to GPS Speed Checking
      static const int GPSRXPin = 4, GPSTXPin = 3;  // GPS Receive and Transmit Pins
      static const uint32_t GPSBaud = 9600; // Set baud for connection to GPS hardware
    // Variables related to digital sender speed checking
      const int hallSensorPin = 2; // Speedo check pin
      volatile byte pulsecount = 0; // variable to count pulses received from speedo sender and set to initial count of 0

  // Initalise Objects
    TinyGPSPlus gps; // The TinyGPS++ object
    SoftwareSerial ss(GPSRXPin, GPSTXPin); // The serial connection to the GPS device

void pulse_count()
   //This is called by Interrupt 0 (associated with digital pin 2) and should trigger 8 times per rotation
   pulsecount++; //Update counter each time interrupt occurs

void setup()

  Serial.begin(9600); //Commence serial comms session
  ss.begin(GPSBaud);  //Commence connection to external GPS Module
  // Set pins

  // Set Interupts
  attachInterrupt(0, pulse_count, FALLING);   //Interrupt 0 is digital pin 2, so that is where the IR detector is connected.

  // Send Data Header


void loop()

  // Send detail to serial
    // GPS
    if ( { 
      Serial.print(; Serial.print(F("/")); Serial.print(; Serial.print(F("/")); Serial.print(; }
      else { Serial.print(F("INVALID")); }
    if (gps.time.isValid()) { 
      if (gps.time.hour() < 10) Serial.print(F("0")); Serial.print(gps.time.hour()); Serial.print(F(":")); 
      if (gps.time.minute() < 10) Serial.print(F("0")); Serial.print(gps.time.minute());Serial.print(F(":"));
      if (gps.time.second() < 10) Serial.print(F("0")); Serial.print(gps.time.second()); Serial.print(F(".")); }
      else { Serial.print(F("INVALID")); }
    Serial.print(gps.speed.kmph()); Serial.print(",");

    // Digital Speed Sender
    Serial.print(pulsecount); Serial.println("|");

  pulsecount = 0; // Reset Counter
  smartDelay(1000); // Wait a second

  if (millis() > 5000 && gps.charsProcessed() < 10) { Serial.println(F("No GPS data received: check wiring")); }

// This custom version of delay() ensures that the gps object is being "fed".

static void smartDelay(unsigned long ms)
  unsigned long start = millis();
    while (ss.available())
  } while (millis() - start < ms);

Welcome to the Forum.

Under what conditions do you reset the counter?

You should have a millis interval, if statement, that stores the total after 1second or what ever time period.
That will give you pulses per second or what ever time period.

You cannot rely on the code execution time to give you constant time intervals.

Lookup in the IDE Examples Blink without delay.

Tom... :slight_smile:

I suggest that every 8 counts (assuming that is the number for one revolution) you record the value of micros() and figure out the time for the 8 counts by subtracting the previous time value - something like this

void void pulse_count() {
   if (pulseCount > 7) {
      revMicros = micros();
      pulseCount = 0;
      newRev = true;

and your code in the main program would be like

if (newRev == true) {
   prevRevMicros = latestRevMicros; // save the last value
      latestRevMicros = revMicros; // get the value produced by the ISR
      newRev = false;
   revTimeMicros = latestRevMicros - prevRevMicros;


You can make smartDelay() smarter to ensure that the whole loop is executed in exactly 1 second instead of 1 second plus the (variable) execution time.

But the idea to count pulses is better. I would count more than 8 pulses. Maybe wait for 100. Then the short-term variations will be averaged out.

I would count more than 8 pulses. Maybe wait for 100.

A multiple of 8, surely? (assuming there are 8 per revolution)

64 and 128 are easy numbers for the Arduino to work with.


Have you connected a scope to the speed sensor output to see exactly what type and strength of signal it is generating?

I agree with oldvetteguy, you should use a scope to see what you are getting. I had a similar project but I was using a PICAXE that had a function to read in pulses. I have to use a diode to make the sine wave square -ish.

Then I just drove the car around with cruise control on (or just trying to keep it steady myself) and noted the speed I was at and what was the frequency I measured. I then made an Excel sheet to give me a curve fitting formula and it worked good enough for my purposes.