3

I'm trying to measure the amount of time that has passed between every time a reed switch mounted on a wheel is activated to calculate the speed of a bike in miles per hour and display it on two 7 segment LEDs. This is what I have so far (without 7 seg code, as that works):

const int reed = A3;
float circumference;
float pi = 3.141592653589
unsigned long time0 = 0;
unsigned long time1;
unsigned long revolutionTime;

void setup() {
  Serial.begin(9600);
}

void loop() {
  circumference = (pi * 26); //26 = the diameter of the wheel
  int reedState = digitalRead(reed);
  if (reedState == HIGH) {
    time1 = millis();
    revolutionTime = (time1 - time0);
    time0 = time1;
    int mph = ((circumference / revolutionTime) * (3600000 / 63360)); //this is the conversion from inches/millisecond to miles/hour.
    Serial.println(mph);
    //And I'd print the result on the 7 segment displays here
  }
}

This code results in around 25 printed entries in the serial monitor from a single activation of the reed switch, all of which consist of randomly alternating values of either 0 or 4571.

Following each step of the code, I assume that the program would generate a garbage result for the first entry (because the time before the reed is activated for the first time could vary greatly), but that following entries would be the correct mph displayed on the 7 segment displays until the reed switch is activated again and a new mph is calculated.

So how would I go about modifying the code to get useful data?

  • 1
    Where is the debouncing? – Ignacio Vazquez-Abrams Jun 12 '15 at 3:27
  • @IgnacioVazquez-Abrams If I include a debouncing portion, would the Arduino still register every reed switch activation at high wheel speeds? – CodeRead Jun 12 '15 at 3:35
  • That depends. Do you expect the wheels to spin in excess of 10kRPM? – Ignacio Vazquez-Abrams Jun 12 '15 at 3:41
  • As an aside you may find the shock of cycling too great for the reed switch and might want to use a hall sensor instead. – Ignacio Vazquez-Abrams Jun 12 '15 at 3:43
  • I like to think I'm fast, but I'm no where near that fast. I'll look into adding in that debounce. And what do you mean when you say "the shock of cycling?" – CodeRead Jun 12 '15 at 3:45
3

Currently your code is not working, because Arduino is fast enough to check the closed switch state many times before it opens again, so before calculating a new speed value, you need to see LOW on the input.

Still, there are at least 2 better ways to do what you want.

pulseIn() - a function intended just for what you are trying to do: measuring lenght of a pulses. If your switch is active HIGH, you need to measure length of a LOW pulse:

unsigned long duration;
duration = pulseIn(reed, LOW);
// calculate speed basing on duration (in microseconds)

Keep in mind, that pulseIn() is a blocking function, so no code will be executed, until it receives the pulse. You can counter it bu specifying a timeout, after which, the program will continue.

External interrupts are an another approach. Interrupt handling routines are executed when a specified event occurs, no matter if the microcontroller is busy doing something else or not. They would guarantee, that you will not miss any pulse even if they would have frequency in order of kHz.

volatile unsigned long lastTime = 0;

void setup() {
    attachInterrupt(0, readTime, RISING);
} 

void readTime() {
    unsigned long t = millis();
    // calculate speed basing on t - lastTime
    lastTime = t;
}

Interrupt handling routine should be as short as possible. Avoid using delays in it, doing Serial.print and other communications. Also, millis() and micros() will not increment during execution of interrupt handler.

Debouncing - even with above two methods, things may still not work properly. The most probable cause would be switch "bouncing". You can counter it by

  • software debouncing - delaying after pulseIn() or ignoring interrupts for some period of time after first interrupt is detected
  • adding a small (10-100nF) capacitor at the input, that will act as a low-pass filter, cutting out short pulses
  • using not a reed switch, but a hall sensor with hysteresis
1

Your test if (reedState == HIGH) does not tell you when the reed switch is activated, it tells you when it's active, which is not the same. You should instead look at when the switch transitions from LOW to HIGH, which means you also have to remember the last state.

For example (untested):

void loop() {
    static int lastReedState;
    static unsigned long lastTransition;
    int reedState = digitalRead(reed);

    // On a rising transition of the reed switch:
    if (reedState == HIGH && lastReedState == LOW) {

        // Compute time since last valid transition.
        unsigned long now = millis();
        unsigned long revolutionTime = now - lastTransition;

        // Debounce: this transition is valid only if enough time has
        // elapsed since the last valid one.
        if (revolutionTime > 20) {

            // Compute and report speed.
            int speed = circumference / revolutionTime;
            Serial.println(speed);

            // Remember this transition.
            lastTransition = now;
        }
    }

    // Remember last state.
    lastReedState = reedState;
}

This assumes circumference has already been calculated and has the appropriate unit (miles⋅ms/h if you want the speed reported in mph). Actually it could be a compile-time int constant (4641), which would avoid having floating point calculations at run time.

0

Imagine your wheel rotating very slow ... imagine time was slowed down. Imagine the magnet comes to the reed sensor and closes the switch. You will now take a measurement. The Ardunio code loop will end and immediately restart ... because time has been slowed (in our story), the wheel will have barely moved and AGAIN you will take another measurement because the reed switch is STILL closed because the magnet hasn't moved far enough.

I believe you need to change your logic such that after you detect the reed switch closing, you then need to detect that the reed switch is OPEN again ... before once more looking for the reed switch to close. Something like:

if (reedState == HIGH) {
   // do something
   while(digitalRead(reed) == HIGH) {
      // do nothing
   }
}
0

A: Use interrupts to detect the state change on the reed switch
B: Use a hall sensor instead of the reed, they do not suffer switch bounce
C: Use a debounce circuit, a 74HC14 with a small capacitor connected between ground and the input
Put a 4K7 resistor as pullup between VCC & the input
Put a 100 ohm resistor on the wire coming from the Reed, which is connected to GND on the other side

The 100 ohm prevents discharge damage to the contacts from the capacitor and suppresses RF interference

Ground all unused inputs and feed the output of the active channel to the Arduino The 74HC series are 3v3 & 5V compatible, the 74HCT series is only 5V compatible but are more suited as level shifters from 3v3 signals to 5V output

D: use a software debounce routine

  • As the 74HC14 is an inverter, you can use a second one to invert the signal, then feed 2 inputs to the Arduino, this will make it easier to write 2 interrupt routines to detect the state changes and measure the time period between pulses – Peter Dunne Jun 15 '16 at 1:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.