The Short Answer: Which Arduino Variable Resistor Should You Use?

For 95% of 5V Arduino projects (Uno, Nano, Mega), the correct choice is a 10kΩ linear taper (B-taper) cermet or conductive plastic potentiometer.

Beginners often grab whatever is in the junk bin—usually a 100kΩ or 1MΩ audio-taper pot—and wonder why their Serial Monitor is spitting out jittery, non-linear garbage. The ATmega328P microcontroller’s internal Analog-to-Digital Converter (ADC) has a sample-and-hold capacitor of roughly 14pF. The official Arduino analogRead documentation and the Microchip datasheet explicitly recommend a source impedance of 10kΩ or less. If your variable resistor exceeds this, the internal capacitor cannot charge fully during the 1.5 ADC clock cycles allocated for sampling. The result? Ghosting, crosstalk between adjacent analog pins, and a compressed value range.

Decision Path: Pick Your Variable Resistor

ApplicationRecommended TypeExact Part / SpecWhy This Wins
General UI / Dials10kΩ Linear (B-Taper) CermetBourns 3386P-1-103LF (~$1.50)Matches the 10kΩ source impedance limit; cermet resists wiper wear and dust.
Precision Calibration10kΩ Multi-Turn CermetBourns 3296W-1-103LF (~$2.80)25 turns allow micro-adjustments without jumping 10-bit ADC steps.
Audio Volume (3.3V ESP32)10kΩ Audio (A-Taper) CarbonAlps RK09K113 (~$3.50)Logarithmic taper matches human hearing curves; 10kΩ is safe for ESP32 ADC limits.
High-Current RheostatDO NOT USE STANDARD POTUse Power Wirewound ResistorStandard pots will melt their carbon tracks at >50mA. Use a power resistor or PWM.

Default Pick: If you are just building a basic knob to control an LED, servo, or menu, buy the Bourns 3386P-1-103LF. It is cheap, robust, and electrically invisible to the Arduino's ADC.

Parts List & Spec Sheet: Building a Jitter-Free Analog Input

This build targets the Arduino Uno R3 (ATmega328P variant). While the newer Uno R4 Minima has a 14-bit ADC, the R3 remains the baseline for 90% of hobbyist tutorials and legacy shields. The principles here apply to both, but the R3's 10-bit (0-1023) resolution is what our code expects.

Bill of Materials (BOM)

  • Microcontroller: Arduino Uno R3 (ATmega328P) - ~$24.00 (Official) / ~$12.00 (Clone)
  • Variable Resistor: 10kΩ Linear (B-Taper) Cermet Trimmer (Bourns 3386P or equivalent) - ~$1.50
  • Bypass Capacitor: 0.1µF (100nF) Ceramic Disc Capacitor - ~$0.10 (Critical for noise rejection)
  • Wiring: 22 AWG solid core jumper wires (Pre-cut breadboard kit)
Bench Tip: Never use a breadboard with loose internal contacts for analog reads. A high-resistance breadboard contact acts as an unintended series resistor, pushing your total source impedance above the 10kΩ threshold and causing intermittent ADC ghosting. Solder the pot and cap to a perfboard for permanent installations.

Wiring & Pin Mapping: Avoiding the Ground Loop Trap

The most common mistake in wiring a 3-pin potentiometer is treating the outer legs as interchangeable without considering the physical rotation direction, or forgetting to tie the ground reference back to the Arduino's main ground plane. If your Arduino is powered via USB and your pot is powered via an external 5V rail, you must bond the grounds, or the ADC will read the voltage differential between the two floating grounds.

Pin Mapping Table

Potentiometer PinPhysical LocationArduino Uno R3 ConnectionNotes
Pin 1 (CCW)Left outer legGNDConnect to Arduino GND. Tie 0.1µF cap between Wiper and this GND.
Pin 2 (Wiper)Center legA0 (Analog Input)This is the variable voltage output. Do not use digital pins.
Pin 3 (CW)Right outer leg5VConnect to Arduino 5V out. Do not use Vin or 3.3V.

Step-by-Step Wiring Procedure

  1. De-energize the board: Unplug the Arduino USB cable before wiring to prevent accidental shorts between 5V and GND while probing the breadboard.
  2. Seat the potentiometer: Insert the Bourns 3386P into the breadboard so the adjustment screw faces up and the three legs span across the center trench.
  3. Wire the outer legs: Connect the left leg to the blue ground rail and the right leg to the red 5V rail.
  4. Install the bypass capacitor: Insert the 0.1µF ceramic capacitor so one leg shares the ground rail connection with Pin 1, and the other leg connects directly to the center trench row holding Pin 2 (the wiper). This creates a hardware low-pass filter that shorts high-frequency EMI to ground before it hits the ADC.
  5. Route the signal: Run a 22 AWG jumper from the wiper's breadboard row directly to the Arduino A0 header pin.
  6. Verify with a multimeter: Before plugging in the Arduino, set your DMM to resistance mode. Measure across Pin 1 and Pin 3; it should read exactly 10kΩ (±10%). Measure Pin 1 to Pin 2 while turning the screw; it should sweep smoothly from 0Ω to 10kΩ.

