Arduino moving average: smoothing a noisy analog reading
Beginner
Or see every lesson.
- 505parts on the bench
- 45boards running now
- 1.00xreal time, on every board
A moving average replaces each reading with the average of the last few, so the random wobble of a sensor or a potentiometer cancels out and the slow changes you care about remain. It costs a little lag and a little RAM. An exponential moving average does the same with one variable and no array, and a median gets rid of single spikes that an average only spreads out.
The circuit above is a potentiometer on A0 of an Uno, read five times a second. Set to the middle, it prints "A0 = 512 (2502 mV)" every time: the simulator’s converter reads a still knob as a still number. A real one wanders by a few counts, more with long wires or a noisy supply, so for the numbers on this page we wobbled the knob by up to 25 counts every 10 ms around 512, then moved it to 800 two seconds in.
Below is a moving average and an exponential one side by side, what each costs in lag, the zeros at the start, an integer version that never arrives, the overflow that turns 64 readings negative, and the median.
Want the step-by-step version? The lesson "Reading a voltage" walks through this with checkpoints.
Open the lessonA moving average of ten readings
Keep the last ten readings in an array, and a running total of them. Each pass, take the oldest reading out of the total, put the new one in its place, add it to the total, and divide by ten. That is one subtraction and one addition however many readings you keep, rather than adding up the whole array every time.
The same sketch keeps an exponential moving average as well: ema = ema + 0.1 * (raw - ema) moves a tenth of the way toward each new reading. Between 0.5 and 2 seconds the raw readings ranged from 487 to 536, the moving average from 505 to 518, and the exponential one from 505.6 to 516.9. Both cut the wobble to about a quarter.
const int NUM = 10;
int readings[NUM];
int next = 0;
long total = 0;
float ema = 0;
unsigned long lastPrint = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(A0);
total = total - readings[next];
readings[next] = raw;
total = total + raw;
next = (next + 1) % NUM;
int average = total / NUM;
ema = ema + 0.1 * (raw - ema);
if (millis() - lastPrint >= 50) {
lastPrint = millis();
Serial.print(millis());
Serial.print(" raw ");
Serial.print(raw);
Serial.print(" avg ");
Serial.print(average);
Serial.print(" ema ");
Serial.println(ema, 1);
}
delay(10);
}Open this sketch in the editorThe code alone: the editor places the board and the parts it can read from the sketch.
The price is lag
Smoothing means a real change shows up slowly too. When the knob moved from 512 to 800 at two seconds, the line "2112 raw 816 avg 798 ema 709.9" came 100 ms later: the moving average had arrived, because all ten readings in its array were new by then, and the exponential one was only two thirds of the way. It reached 794.9 at 2318 ms.
So the moving average of N readings lags by exactly N readings and then stops lagging, while an exponential average never quite finishes and keeps a little of every old reading. More readings, or a smaller factor than 0.1, means a smoother line that follows more slowly. Pick the size from how fast the real signal can change: a thermometer can average a second of readings, a knob a player turns wants a tenth of that.
The zeros at the start
Both filters start from nothing. With the knob still at 512, the first line printed was "50 raw 512 avg 307 ema 239.9": the array still held zeros and the exponential average had started at 0. The moving average was right after ten readings; the exponential one was still at 510.6 after half a second.
The fix is to start them from a real reading. In setup(), read A0 once, fill the array with it and set the total to ten times it, and set ema to the same reading. Then the first value out is already right.
An exponential average in whole numbers
Floats are slow on an Uno, so a common shortcut is ema = ema + (raw - ema) / 8; with ema an int. It never gets there. With the knob held at 800, it printed "800 793" for ever, because once the difference is under 8, dividing it by 8 gives 0 and nothing changes.
Keep the average eight times larger instead, so the fraction is not thrown away: long ema8;, then ema8 = ema8 + raw - ema8 / 8;, and ema8 / 8 is the smoothed value.
The total needs a long
An int on an Uno holds up to 32767, and 64 readings of 1023 add up to 65472. With int total and the knob all the way up, the sketch printed "total -64 average -1". The same sketch with long total printed "total 65472 average 1023". Make the total a long whenever readings times count can pass 32767, which for a 10-bit reading is any average of more than 32.
Spikes: use a median
An average spreads a single wild reading out rather than removing it. With the RunningAverage and RunningMedian libraries, both built into Mokxi, five readings each, one reading of 1023 among steady 512s printed "1023 average 614.2 median 512.00", and the average stayed at 614.2 until that reading left its window. The median of five ignores any one or two readings that are far from the rest.
A median costs more time, because it sorts its readings, so the common recipe is a short median to remove spikes, followed by an average to remove the wobble.
#include <RunningAverage.h>
#include <RunningMedian.h>
RunningAverage average(5);
RunningMedian median(5);
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(A0);
average.addValue(raw);
median.add(raw);
Serial.print(raw);
Serial.print(" average ");
Serial.print(average.getAverage(), 1);
Serial.print(" median ");
Serial.println(median.getMedian());
delay(20);
}Open this sketch in the editorThe code alone: the editor places the board and the parts it can read from the sketch.
Open this circuit in the editor. Arduino Uno: a potentiometer on A0, wired as it runs at the top of this page. Change a part or a value and run it again.
Open the circuitQuestions
How do I smooth analogRead values on an Arduino?
Average the last few readings: keep them in an array with a running total and divide by how many there are. Ten readings at 10 ms apart cut random wobble to about a quarter and add 100 ms of lag.
What is the difference between a moving average and an exponential moving average?
A moving average weighs the last N readings equally and needs an array. An exponential one needs a single variable, weighs new readings most, and keeps a little of every old one, so it never quite finishes following a change.
Why does my average start at zero?
The array of readings starts full of zeros, and the first average includes them. Fill the array with the first real reading in setup(), and start an exponential average from it too.
Why is my average negative?
The total overflowed an int. On an Uno an int stops at 32767, so more than 32 readings of up to 1023 need a long for the total.
Is there a smoothing library for Arduino?
Yes: RunningAverage and RunningMedian by Rob Tillaart are common, and both work in Mokxi. They do what the sketches on this page do, and the median one also removes single spikes.
Keep going
Where the facts on this page come from
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.