Calculating speed always returns 0

Hello

The following sketch is one I have borrowed from Robin2 on here, and modified slightly to give speed in MPH for a project.

The idea is to use the pulses on reDetectorISR and display number of revolutions in total, revDuration and MPH. It displays revDuration and revCount just fine, but MPH is always 0.

I cannot think why. I'm quite sure I am doing something very basic wrong. Here is the sketch:

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;
int SpeedMPH;

unsigned long prevDisplayMillis;

int displayInterval = 1000;
int secondsperhour = 3600;
unsigned long wheelcircumference = 0.001112257;

    // variables for the ISR

volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile bool newIsrMicros = false;



void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");

    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();

    }
}


//===========

void getIsrData() {
  if (newIsrMicros == true) {
    prevRevMicros = revMicros; // save the previous value
    noInterrupts();
    revMicros = isrMicros;
    revCount = isrCount;
    newIsrMicros = false;
    interrupts();
    revDuration = revMicros - prevRevMicros;
    SpeedMPH = revDuration * secondsperhour * wheelcircumference; //This is half the pulses from speed sensor in Hz (2 pulses per wheel rotation), multiplied by seconds in an hour, multiplied by wheel circumference in miles

}
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
    Serial.print(" Speed ");
    Serial.print(SpeedMPH);
    Serial.print(" revDuration ");
    Serial.print(revDuration);
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

If anyone could help me I would be most grateful.

Thanks!

Dave

unsigned long wheelcircumference = 0.001112257;

that is a integer type and therefore will be equal to ZERO.

try changing the type to 'float' and try again....

Hope that helps.

Arduino data types.

Yes, or for this application you could just:

    SpeedMPH = revDuration * 4;

...since secondsperhour * wheelcircumference == 4.0041252

sherzaad: unsigned long wheelcircumference = 0.001112257;

that is a integer type and therefore will be equal to ZERO.

try changing the type to 'float' and try again....

Hope that helps.

And that fixed it! Thank you very much! Now getting speed output. Just need to rethink the formula I used... Obvious now, but micros is a number, the Arduino does not care that it is microseconds. Doh! Speed is thousands of miles per hour at the moment. I wish the car was that fast! Hahaha!

aarg: Yes, or for this application you could just:

    SpeedMPH = revDuration * 4;

...since secondsperhour * wheelcircumference == 4.0041252

yes indeed I could. However, ultimately the sketch will allow for different wheel circumferences, and I want to keep all workings in the code so when I refer back I know where a value has come from. But thanks! :-)

EDIT

Though now the code hangs, a lot. Dang. It didn't hang at all before.

You can still use fixed point scaled arithmetic instead of floating point. It will be faster, and you can achieve similar precision.

Well that's interesting.

I've played around a bit with the maths to remove any floats, and speed either comes out as 4, or 4777494. Usually 4777494 unless the pulses are very slow.

Code:

unsigned long revMicros; unsigned long prevRevMicros; unsigned long revDuration; unsigned long revCount; unsigned long SpeedMPH;

unsigned long prevDisplayMillis;

int displayInterval = 1000; int secondsperhour = 3600; int wheelcircumference = 899; //This is 1/wheel circumference in miles unsigned long RPM; unsigned long revDurationSeconds;

// variables for the ISR volatile unsigned long isrMicros; volatile unsigned long isrCount; volatile unsigned long TimeOut; volatile bool newIsrMicros = false;

void setup() { Serial.begin(115200); Serial.println("SimpleISRdemo.ino");

isrCount = 0; attachInterrupt(0, revDetectorISR, RISING); }

//==========

void loop() { getIsrData(); if (millis() - prevDisplayMillis >= displayInterval) { prevDisplayMillis += displayInterval; showData();

} }

//===========