Complete Compilable Code: Smoothing and Mapping ADC Reads

Raw analogRead() values on an Uno R3 will naturally fluctuate by ±2 to ±5 steps even when the knob is perfectly still, due to thermal noise and internal ADC reference jitter. Instead of using a blocking delay() and averaging 100 samples (which makes the UI feel sluggish), we use an Exponential Moving Average (EMA) filter. This provides smooth, responsive output with minimal memory overhead.

This code also includes fault detection to catch disconnected wipers or misconfigured pins.

/*
 * Target Board: Arduino Uno R3 (ATmega328P)
 * Component: 10kΩ Linear Variable Resistor on Pin A0
 * Feature: EMA Filtering & Disconnected Wiper Fault Detection
 */

#include 

// --- PIN DEFINITIONS ---
const uint8_t POT_PIN = A0;
const uint8_t STATUS_LED = 13; // Built-in LED for fault indication

// --- FILTER & THRESHOLD CONSTANTS ---
const float EMA_ALPHA = 0.15;       // Smoothing factor (0.0 to 1.0). Lower = smoother but more lag.
const int FAULT_VARIANCE_THRESHOLD = 150; // Max allowed jump in raw ADC steps per cycle
const int PEGGED_HIGH_THRESHOLD = 1020;   // Threshold to detect short to 5V
const int PEGGED_LOW_THRESHOLD = 3;       // Threshold to detect short to GND

// --- STATE VARIABLES ---
float smoothedValue = 0.0;
int previousRawValue = 0;
bool isFirstRead = true;

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2000) { /* Wait for serial port on native USB boards */ }
  
  pinMode(STATUS_LED, OUTPUT);
  pinMode(POT_PIN, INPUT); // Explicitly set as input, though analogRead does this implicitly
  
  // Prime the ADC with a dummy read to charge the sample-and-hold capacitor
  analogRead(POT_PIN);
  delay(10);
  
  Serial.println(F("[SYS] Arduino Variable Resistor ADC Monitor Initialized."));
  Serial.println(F("[SYS] Target: 10k B-Taper | Filter: EMA (Alpha 0.15)"));
}

void loop() {
  // 1. Acquire raw 10-bit ADC reading (0-1023)
  int rawValue = analogRead(POT_PIN);
  
  // 2. Error Handling & Fault Detection
  if (isFirstRead) {
    smoothedValue = rawValue; // Seed the filter on the first run
    previousRawValue = rawValue;
    isFirstRead = false;
  } else {
    // Check for disconnected wiper (floating pin causes massive random jumps)
    int delta = abs(rawValue - previousRawValue);
    if (delta > FAULT_VARIANCE_THRESHOLD) {
      Serial.print(F("[ERR] ADC_FLOAT_DETECTED: Wiper contact lost or missing bypass cap. Delta: "));
      Serial.println(delta);
      digitalWrite(STATUS_LED, HIGH); // Turn on LED to indicate hardware fault
      delay(500); // Throttle error spam
      previousRawValue = rawValue;
      return; // Skip filtering this cycle
    }
    
    // Check for pegged values (shorts or wrong pin mapping)
    if (rawValue >= PEGGED_HIGH_THRESHOLD) {
      Serial.println(F("[ERR] ADC_PEGGED_HIGH: Pin shorted to 5V or wiper at max extreme."));
    } else if (rawValue <= PEGGED_LOW_THRESHOLD) {
      Serial.println(F("[ERR] ADC_PEGGED_LOW: Pin shorted to GND or wiper at min extreme."));
    }
    
    digitalWrite(STATUS_LED, LOW); // Clear fault LED if reading is stable
    
    // 3. Apply Exponential Moving Average (EMA) Filter
    // Formula: Smoothed = (Alpha * New) + ((1 - Alpha) * Old)
    smoothedValue = (EMA_ALPHA * rawValue) + ((1.0 - EMA_ALPHA) * smoothedValue);
    previousRawValue = rawValue;
  }
  
  // 4. Map to useful ranges
  int mappedPercent = map((int)smoothedValue, 0, 1023, 0, 100);
  int mappedPWM = map((int)smoothedValue, 0, 1023, 0, 255);
  
  // 5. Output telemetry
  Serial.print(F("Raw: ")); Serial.print(rawValue);
  Serial.print(F(" | Smooth: ")); Serial.print((int)smoothedValue);
  Serial.print(F(" | Percent: ")); Serial.print(mappedPercent);
  Serial.print(F("% | PWM: ")); Serial.println(mappedPWM);
  
  delay(20); // ~50Hz update rate, plenty fast for human-interface dials
}

