Getting RPM by measuring non-symetric signal.

I am working on a Motorcycle speed sensor. The cycle provides a signal to the speedometer, from sensor on hub. The signal comes from a reedswitch sensing magnets on a rotating disc. The signal is two short duration negative pulses, followed by a long pause. The duration of each of the items becomes shorter as the speed increases. I am connecting to the Arduino with a pull up resistor, and the reedswitch is pulling to ground as magnet passes by. The first short duration pulse is less than the second duration pulse and on the order of 1 to tens of milli-seconds. For best accuracy I’d like to measure the pause between the pulses, and then I can convert that to MPH. I am not sure if I should use attachinterrupt() or some other method to measure this. I also am not sure how to ignore everything but the long time between pulses. Any suggestions. Attached is an image of the signal captured with an oscilloscope…

NOTE: This is part of a much larger project, so need it to be very efficient as far as time usage is concerned.

Any ideas would be appreciated. I thought perhaps could attach two interrupts to a specific pin. e.g. attachinterrut(0,RisePulse,HIGH) and attachinterrupt(0,LowPulse,LOW). Then with the RisePulse start a timer, and measure it with the LowPulse. If the measurement is > last measurement I know I"m measuring the space between, and keep that value. Otherwise I just discard the value. ANY HELP/IDEAS?

The signal comes from a reedswitch sensing magnets on a rotating disc.

Why don't you use a hall-effect sensor like everybody else does?

Since I already have an accurate source of data from the wheels rotation, I want to not add any additional hardware or modify the motorcycle any more than I have to. Thanks for asking!