void getIsrData() { if (newIsrMicros == true) { prevRevMicros = revMicros; // save the previous value noInterrupts(); revMicros = isrMicros; revCount = isrCount; newIsrMicros = false; interrupts(); revDuration = revMicros - prevRevMicros; revDurationSeconds = revDuration / 1000000; RPM = 60 / revDurationSeconds; SpeedMPH = RPM * 60 / wheelcircumference; //revs per minute * 60 to get revs per hour } }

//===========

void showData() { Serial.println(); Serial.println("==============="); Serial.print(" Rev Count "); Serial.print(revCount); Serial.println(); Serial.print(" RPM "); Serial.print(RPM); Serial.print(" Speed "); Serial.print(SpeedMPH); Serial.print(" revDuration "); Serial.print(revDuration); Serial.print(" rev duration seconds"); Serial.print(revDurationSeconds);

}

//===========

void revDetectorISR() { isrMicros = micros(); isrCount ++; newIsrMicros = true;

}

Looking at the serial monitor, it becomes apparent where the problem is though.

Rev Count 4

RPM 60 Speed 4 revDuration 1976140 rev duration seconds1

Rev Count 5

RPM 12 Speed 0 revDuration 5361276 rev duration seconds5

Rev Count 7

RPM 4294967295 Speed 4777494 revDuration 507304 rev duration seconds0

Rev Count 8

RPM 4294967295 Speed 4777494 revDuration 552532 rev duration seconds0

So, rev duration in seconds is the problem, I'm guessing that this line:

    revDurationSeconds = revDuration / 1000000; 
[quote] 

is the problem because revDuration would have to be a float. It is quite tricky to get around using floats, it seems!! 

Back to the drawing board... How to convert to MPH without using any floats from the pulses per second... Any thoughts, folks?

revDuration is a huge number for any practical wheel RPM. On the other hand, MPH is a relatively small number. This is fortunate for you, because that means there is some single integer constant that will yield MPH when it divides revDuration. The trick is defining that constant. To do that, consider all your physical unit conversions... it will just be a series of multiplications and divisions. If you perform all the multiplications first, division last, you will achieve the least truncation and the greatest accuracy. In place of real numbers, you use rational numbers. Here I will demonstrate a scale by PI:

long result = 3141592L * inputNumber / 1000000;

See? If inputNumber == 1000, then result will end up == 3141

Hello

So. That worked. Kind of. I worked out that speed is 50000 / revDuration * 4.

However, to maintain a more accurate speed, I made the Arduino actually calculate (500000000 / revDuraction * 4) / 1000

The code still hangs now and then, and sometimes misses interrupts. I'm quite sure that I am doing something fundamentally simple wrong.

The setup is simple, a circular magnet from a speedometer with a hall effect sensor close to it. An LED on the sensor pulses with each polarity change of the magnet, so I know it is receiving pulses and sending this to the arduino. The magnet is connected to a drill with a flexible shaft. Spinning the drill spins the magnet. If I take the revDuration from the serial monitor, that corresponds with the speed I calculate.

So, this is the code:

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;
unsigned long SpeedMPH;
unsigned long prevDisplayMillis;

int displayInterval = 1000;



    // variables for the ISR
volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile bool newIsrMicros = false;



void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");

    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();

    }
}

//===========

