Bike Spedometer Code Issue

Hello! I’m relatively inexperienced with Arduino, but I’m trying to make a reed switch bike spedometer by measuring the time for the wheel to rotate and calculating speed from that. I had tried to start this project, but a wrench was thrown in the plan when I realized my reed switch can turn off even in the presence of a magnetic field if the magnet is postioned correctly. To solve this, I worked with a debounce feature, but now I’m stuck with smoothing the data.

Here’s my code with annotations:

const int reedPin = 2;    // the number of the pushbutton pin
int reedState;             // the current reading from the input pin
int lastreedState = LOW;  // the previous reading from the input pin
int starttime;
int total = 0;
int endtime;
int cycletime;
int smooth;
int rottime;
int i;
int numReadings = 12;
// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 20;    // the debounce time; increase if the output flickers

void setup() {
  pinMode(reedPin, INPUT);
 starttime = millis();
  Serial.begin(9600);
 
}

void loop() {
 for(i = 0; i < 5; i++){     //this takes five measurements of the time it takes for one revolution and adds them to a running total below
  int reading = digitalRead(reedPin);
  if (reading != lastreedState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {   
    if (reading != reedState) {
      reedState = reading;         // this previous code is for debouncing
      if (reedState == HIGH) {
        endtime = millis();        // if the reed switch goes high, then that means a cycle is completed, so the endtime is millis()
      }
    }
     cycletime = endtime - starttime; //this calculates the length of one revolution of the wheel.
     if(cycletime != 0){ // this eliminates all the 0 cyle times that the plotter would display
      //Serial.print(cycletime); 
       total += cycletime; //this is a running total for five cycles. It is in the if statement so that it doesn't fill with a lot of 0s for cycletimes.
     }
   
  }
 


starttime = endtime; // this refreshes the time for a new cycle
lastreedState = reading; // this refreshes the reed switch state for a new cycle
 }
smooth = total/5; // this calculates an average for the five cycles
Serial.println(smooth);

}

Instead of printing averages, the smooth function seems to always increase in the serial monitor. I have been able to successfully plot all nonzero cycletimes, but I’m struggling to average them out. I am open to getting rid of the for loop for an easier way of averaging, but that’s where the bottleneck is. I think debounching will work as well as finding the time of one rotation, but calculating that average is where the problem is.
Would you mind helping me out?

If you have any questions, feel free to ask. Thanks for the help!!!

Where does 'total' get set back to zero so you can add more samples to it?

I don't think wrapping a 'for' loop around the detection of state changes will do what you want. Won't the loop just take 5 samples and do nothing for the 99.99 percent of samples where the reed switch has not just closed?

Unless you are going quite fast, an average of 5 revolutions will take a while. You might want to use a moving average.

I'd start with NOT averaging to see how stable the rotation period is. You can always add the period to a moving average later.

const int reedPin = 2;
const unsigned long DebounceDelay = 20;
unsigned long LastDebounceTime = 0;


void setup()
{
  pinMode(reedPin, INPUT);
  Serial.begin(9600);
}


void loop()
{
  static boolean reedSwitchWasClosed = false;
  boolean reedSwitchIsClosed = digitalRead(reedPin);
  unsigned long currentMillis = millis();
  static unsigned long lastClosedTime = 0;


  if (reedSwitchIsClosed != reedSwitchWasClosed &&
      currentMillis - LastDebounceTime > DebounceDelay)
  {
    // Switch just changed state
    reedSwitchWasClosed = reedSwitchIsClosed;
    LastDebounceTime = currentMillis;
    
    if (reedSwitchIsClosed)
    {
      // Switch just closed
      unsigned long period = currentMillis - lastClosedTime;
      lastClosedTime = currentMillis;
      Serial.println(period);
    }
  }
}

Not sure about anything else in the code, but you don’t appear to ever reset ‘total’, so it is always increasing. Adding every non-zero reading, not just the last 5.

The bike tire has a particular circumference measurement. One rotation and the bike tire has traveled the length of the circumference or distance traveled per tire revolution. Might be easier to count tire rotations as a measurement of distance traveled and integrate time into the formula.

Speed = Distance / Time

Thank you all!!! :slight_smile: :slight_smile:

For very fast pulses, use the counting pulses method. For slow pulses, use the elapsed time between pulses method. The reason is, when you are counting pulses, it takes time to accumulate them so there is a time delay. Imagine if you are driving a car too fast, the police stop you and ask if you know how fast you are going. You say, "I don't know, it says zero because I'm still counting wheel revolutions". With the elapsed time method, you get a reading every revolution, so without getting into tedious calculations it might be from a few seconds to 1/100 second. However if you choose to count, the accuracy is limited to the count but the count must be accumulated during the sampling interval. At the end of some fixed sampling period (that you must set) you will have an integer number of events in the count. But there will be a real difference in the wheel position, and the precision of the remaining part of the wheel rotation is lost. Also since you can generally time events to thousandths or even millionths of a second, the interval method is very practical since it covers a wide pulse frequency range, and is easily measured.

If you're getting thousands of pulses a second, from a high RPM device for example, then it might start to make some sense to count pulses. In that case, another example, you could achieve a resolution of 1/1000 with readings every one second.

You also have the additional option, albeit more complex, to determine the speed based on the length of time the contacts are closed (assuming the contacts are normally open).
This has more dependencies and, in practice, the relationship between speed and duration of contact closure would have to be learned from the algorithm which determines speed based on the period of a single revolution. This relationship may also not be entirely linear.
However, it would be better at lower speeds because it requires only one pass of the magnet over the sensor to give a reading. The “standard” method for determining speed requires two passes.

What is your wheel circumference and how many magnets?