I tried the following code, and got the following response. Can any one tell me if my understanding of the attachInterrupt(0,xxx,RISING) call is what I need. And it seems that in the function xxx, when i tried to disable interrupts to finish some calculations, the program stopped running. (note after the calculation I ussue the Interrupts() command to re-enable them. What am I doing wrong here?

Help is appreciated…

Code follows…

  RPM counter for Non-Symetrical Signal:
  Turns on an LED on for one second, then off for one second, to indicate code is running
  Want to Capture the pulses shown below, and measure the time/duration of the pulses.
  Due to the non symettrical nature of the pulses, a simple time measurement won't work.
  I'm thinkig if I triger on the Low to High pulses with an interrupt, I would then be
  getting two pulses/revolution.  A short pulse, followed by a long pulse.  I could then
  compare the prev pulse against current pulse, and if it is shorter throw the data out, but
  if it is longer save the data to be averaged.
  Then periodically stop the interrupt, allowning time to calculate an average for the
  captured pulses, and calculate an "RPM" value on that averaged value.  After the calculation
  allow the interrupts to continue, and start all over...
  Any ideas on this?  Does the code below look feasible.  I am new to Arduino, and esp the
  use of the interrupts.  Open to suggestions.
                                                           ltetzner   3/6/2014
  Signal as seen on an Oscilloscope                                                     
[tt] +5V_______   _____      _______________________   _____      _______________________
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
gnd        ----    -------                      ----    ------- 
              ^           ^                         ^         ^
Low-> Hi      |           |                         |         |  [/tt]
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;
int led2 = 12;
  static long counter;    // Holds # of times interrupt was called

// the setup routine runs once when you press reset:  Target board is an Arduino UNO
[b]void setup()[/b] {           

// initialize the digital pin as an output.
  pinMode(led, OUTPUT);       //flash every second or so
  pinMode(led2,OUTPUT);      // Flash when input changes
  pinMode(0,INPUT);          //  Input is pin 2, which used interrupt 0.
  pinMode(0,HIGH);           // Enable the internal pull-up resistor
  Serial.print("Arduino Tachometer .. 3/6/2014\n\n");
  attachInterrupt(0,rpmfan,RISING);  //trigger low to high change

// the loop routine runs over and over again forever:
[b]void loop() [/b]{
    // Main loop just blinks led 13 to indicate code is running, real work done in interrupt.

/*  Called by attachInterrupt(0,rpmfan,RISING);

[b]void rpmfan() {[/b]
  long nww;            // Time of this interrupt call
  int k;
  static int j=0;        // Pointer into array holding all Pulse Widths
  static long oldnw;      // store last time interrupt was called
  static long PulseWidth[100];  // array to hold pulse widths
  counter++;    //global val counting # times interrupt called

  nww = millis();
  //each 100 time pulse detected, blink another LIED
  if ( counter == 50 || counter == 100) {    
  } else {
  if (counter >= 100) { counter=0;}    // Reset counter 
  PulseWidth[j]= nww - oldnw;          // Save width of pulse was called

if (j == 100) {
  //noInterrupts();      // disable all interrupts
  /// do other stuff
  Serial.print("Filled up 100 items in the array\n");
    // print out all the items in the array

 /* for (k=1;k<=100;k++) {
    if (k % 20== 0) {

 // interrupts();      // enable interrupts

  oldnw = nww;

[b]void Blink()[/b]

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
  unsigned long currentMillis = millis();
 const int ledPin = 13;      // Blink LED pin
 static int ledState=LOW;
 static long previousMillis=0;
 static int count=0;
  if(currentMillis - previousMillis > 1000) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

    // if the LED is off turn it on and vice-versa:

      ledState = !ledState;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
    if (ledState) { 
     Serial.print("\t ON   "); 
     if (count==5){ 
     } else {
    }  //endif ledState
  }  //endif CurrentMillis
}   //end sub Blink

This is what I was seeing on the Serial Monitor
Arduino Tachometer … 3/6/2014

1001 ON 3003 ON 5005 ON 7007 ON 9009 ON 11011 ON
13014 ON 15016 ON 17018 ON 19020 ON Filled up 100 items in the array
21022 ON
23024 ON Filled up 100 items in the array
25026 ON 27028 ON 29030 ON Filled up 100 items in the array
31032 ON
33034 ON 35036 ON Filled up 100 items in the array
37038 ON 39040 ON Filled up 100 items in the array
41042 ON
43044 ON 45046 ON Filled up 100 items in the array
47048 ON 49050 ON 51052 ON
53055 ON 55057 ON 57059 ON 59061 ON 61063 ON
63065 ON 65067 ON 67069 ON 69071 ON 71073 ON
73075 ON 75077 ON 77079 ON 79081 ON 81083 ON
83085 ON 85087 ON 87089 ON 89091 ON 91094 ON

Interrupts should always be as short and fast as possible. Never print from an interrupt! For this application, I doubt you need interrupts. I would just sample the input in the main loop, put a time stamp on each input transition using millis() or microsec() and calculate rotational velocity from there.

First off you need to place code between code tags.
From what I think you describe and show would it be simpler to just measure the time between alternate rising or falling edges? A flipflop bit/byte that gets it's state flipped every call to the interrupt but the interrupt code only reads the time on one particular flipflop state.
Sudo interrupt code...

volatile byte flipflop = true;
volatile unsigned long lastTime;
volatile unsigned long timeDifference;

void int0_ISR(){
  flipflop = !flipflop; // Flip the state
  if (flipflop == true){
    timeDifference = millis() - lastTime;
    lastTime = millis();

The time between a1-a2 and b1-b2 will be the same, no need to ignore short duration.

  Signal as seen on an Oscilloscope                                                     
+5V _______   _____      _______________________   _____      _______________________
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
gnd        ----    -------                      ----    -------
              ^           ^                         ^         ^
Low-> Hi    a1|         b1|                       a2|       b2|

_Thank you for the feedback. I’m not sure what you meant by “place code between code tags”. I did have some code I commented out with /* & */ so it wouldn’t run when debugging. Was that what you were reffering to? _ Nevermind I just figured it out (was wondering how that was done when I initially posted this). Got it now!


  • Good idea on the flip flop idea, as deleting the need to filter out different times. As for you second point would I use attachinterrupt(0,int_ISR,RISING) to measure time between the points mentioned? instead of “timeDifference” I could put that into an long array?
  • It seems to me that this would randomly lock onto either the long time (A1-b1) OR longer time (b1-a2). Any ideas on how to only time the (b1 - a2) time. I think that would give me better resolution, especially at high rotational speeds (since the pules get closer together at the high speeds).
  • Also I am a little “fuzzy” on the need and use of the “volatile” item. Could you explain it?

You may be right on not needing interrupts for this. I am just using it as an excuse to “play-with/learn” how to use interrupts. Once I get this working, I may try a non-interrupt approach. The input from a sensor just seemed like a natural use for an interrupt driven routine. (but then again, I am new to this Ardunio stuff)!!! I was able to get the above code to work and display the “Filled up 100 items…” string. But when I tried to disable interrupts (in the interrupt routine) to give me time to print out the results, the code locks and no further output. (never gets to the interrupts() command to re start interrupts.). Anyway thank you for taking the time to respond.

Follow up:

I’ve made some progress, in that I’ve been able to read some pulses and made a first pass at correlating the pulse count to Miles-per-hour (I’d thought it would be linear, but it isn’t). I am now trying to break the relationship into a series of lines, to convert from rotations to MPH (each 10MPH has a different line… see attached graph…

Code follows…

  /*    Tachometer/Speedometer measurement fron non-symmetrical signal
  //Global constants and variables...
  int LED1=13;
  int LED2=12;
  volatile int ptr=1;
  volatile byte flipflop = true;
  volatile unsigned long lastTime;
  volatile unsigned long TimeDifference;
  volatile unsigned long Measurements[20];
  volatile unsigned long RevolutionCounter=0;
  void setup() 
    Serial.println("Tachometer Program:  3/6/14 8:08pm\n");
  }  //end setup
      Parameters for the line segments, where Y = mX + b is the equation for the line
        X	    Y       m	            b	        Line
      124971.       10	-0.000183273	32.90399536	Y =-0.000183273260431303* X + 32.9039953570774
      70408.5	    20	-0.000414139	49.15888431	and
      46262.0	    30	-0.001362175	93.01694546	Y actually is MPH
      38920.8	    40	-0.001132357	84.07225951	
      30089.7	    50	-0.004106664	173.5681432	
      27654.6	    60	-0.002184789	120.4194796	
      23077.5	    70	-0.003528167	151.4212631	
      20243.2	    80	-0.002419745	128.9833038	
      16110.5	    90	-0.00562799	180.6697308	
      14333.7	    100	 0.012752004	-82.78297304	
      15117.9	    110	-0.00571693	196.4277353	
      13368.7	    120	-0.005201711	189.5399376	
      11446.2	    130  0.011357459	0	
  void loop()  {
  float Y;      // -3.4028235E+38 < float range < 3.4028235E+38\
  float m;
  float X;
  float b;
  float MPH;
  char buffer[16];    //future usage
    if (ptr == 10) {      // This is set in the interrupt routine
      noInterrupts();    //stop interrupt during this calculation
       digitalWrite(LED1,HIGH);    // Show that this is running
           Serial.print("Average= ");
           X = (Measurements[1]+Measurements[2]+Measurements[3]+Measurements[4]+Measurements[5]+Measurements[6]+Measurements[7]+Measurements[8]+Measurements[9])/9;
           /* NOTE regression analysis gave the equition 3x10^6 * X^-1.075  Using this did NOT
           give good results for the speedometer.  Will try using multiple line segments between
           inter MPH's instead
           0-10, 10-20, 20-30, etc.... - 140
              Y=mX + b    or MPH = m * Counts + b
           if (X >= 124972 && X < 70409) {         // 10-20 MPH
             m = -0.000183273; 
              b= 32.90399536;
           } else if (X >= 70409 && X < 46262) {  // 20-30 MPH
              m= -0.000414139;
              b=  49.15888;
           } else if (X >= 46262 && X < 38921) {  // 30-40 MPH
              m= -0.001362175;
              b=  93.01695;
          } else if (X >= 38921 && X < 30090) {  // 40-50 MPH
              m= -0.001132357;
              b=  84.07226;
           } else if (X >= 30090 && X < 27655) {  // 50-60 MPH
              m = -0.004106664;
              b=  173.5681;
           } else if (X >= 27655 && X < 23078) {  // 60-70 MPH
              m = -0.002184789;
           } else if (X >= 23078 && X < 20243) {  // 70-80 MPH
              m = -0.003528167;
           } else if (X >= 20243 && X < 16111) {  // 80-90 MPH
              m= -0.002419745;
           } else if (X >= 16111 && X < 14334) {  // 90-100 MPH
              m= -0.00562799;
           } else if (X >= 14334 && X < 15118) {  //100-110 MPH
              m= 0.012752004;
           } else if (X >= 15118 && X < 13369) {  //110-120 MPH
              m = -0.00571693;
           } else if (X >= 13369 && X < 11446) {  //120-130 MPH
              m = -0.005201711;
           } else if (X >= 20243 && X < 16111) {  // 130+
              m= = 0.011357459 ;
           } //if (X
           MPH = (m * X) + b;     //Y = mX + b.... line segment for each 10 MPH range.
           dtostrf(MPH,3,1,buffer);      //Re-format to show MPH with a single digit
           Serial.print(" MPH \t based on count of [");
           Serial.print("]\t\t RawValue= ");
           Serial.print(" \t# Rev's = [");
           Serial.println("] Rev's");
             ptr=1;      // Reset pointer into results array, and refill
    } // endif ptr
  } //end loop
  /*      Take 10 Delta-T (time) readings, and store them in array Measurements[]
        Store the RevolutionCounter  (will be used later to calculate ODOMETER readings.
  void MeasRPM()  {
  unsigned long now;
      // otherwise do nothing till ptr is set back to 0
     flipflop = !flipflop; // Flip the state
    if (flipflop == true){
      TimeDifference = now - lastTime;
      RevolutionCounter++;      //Eventually use to determine mileage/odometer
      if (ptr <= 10) {
        Measurements[ptr] = TimeDifference;
      } //endif ptr
        lastTime = now; 
    }//endif FlipFlop
  }  //end sub

Regression Analysis.png


  • It seems to me that this would randomly lock onto either the long time (A1-b1) OR longer time (b1-a2). Any ideas on how to only time the (b1 - a2) time. I think that would give me better resolution, especially at high rotational speeds (since the pules get closer together at the high speeds).
    In your original post you said the signals came from magnets placed on a disc so I assumed both pulses come from 2 magnets. If it is that simple then you need to include the whole time between alternate pulses and where the ISR locks on does not matter. If this is some electronic system that puts out a short duration double pulse and the land between the second rising edge and the next drop (d-e) is the speed then what I suggested is no good as it is and will need re-thinking.
  • Also I am a little "fuzzy" on the need and use of the "volatile" item. Could you explain it?
    Volatile should be used on any variables that are accessed by the main program and the interrupt code.

A quick look at your latest code above and a couple of problems I can see.
Your not bounds checking ptr in the ISR so it could start writing to memory that does not belong to it. An array also starts at zero not one.
You need to disable interrupts in your main code to safely access interrupt variables (as you do) but then you go on to do calculations and printing before enabling them again. A better approach would be disable ints/copy vars/enable ints/calc on copies/print so the interrupts are disabled for the least time possible.

+5V _______   _____      _______________________   _____      _______________________
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
           |  |    |     |                      |  |    |     |
gnd        ----    -------                      ----    -------
           ^  ^    ^     ^                      ^  ^    ^     ^
           a  b    c     d                      e  f    g     h

Hi Riva,

Hi Riva, Excellent feedback I will look into this some more. I am not sure what causes non-symmetry of the signal. I am connecting to a both sides of a 'reed switch', that is sensing something inside a Motorcycle Speedometer. (originally this switch was intended for use in the cycle to turn off turn signals after a certain distance is moved. The cable comes off a mechanical gear on the hub of the front wheel.)

I am working on trying to get a better feel for this signal, by looking at it with an oscilloscope. One side of switch is at "ground", the other is pulled up with resistor to 5V, and connected to the Arduino input. The basic shape (small pulse - large pulse - long pause) stays the same, but as the speedometer increases the duration of each "event" gets shorter. (I'll post two Oscilloscope screen shots later for anyone who is interested).

I've been playing with using external interrupts, and just a very fast interrupt to track when transitions occur. By comparing the input signal (on O-scope) to an output pulse, to see if I'm triggering on every change etc. e.g. {  K = digitalRead(7);  digtalWrite(8,K);  } With not much going on in other loops I am able see a duplicate signal on pin 8 (output) as is coming in on pin 7 (input) with a slight delay of course. But when other things are occurring in code I'm not getting the same signal symmetry.

ASIDE: Thanks for the info on "Volatile" and the idea about when to disable interrupts etc. I will look into this next. This is to be just another part of a bigger project that is using interrupt 2 as a clock to do other things. I am now trying to add an Odometer/SpeedOmeter/Trip computer/On Time/Trip Time functions to the current project.
This is a lot to handle for a "newbie" but very intersting, and a lot of fun!
Thank you for taking the time to provide feedback!

A reed switch will close when the magnetic field on one end of the switch is strong enough to cause attraction of the reed to the other part of the switch. If you pass a magnet over the center of the reed switch (perpendicular to the reed switch center line) so that the magnetic fields are equal on both ends, the switch will not close. It could be that the reed switch you are using is oriented such that the magnet passes down the length of it causing it to close, open, then close again as the magnetic field changes in the reed switch. If you can it would be better to have the switch oriented so that the magnet passes over one end of it only perpendicular to the reed switch centerline.

The duration of the pulses should not concern you too much, what you need to measure is the elapsed time between pulses.

Since you appear to be getting two pulses per wheel revolution, you actually want to measure the time from the rising ( or falling ) edge of a pulse, not to the next pulse, but to the pulse after that.

If doesn’t matter if you are detecting the first pulse of each pair, or the second pulse of each pair, you just want to measure the time between every second pulse.

and made a first pass at correlating the pulse count to Miles-per-hour (I’d thought it would be linear, but it isn’t).

Your idea that it would be linear, is mis-conceived.

It IS linear, but you have your units upside down. The pulse counter correlates directly to hours per mile.

I second michinyon. If you measure the time between pulses (say between the leading edges), then miles-per-hour is directly proportional to 1/(time between pulses). The graph you show is the inverse of that relationship.