void getIsrData() {
  if (newIsrMicros == true) {
    prevRevMicros = revMicros; // save the previous value
    noInterrupts();
    revMicros = isrMicros;
    revCount = isrCount;
    newIsrMicros = false;
    interrupts();
    revDuration = revMicros - prevRevMicros;
    SpeedMPH = (500000000 / revDuration * 4) / 1000; 

}
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
    Serial.print(" Speed ");
    Serial.print(SpeedMPH);
    Serial.println();
    Serial.print(" revDuration ");
    Serial.print(revDuration);

   
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

I then took the code, and added in the driver for the nixie tubes. This kind of works, but hangs really badly...

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long revDuration;
unsigned long revCount;
unsigned long SpeedMPH;
unsigned long prevDisplayMillis;

int displayInterval = 1000;
int NixieA;
int NixieB;




    // variables for the ISR
volatile unsigned long isrMicros;
volatile unsigned long isrCount;
volatile unsigned long timeoutMicros;
volatile bool newIsrMicros = false;

#define DIN_PIN   7          // Nixie driver (shift register) serial data input pin             
#define CLK_PIN   6          // Nixie driver clock input pin
#define EN_PIN    5          // Nixie driver enable input pin




void setup() {
    Serial.begin(115200);
    Serial.println("SimpleISRdemo.ino");

     
    pinMode(DIN_PIN, OUTPUT); 
    digitalWrite(DIN_PIN, LOW);    
    
    pinMode(CLK_PIN, OUTPUT);
    digitalWrite(CLK_PIN, LOW);         
  
    pinMode(EN_PIN, OUTPUT);
    digitalWrite(EN_PIN, LOW);


    isrCount = 0;
    attachInterrupt(0, revDetectorISR, RISING);
}

//==========

void loop() {
    getIsrData();
    if (millis() - prevDisplayMillis >= displayInterval) {
        prevDisplayMillis += displayInterval;
        showData();

    }
}

//===========

void getIsrData() {
  if (newIsrMicros == true) {
    prevRevMicros = revMicros; // save the previous value
    noInterrupts();
    revMicros = isrMicros;
    revCount = isrCount;
    newIsrMicros = false;
    interrupts();
    revDuration = revMicros - prevRevMicros;
    SpeedMPH = (500000000 / revDuration * 4) / 1000;



}
}

void NixieDisplay(byte digit1, byte digit2)
{
  StartShiftOutData();
  if (digit2 != 10) ShiftOutData(digit2);
  if (digit1 != 10) ShiftOutData(digit1);
  EndShiftOutData();
}

void StartShiftOutData()
{
  // Ground EN pin and hold low for as long as you are transmitting
  digitalWrite(EN_PIN, 0); 
  // Clear everything out just in case to
  // prepare shift register for bit shifting
  digitalWrite(DIN_PIN, 0);
  digitalWrite(CLK_PIN, 0);  
}

void ShiftOutData(byte digit)
{
  // Send data to the nixie drivers 
  for (int i = 15; i >= 0; i--)
  {
    // Set high only the bit that corresponds to the current nixie digit
    if(i == digit) digitalWrite(DIN_PIN, 1); 
    else digitalWrite(DIN_PIN, 0);
    
    // Register shifts bits on upstroke of CLK pin 
    digitalWrite(CLK_PIN, 1);
    // Set low the data pin after shift to prevent bleed through
    digitalWrite(CLK_PIN, 0);  
  }   
}

void EndShiftOutData()
{
  // Return the EN pin high to signal chip that it 
  // no longer needs to listen for data
  digitalWrite(EN_PIN, 1);
    
  // Stop shifting
  digitalWrite(CLK_PIN, 0);    
}

//===========

void showData() {
    Serial.println();
    Serial.println("===============");
    Serial.print("  Rev Count ");
    Serial.print(revCount);
    Serial.println();
    Serial.print(" Speed ");
    Serial.print(SpeedMPH);
    Serial.println();
    Serial.print(" revDuration ");
    Serial.print(revDuration);
    Serial.println();
    Serial.print(" Nixie Display ");
    Serial.print(NixieA); 
    Serial.print(NixieB);
    
    NixieB = (SpeedMPH % 10); // Assign second digit of the speed to NixieB
    NixieA = (SpeedMPH / 10) % 10; // Assign first digit of the speed to NixieA
    NixieDisplay(NixieB, NixieA); // Write these values to the tubes with the NixieDisplay function 



   
}

//===========

void revDetectorISR() {
    isrMicros = micros();
    isrCount ++;
    newIsrMicros = true;

}

I'm close, but I just can't figure out why the code hangs so terribly.

Can anyone help?

Edit I have a plan B now, using a frequency to analogue converter and simply read the input voltage, apply a value in MPH to a given voltabe, and output that. I've ordered three LM2907 chips to try out.

Thanks

You didn't grasp what I said about the scaled arithmetic. You have to perform ALL the multiplications FIRST!

    SpeedMPH = (500000000 / revDuration * 4) / 1000;

and you don't need to prescale by 1000 if you do this right...

    SpeedMPH = 50000 * 4 / revDuration;

If it doesn't work for you, you probably miscalculated the 50000 value. Offhand, it does appear small, if revDuration is microseconds per revolution. Can you show us the math that you used to come up with this value?

Also, what does this mean, "hangs really badly... "? Becomes non-responsive, or...?

Apologies, I just copied what I'd worked out and written down and assumed (wrongly I presume) that Arduino follows bidmas.

So. The maths...

Frequency = 1/revDuration

However, there are two edges per magnet rotation, so:

Frequency actually = 0.5/revDuration

Wheel circumference = 0.001112257 miles

SpeedMPH = MagnetPulseFrequency * 60 * 60 (to give pulses per hour) * Wheel circumference

So

MPH = 4 * Frequency

Happily, this makes a very convenient bit of maths to play with.

I will adjust, and try again.

By "hangs" it becomes unresponsive for random periods of time, and misses interrupts quite a bit.

So either a pulse to voltage converter, or... Perhaps if I wait for two interrupts before pausing interrupts to make the calculations and output the product?

Can we just back up a minute and derive the circumference properly, and do it in code to make it more practical? I’m sure your ruler is not marked in miles. What was the actual circumference and what units did you measure it in?

I’m following this, I was just starting out from scratch with some math to make it easy to scale. Thanks for getting back…

The rule of multiplication first then division has nothing to do with BIDMAS, except that / and * have equal precedence, which allows you to control the order easily since it’s evaluated left to right.

Ah sorry, ok.

Wheel circumference in miles is:

diameter of wheel in mm * Pi / 1609344

(1609344 is mm per mile).

Can't remember offhand what the wheel diameter was.

But I will use a potentiometer on an analogue pin to read a voltage to trim that value and calibrate it.

We are starting fresh here... what units would you use to measure your wheel, if you were to measure it now? Inches? If I calculate 0.001112257 * feet/mile * inches/foot, I get 70.5 inches. That is about a 22" diameter wheel. Does that seem realistic?

Oh, and... what accuracy do you require? Will you round or truncate to the nearest integer MPH? Or do you need fractional MPH?

I always work in mm. Unfortunately I can't get to my workshop any more to measure my wheel diameter again but 22" sounds righl, it's an R15 rim. If I close my eyes and think hard, the tyre might be a 125*80 profile, which would give a 100mm sidewall height, so all in would give around 580mm which is about 22.8"

Accuracy, it is for a two digit nixie speedometer, so an integer is all it can use.

Ok, problem solved! Works perfectly without any hanging, simply changing the order in which the speed calculation was done fixed it, so now:

void getIsrData() {
  if (newIsrMicros == true) {
    prevRevMicros = revMicros; // save the previous value
    noInterrupts();
    revMicros = isrMicros;
    revCount = isrCount;
    newIsrMicros = false;
    interrupts();
    revDuration = revMicros - prevRevMicros;
    SpeedMPH = 500000 * 4 / revDuration;
    NixieB = (SpeedMPH % 10); // Assign second digit of the speed to NixieB
    NixieA = (SpeedMPH / 10) % 10; // Assign first digit of the speed to NixieA




}
}

Thank you for the help! I have another question, but will put that in a new thread.

:-)

