A potentiometer is the simplest way to learn the Pico W’s ADC. The two outer legs go to 3V3 and AGND, the wiper to GP26 (ADC0), and MicroPython reads it with ADC(26).read_u16().
The script prints the raw value, the voltage and a percentage four times a second. Add an LED on GP15 and it follows the knob through hardware PWM.
Wiring
| Raspberry Pi Pico W | Rotary potentiometer (10 kΩ, B10K) | Wire | Notes |
|---|---|---|---|
| AGND | Pin 1 | black | AGND (pin 33) is the analog ground next to the ADC pins — quieter than a digital GND. Any GND also works. |
| GP26 | Wiper | green | GP26 = ADC0, physical pin 31. |
| 3V3 | Pin 3 | red | 3V3 is pin 36. Never use VBUS or VSYS here — the wiper would reach 5 V. |
Passive — across the board’s 3.3 V or 5 V part, 3.3 V board. Connect the outer legs to the same voltage your ADC uses: 3.3 V and GND on ESP32 and Pico, 5 V and GND on Uno. The wiper can then never exceed the pin limit.
Board quirks
- Only GP26, GP27 and GP28 are analog on the header (ADC0–ADC2). GP29 is used inside the Pico W by the wireless chip.
read_u16()always returns 0–65535: the RP2040’s 12-bit result scaled up, so code moves unchanged between boards.- The ends of the range are not perfect: expect a few hundred counts at 0 V and a little below 65535 at full scale. Averaging 16 reads steadies the number.
- The Pico W’s own LED is switched by the wireless chip and cannot be dimmed — that is why the PWM demo uses an external LED.
Code
# Potentiometer on a Raspberry Pi Pico W ADC (MicroPython).
# Wiring: Pin 1 -> AGND (pin 33), Wiper -> GP26 / ADC0 (pin 31), Pin 3 -> 3V3 (pin 36).
# Optional: an LED + 220 Ohm on GP15 (pin 20) follows the knob. The Pico W's own LED sits on the
# wireless chip and can only switch on/off, so PWM dimming needs an external LED.
from machine import ADC, Pin, PWM
import time
pot = ADC(26) # GP26 = ADC0. Only GP26, GP27 and GP28 are analog on the header.
led = PWM(Pin(15)) # optional LED + 220 Ohm on GP15 (pin 20)
led.freq(1000)
def read_avg(n=16):
# read_u16() always returns 0-65535 (the 12-bit result scaled up); average away the noise
return sum(pot.read_u16() for _ in range(n)) // n
while True:
raw = read_avg()
volts = raw * 3.3 / 65535
pct = raw * 100 // 65535
led.duty_u16(raw)
print("raw {:5d} {:.2f} V {:3d} %".format(raw, volts, pct))
time.sleep_ms(250)# Potentiometer on a Raspberry Pi Pico W ADC (MicroPython).
# Wiring: Pin 1 -> AGND (pin 33), Wiper -> GP26 / ADC0 (pin 31), Pin 3 -> 3V3 (pin 36).
# Optional: an LED + 220 Ohm on GP15 (pin 20) follows the knob. The Pico W's own LED sits on the
# wireless chip and can only switch on/off, so PWM dimming needs an external LED.
from machine import ADC, Pin, PWM
import time
pot = ADC(26) # GP26 = ADC0. Only GP26, GP27 and GP28 are analog on the header.
led = PWM(Pin(15)) # optional LED + 220 Ohm on GP15 (pin 20)
led.freq(1000)
def read_avg(n=16):
# read_u16() always returns 0-65535 (the 12-bit result scaled up); average away the noise
return sum(pot.read_u16() for _ in range(n)) // n
while True:
raw = read_avg()
volts = raw * 3.3 / 65535
pct = raw * 100 // 65535
led.duty_u16(raw)
print("raw {:5d} {:.2f} V {:3d} %".format(raw, volts, pct))
time.sleep_ms(250)Expected serial output
raw 412 0.02 V 0 %
raw 21876 1.10 V 33 %
raw 43305 2.18 V 66 %
raw 65231 3.28 V 99 %raw 412 0.02 V 0 % raw 21876 1.10 V 33 % raw 43305 2.18 V 66 % raw 65231 3.28 V 99 %
Common errors
ValueError: invalid Pin for ADC
Only GP26, GP27 and GP28 work with ADC(). Move the wiper to one of them.
Value jumps by hundreds while the knob is still
Normal ADC noise on a breadboard. Use AGND for the pot, keep wires short, and average more readings.