Read a potentiometer and dim an LED with an Arduino
- 192parts on the bench
- 25boards running now
- 1.00xreal time, on every board
A potentiometer on an analog pin is the smallest complete input-to-output circuit there is: read a voltage, write a brightness. It is also where two of the most useful Arduino functions, analogRead() and analogWrite(), meet for the first time, and where people find out that they do not use the same range. The circuit above is a knob on A0 of an Arduino Uno setting the brightness of an LED on pin 9.
It is running on a simulated Uno. Open it in the editor and turn the knob while it runs; the serial monitor prints the position as a percentage and the PWM value it becomes.
What you need
- An Arduino Uno
- A 10 k potentiometer
- An LED and a 220 ohm resistor
- A breadboard and jumper wires
Wiring
What the potentiometer does
A potentiometer is a resistive track with a wiper that slides along it. With the two ends on 5 volts and ground, the wiper sits at a voltage somewhere between the two, in proportion to where it is on the track. That makes it a voltage divider you can adjust by hand, and A0 reads that voltage.
The Uno’s analog to digital converter turns 0 to 5 volts into a number from 0 to 1023. Half way around the knob reads about 512. The simulated potentiometer is modeled as the track itself, so the wiper also has the source resistance a real one has, which is up to 2.5 k at the center of a 10 k pot. Put a heavy load on the wiper and the reading bends, exactly as it would on a desk.
Ten bits in, eight bits out
analogRead() gives 0 to 1023. analogWrite() takes 0 to 255. So the reading has to be scaled down by four. You can write map(value, 0, 1023, 0, 255), or divide by four, or do what the example does and shift right by two bits, which is the same thing and on an AVR is a couple of instructions rather than a call to a divide routine.
analogWrite() does not produce a voltage. On pin 9 it switches the pin fully on and off about 490 times a second, and the value sets how much of each cycle is on. The LED looks dimmer because it is on for less of the time; the simulator’s LED averages its brightness over a few milliseconds the way the eye does, so a PWM-dimmed LED looks the way it does on a bench.
void loop() {
int raw = analogRead(KNOB);
// A quarter of the new reading, three quarters of what we had.
smoothed += (raw - smoothed) / 4;
int level = smoothed >> 2; // 0..1023 down to 0..255
if (level > 255) level = 255;
analogWrite(LAMP, level);
delay(5);
}Why the reading is smoothed
On real hardware, a steady knob does not give a steady number. The wiper is a sliding contact and the last bit of the converter is noise, so the reading wobbles by a count or two. At the bright end nobody notices, but at the dim end, where the eye is most sensitive, that wobble becomes a visible flicker.
The fix in the sketch is the cheapest filter there is: keep a running value and move it a quarter of the way towards each new reading. It settles in about thirty milliseconds, faster than a hand can turn a knob. The simulated potentiometer is cleaner than a real one and does not wobble, so the filter is doing less work here than it would on your desk. It is left in because it is what you should write for the real thing.
Try it in the editor
Open the circuit in the editor and turn the knob slowly near the bottom of its travel. The lamp fades smoothly down to off. Now select the potentiometer and change its taper property from linear to log. The same half-turn now covers a much smaller part of the brightness range, which is why a volume control pot feels wrong on an LED.
Next, move the LED’s wire from pin 9 to pin 8 and change LAMP to 8. Pin 8 has no PWM, so the lamp can only be fully on or fully off, and it snaps between them half way around the knob.
To see what the PWM actually looks like, drag an oscilloscope from the parts bin and put its probe on pin 9. The duty cycle widens and narrows as you turn the knob.
Common mistakes
Using a pin without PWM. On an Uno only pins 3, 5, 6, 9, 10 and 11 can dim. analogWrite() on any other pin just switches it fully on above 127 and off below.
Wiring the wiper to an end. If the reading only jumps between 0 and 1023, or does not change at all, the middle leg is not on A0.
Leaving out the scaling. Passing the raw 0 to 1023 reading straight to analogWrite() goes wrong in two different ways. The simulated Uno treats anything from 255 up as fully on, so the lamp is at full brightness a quarter of the way around the knob. A real Uno keeps only the low eight bits of values it does not special-case, so the lamp cycles from dim to bright four times in one turn. Either way, scale first.
Questions
What value potentiometer should I use with an Arduino?
10 k is the usual choice. Much lower wastes current through the track; much higher makes the ADC reading slower to settle, because the converter wants a source impedance of 10 k or less.
Why does my potentiometer only reach part of the range?
Check that the ends really are on 5 V and GND. A log taper pot, meant for volume controls, also feels very uneven with a linear reading. Kits use linear ones, and the simulated pot has a taper property for both.
Can I use a potentiometer with an ESP32?
Yes. Put the ends on 3.3 V and GND. The ESP32’s ADC reads 0 to 4095, and the ESP32 knob example shows why a 3.3 volt rail never quite reaches 4095 with the default attenuation.
Keep going
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.