EDIT

No, I don't have another question, I solved the problem. I'm a happy bunny today!

Nixie speedometer working attached to a drill

Here's a video of it working for anyone interested. Hoping to have the rest of the electronics assembled soon - everything now works! Need my PCB etching kit to arrive though so that I can develop the boards I need rather than it working on prototype boards.

Thanks for your answers. I do it for my projects. They make me successfully

fall-apart-dave: Ok, problem solved! Works perfectly without any hanging, simply changing the order in which the speed calculation was done fixed it, so now:

void getIsrData() {
  if (newIsrMicros == true) {
    prevRevMicros = revMicros; // save the previous value
    noInterrupts();
    revMicros = isrMicros;
    revCount = isrCount;
    newIsrMicros = false;
    interrupts();
    revDuration = revMicros - prevRevMicros;
    SpeedMPH = 500000 * 4 / revDuration;

Sorry Dave, tried piecing together the math you used - but couldn't quite get there. What is this 500000 number exactly?

My wheel circumference is slightly larger than yours, 24" or 0.001189996843 miles - so I used that information.... 0.001189996843 * 60(seconds) * 60(minutes) = 4.2839886348

So would my SpeedMPH look like: SpeedMPH = 500000 * 4.284 / revDuration;

Or do I need to figure out a different 500000 number?

Thanks so much!