I'm currently trying to set up a speedometer for a bicycle using an Arduino Uno, and a reed switch. I've already written code for mph and got it to display on an LCD, but now I want to calculate average speed using the 20 most recent mph values. I've looked online but I haven't been able to find something that works and that I can understand (I'm new to all of this, I've only been teaching myself for a couple days). Thank you for the help!
Averaging an average will give you the wrong result.
Count a large number of actuations on the reed switch, like 100 or 1000 revolutions of the wheel. Then look at the time between the first and last to get an average of the speed over a longer distance. Or do it by time: count clicks for 30 seconds or 300 seconds and use that distance.
In practice you probably want to do this 300sec average many times in 300sec. So you need to store many values of how many clicks you had 299sec ago, 298sec ago, 297sec ago...
So each time you produce an "average speed" output, save the actual time and distance in a large enough array to enable you to always go back 300 seconds (or whatever time you pick.) Use a "circular buffer" so that you overwrite the old values after the buffer is filled.
Be careful picking the buffer size as it's easy to exceed the memory available on the basic Arduinos.
Google Arduino Running Average. I bet the playground has something on this.
Here's an old sketch I did to get average of last 16 temperature readings, maybe you can get some useful ideas.
// program for testing LM35 temperature sensor
unsigned long timerStart;
const byte ainPin = A0, // lm35 input
numSamples = 16; // number of samples
byte index; // index into array
int samples[numSamples], // circular array
total; // sum of 16 samples
const float aRef = 1.09, // voltage at AREF pin measured with DMM
cal = 100.0; // adjust to calibrate with accurate thermometer
float avg, // average of samples
tempC, // Celsius temperature
tempF; // Fahrenheit temperature
// display update time (milliseconds)
const unsigned int updateTime = 2000; // 2 seconds here
void setup()
{
Serial.begin(9600);
analogReference(INTERNAL); // set internal ref. nominal 1.1 volts
for (byte i = 0; i < numSamples; i++) // fill array with initial value
{
samples[i] = analogRead(ainPin);
delay(5);
}
}
void loop()
{
if (millis() - timerStart > updateTime)
{
samples[index] = analogRead(ainPin); // insert latest value in array
// at index position
for (byte j = 0; j < numSamples; j++)
total += samples[j]; // sum values in array
avg = total * 1.0 / numSamples; // average of values
tempC = avg * aRef * cal / 1023; // Celsius temperature
tempF = tempC * 1.8 + 32;
printResults(index);
total = 0;
if (++index >= 16) // increment index
index = 0; // reset index to beginning of array
timerStart = millis(); // reset timer
}
} // end loop
void printResults(byte i)
{
Serial.println(F("index latest total avg tempC tempF"));
Serial.print(" ");
Serial.print(i); Serial.print("\t");
Serial.print(samples[i]); Serial.print("\t");
Serial.print(total); Serial.print("\t");
Serial.print(avg); Serial.print("\t");
Serial.print(tempC, 1); Serial.print("\t");
Serial.print(tempF, 1);
Serial.println("\n");
for (byte k = 0; k < numSamples; k++) // print value of each array member
{
Serial.print(samples[k]);
Serial.print(" ");
}
Serial.println();
for (byte i = 0; i < numSamples; i++) // print "***" under updated member
{
if (i == index)
Serial.print("*** ");
else Serial.print(" "); // 4 spaces
}
Serial.println();
} // end printResults
You can't average averages.
Let's say I have a bicycle odometer with one click per rotation of the wheel. (The OP hasn't told us any different.) I "take a reading" once per rotation.
Let's say I sit stationary for 4 minutes and ride at 30kph for 1 minute. What's my average speed for the previous 5 minutes? Well, there's no readings for the stationary time so the average-of-averages will think I have been doing 30kph for 5 minutes, which would imply that I've moved 2.5km. But I've only moved 0.5km.
Speed is always distance divided by time. 5 minutes ago, I was 0.5km away from where I am now. That's an average speed of 6kph.
Of course you may actually want the "average speed when I'm moving" value. That's always going to be a higher number.
Sounds like a job for a low-pass filter.
avgSpeed = .75avgSpeed + .25sensedCurrentSpeed;
the .75 should be adjusted to suit, of course.