Debugging: First 3 Things to Check When Your Analog Read Fails

When your serial output looks like a heart monitor in a horror movie, do not immediately blame the code. 99% of analog input issues are physical. Here is your ranked troubleshooting path.

1. Symptom: Serial Monitor spitting out random numbers (0 to 1023) while the knob is untouched.

  • Ranked Cause A (Most Likely): Missing 0.1µF bypass capacitor, or a floating ground. The ATmega328P is picking up RF noise from your PC's USB switching power supply.
  • Ranked Cause B: Wiper oxidation. If using a cheap carbon-track pot from a 10-year-old kit, the wiper contact resistance is fluctuating wildly.
  • The Fix: Solder a 100nF ceramic capacitor directly across the Wiper and GND pins on the potentiometer body. If the noise persists, swap the pot for a cermet variant (Bourns 3386P).

2. Symptom: Reading is stuck at exactly 1023 or 0, regardless of rotation.

  • Exact Error String: [ERR] ADC_PEGGED_HIGH: Pin shorted to 5V or wiper at max extreme.
  • Ranked Cause A: You wired the wiper (center pin) to a Digital pin (e.g., D2) instead of an Analog pin (A0-A5), or you are reading the wrong pin in code. Digital reads on a pulled-up pin will just return 1.
  • Ranked Cause B: The outer legs are reversed, or the wiper leg is physically shorted to the 5V leg via a stray strand of wire on the breadboard.
  • The Fix: Disconnect the Arduino. Use your multimeter in continuity mode. Probe the wiper wire at the Arduino header and trace it to the center leg of the pot. Verify the left leg goes to GND and the right to 5V.

3. Symptom: The reading works, but turning the knob only changes the value from 400 to 600 (compressed range), or adjacent analog pins start changing when you turn this one.

  • Exact Error String: [ERR] ADC_GHOSTING: Source impedance too high (>50k). (Note: This is a conceptual error; the ADC just outputs bad data, the code above detects it as variance if it jumps, but ghosting is usually steady-state corruption).
  • Ranked Cause: You are using a 100kΩ, 500kΩ, or 1MΩ potentiometer. The source impedance is too high for the ADC's sample-and-hold circuit to charge in time, causing voltage droop and crosstalk from the previously polled analog pin.
  • The Fix: Replace the potentiometer with a 10kΩ or 5kΩ variant. If you absolutely must use a high-impedance sensor, add a 10kΩ pull-down resistor and a 1µF capacitor to create a hardware buffer, or use an op-amp voltage follower (like the LM358) between the pot and the Arduino pin.

Extending and Simplifying the Build

Once you have a single variable resistor working flawlessly, you will inevitably want to add more, or you will realize a potentiometer is the wrong tool for the job.

How to Simplify: Switch to a Rotary Encoder

If your goal is menu navigation or infinite-scroll volume control, a potentiometer is a dead end because it has physical hard stops (usually 270° of rotation). Simplify the build by switching to a KY-040 or EC11 Rotary Encoder. Encoders output digital quadrature pulses, meaning they are completely immune to ADC jitter, source impedance limits, and analog noise. You can read them using hardware interrupts on pins D2 and D3, giving you infinite rotation with zero analog overhead.

How to Extend: Multiplexing 8 Knobs on a Single Analog Pin

The Uno R3 only has 6 analog pins. If you are building a MIDI controller or a synth pedalboard and need 8 or 16 variable resistors, do not upgrade to an Arduino Mega just for the pins. Instead, extend the build using a CD4051B Analog Multiplexer (~$0.50).

Wire the 8 potentiometer wipers to the CD4051's input channels (Y0-Y7). Use 3 digital pins on the Arduino to control the multiplexer's select lines (A, B, C). By toggling the digital pins, you route one of the 8 analog signals to a single Arduino analog pin (e.g., A0). You can read 8 knobs in roughly 2 milliseconds, well within the limits of human perception for UI latency. For an even deeper dive into ADC noise rejection techniques at the silicon level, refer to this Analog Devices technical article on ADC input filtering.

Final Recommendation: Stop buying the 100-pack of cheap, unbranded blue plastic potentiometers from Amazon. They drift with temperature and develop dead spots within a month. Spend the extra $1.50 per unit on Bourns or Alps cermet/conductive plastic pots. Your code will run cleaner, your debugging sessions will be shorter, and your final project will feel like a professional instrument rather than a science fair prototype.