Smoothing the reading
A still knob does not give a still number: every ADC reading wanders by a few counts. A running average takes the wander out, and the price is lag. Moving the average an eighth of the way to each new reading, a hundred times a second, is steady and still feels direct.
A still knob, a moving number
Leave the knob alone and print the reading, and the last digit or two keep changing. The ADC is measuring a real voltage with real electrical noise on it, and every conversion lands a little differently. On an Uno the wander is usually a count or two; on the 12-bit boards, and the ESP32 in particular, it is more.
For a knob that only sets a brightness, nobody notices. For a knob whose value is printed, compared with a threshold or shown on a display, the flicker is the first thing anyone sees.
An average that moves a fraction each time
The sketch keeps one number, the average, and each time it reads the pin it moves the average a fraction of the way towards the new reading. With a weight of 1/4 the average goes a quarter of the way; with 1/16, a sixteenth. Noise pulls it up and down by a fraction of the noise, so the wander shrinks. A real turn keeps pulling it the same way, so the average follows, a little late.
The figure's noise is a picture of the behaviour, not a measurement of any board. Its shape is what matters: at 1/16 the line is steadiest, and takes about 35 readings to catch up with a turn. At 1/4 it catches up in about 8 and wanders more. The sketch uses 1/8, about 17 readings, and at a reading every 10 ms that is under two tenths of a second, which still feels direct.
This is the same trade as debouncing a button: a window long enough to ignore what you do not want, and short enough that the reader does not feel it.
A dead band, for values you act on
A smoothed number still flips between two neighbours when it sits right on the boundary. If the sketch does something when the value changes, send a message, move a servo, redraw a display, only accept a change of two counts or more from the last value it acted on. That dead band costs no lag, and the flicker stops.
Smoothing does not straighten the bend: it averages the reading it is given. Smooth first, then look the result up in the calibration table.
The code
One reading every 10 ms, and an average that moves an eighth of the way towards each new reading. It prints both, labelled, so the Serial Plotter draws the raw reading and the smoothed one as two lines on the same axes.
/*
Rotary Potentiometer - smoothing TK08 / /p/tk08
Wiring. Count from the square pad on the TinkerBlock board, knob
up, header at the bottom:
GND -> GND
VCC -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
(the full turn puts VCC on your analog pin)
NC -> nothing (unconnected on the board)
SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
ESP32-S3, GP26 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
Tools > Serial Plotter at 115200, Serial Monitor closed
No library needed.
*/
// The pin SIGNAL is wired to, picked for the board you compile for.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
#if defined(ARDUINO_ARCH_AVR)
const int POT_PIN = A0;
#elif defined(ARDUINO_ARCH_RP2040)
const int POT_PIN = 26;
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
const int POT_PIN = 4;
#else
const int POT_PIN = 34; // the classic ESP32
#endif
const int SMOOTH_N = 8; // each reading moves the average 1/8
float average;
void setup() {
Serial.begin(115200);
#if !defined(ARDUINO_ARCH_AVR)
analogReadResolution(12); // the Pico's core starts at 10 bits
#endif
average = analogRead(POT_PIN); // start from a real reading
}
void loop() {
int raw = analogRead(POT_PIN);
average += (raw - average) / SMOOTH_N;
Serial.print("raw:");
Serial.print(raw);
Serial.print(" smooth:");
Serial.println(average, 0);
delay(10); // a hundred readings a second
}The average is a float so the eighths are not lost to rounding. Start it at a real reading, as setup() does, or it spends its first second climbing up from zero. SMOOTH_N sets the trade: larger is steadier and slower.
The same running average in MicroPython. Thonny's plotter draws both numbers on each printed line, so the raw reading and the smoothed one appear as two lines.
"""
Rotary Potentiometer - smoothing, MicroPython TK08 / /p/tk08
Wiring. Count from the square pad on the TinkerBlock board, knob
up, header at the bottom:
GND -> GND
VCC -> 3V3 (never 5V: the full turn puts VCC on your pin)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
View > Plotter draws both numbers
Nothing to install: machine and time are built in.
"""
from machine import ADC, Pin
import sys
import time
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
POT_PIN = 4
SMOOTH_N = 8 # each reading moves the average 1/8
pot = ADC(Pin(POT_PIN))
if sys.platform == "esp32":
pot.atten(ADC.ATTN_11DB) # widest range, roughly 0 to 3.1 V
average = pot.read_u16() >> 4 # start from a real reading
while True:
raw = pot.read_u16() >> 4 # 0 to 4095
average += (raw - average) / SMOOTH_N
print("raw:", raw, "smooth:", round(average))
time.sleep_ms(10) # a hundred readings a secondThere is no Uno here: an Uno cannot run MicroPython. Readings are scaled to 0 to 4095 to match the Arduino sketch on a 3.3 V board. Printing a hundred lines a second can slow Thonny's shell; if it lags, print every fifth reading.
When it does not work
Any number that is rounded will sit on a boundary sometimes and flip between two values. If something acts on the exact value, only accept a change of two counts or more from the last value you acted on. That dead band, called hysteresis, stops the flicker without adding lag.
The average is too heavy, or the loop too slow. With a weight of 1/8 the average needs about seventeen readings to catch up with a quick turn; at a reading every 10 ms that is under two tenths of a second. Use a lighter weight, or read more often.
You can take several readings in a row and average them, as the calibration sketch does with sixteen. That smooths each value but makes each one slower to take. The running average here keeps one cheap reading per loop and spreads the averaging over time.
A few counts on an Uno, and often more on the 12-bit boards, especially the ESP32, whose ADC is noisy by design. Wander of tens of counts that follows your hand is not noise: the SIGNAL wire is loose and the pin is floating.
Six wrong readings and the one thing to check for each.
When the reading is wrong →Edit this page — content/books/rotary-potentiometer/smoothing-the-reading.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Rotary Potentiometer
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.