An Arduino digital resistor circuit relies on a digital potentiometer (digipot) IC to provide software-adjustable resistance via serial protocols. For a 10kΩ range on a 5V logic board like the Arduino Uno R3, the Microchip MCP41010 is the optimal choice. It operates via SPI, costs between $1.50 and $2.20 per unit in 2026, and handles 5V logic natively without level shifters. This guide provides the exact pinout, datasheet-accurate SPI command bytes, and a closed-loop verification code to ensure your wiper is actually moving.

Project Overview & Difficulty Rating

Target Board: Arduino Uno R3 (ATmega328P, 5V logic)
Difficulty: 2/5 (Basic SPI wiring and serial parsing)
Estimated Time: 45 minutes
Core Concept: The MCP41010 contains a 256-tap internal resistor ladder. By sending an 8-bit command and an 8-bit data byte over SPI, you move the wiper (PW) across the ladder between the PA and PB terminals, altering the resistance dynamically.

MCP41010 vs AD5206 vs TPL0101: Spec-Sheet Comparison

Not all digital resistors are created equal. Choosing the wrong interface or voltage rating is the most common reason embedded projects fail on the bench. Below is a data-dense comparison of the three most common digipots used in maker projects.

IC Model Interface Channels Resistance Wiper Resolution VCC Range Approx. Price (2026)
Microchip MCP41010 SPI 1 10 kΩ 256 steps (8-bit) 2.7V - 5.5V $1.65
Analog Devices AD5206 SPI 6 10 kΩ / 100 kΩ 256 steps (8-bit) 2.7V - 5.5V $4.50
TI TPL0101-100 I2C 1 100 kΩ 256 steps (8-bit) 2.7V - 5.5V $2.10

Source: Component pricing aggregated from DigiKey and Mouser Q1 2026 catalogs.

Parts List & Hardware Pin Mapping

To build this circuit, you need the exact components listed below. Do not substitute the MCP41010 with the MCP42010 (dual channel) without adjusting the SPI command bytes, as the internal addressing differs.

  • 1x Arduino Uno R3 (or Nano v3 with ATmega328P)
  • 1x Microchip MCP41010-I/P (8-pin PDIP package)
  • 1x 10kΩ fixed resistor (for voltage divider verification)
  • 1x Solderless breadboard and male-to-male jumper wires
  • 1x 10µF decoupling capacitor (placed across VCC and GND)

MCP41010 8-Pin PDIP Pinout Table

MCP41010 Pin Function Arduino Uno R3 Connection Notes
1CS (Chip Select)Digital Pin 10 (SS)Active LOW. Must idle HIGH.
2SCK (Serial Clock)Digital Pin 13 (SCK)Hardware SPI clock line.
3SI (Serial Input)Digital Pin 11 (MOSI)Data from Uno to Digipot.
4GNDGNDCommon ground.
5PA (Pot Terminal A)5VTop of resistor ladder.
6PW (Wiper)Analog Pin A0Output. Also wire to your external load.
7PB (Pot Terminal B)GNDBottom of resistor ladder.
8VCC5VPower supply. Add 10µF cap to GND.

Step-by-Step Wiring Procedure

  1. Seat the IC: Place the MCP41010 across the center trench of the breadboard. Ensure the half-circle notch is at the top; Pin 1 is top-left.
  2. Power & Decoupling: Connect Pin 8 (VCC) to the 5V rail and Pin 4 (GND) to the ground rail. Place the 10µF capacitor directly across these two pins on the breadboard to filter high-frequency SPI switching noise.
  3. SPI Bus Routing: Connect Pin 1 (CS) to Uno D10, Pin 2 (SCK) to Uno D13, and Pin 3 (SI) to Uno D11. Crucial: Do not connect MISO (D12). The MCP41010 is a write-only device; it has no data output pin.
  4. Resistor Ladder Biasing: Connect Pin 5 (PA) to 5V and Pin 7 (PB) to GND. This creates a 0V to 5V gradient across the internal 10kΩ string.
  5. Wiper Verification: Connect Pin 6 (PW) to Arduino Analog Pin A0. This allows the microcontroller to read back the actual voltage and verify the SPI command succeeded.

Complete Arduino SPI Code with Verification

This code targets the Arduino Uno R3. It includes a closed-loop verification step: after sending the SPI bytes, it reads the A0 pin to confirm the wiper moved. This prevents the silent failures common in open-loop digipot tutorials.

#include <SPI.h>

// Pin Definitions for Arduino Uno R3
#define CS_PIN 10
#define WIPER_SENSE_PIN A0

// MCP41010 Command Byte: 00010000 (0x10)
// Bits 7-6: 00 (Write to potentiometer)
// Bits 5-4: 01 (Select Pot 0 - MCP41 only has one)
// Bits 3-0: 0000 (Dummy bits, data follows in next byte)
const byte CMD_WRITE_POT0 = 0x10;

void setup() {
  Serial.begin(115200);
  while (!Serial) { ; } // Wait for serial port (native USB boards)
  
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // CS must idle HIGH
  
  SPI.begin();
  // MCP41010 max clock is 10MHz, Arduino default is 4MHz. Safe to use default.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0); // CPOL=0, CPHA=0
  
  Serial.println("Arduino Digital Resistor (MCP41010) Initialized.");
  Serial.println("Enter a value between 0 and 255 to set the wiper.");
}

void loop() {
  if (Serial.available() > 0) {
    int targetValue = Serial.parseInt();
    
    // Error Handling: Bounds checking
    if (targetValue < 0 || targetValue > 255) {
      Serial.print("Error: Resistance value ");
      Serial.print(targetValue);
      Serial.println(" exceeds 8-bit register limit (0-255). Ignored.");
      return;
    }
    
    setDigipot(targetValue);
    verifyWiperVoltage(targetValue);
  }
}

void setDigipot(int value) {
  // Pull CS LOW to start transmission
  digitalWrite(CS_PIN, LOW);
  
  // Send 16-bit sequence: Command Byte then Data Byte
  SPI.transfer(CMD_WRITE_POT0);
  SPI.transfer(value);
  
  // Pull CS HIGH to latch the data
  digitalWrite(CS_PIN, HIGH);
}

void verifyWiperVoltage(int expectedStep) {
  delay(10); // Allow wiper capacitance to settle
  int adcReading = analogRead(WIPER_SENSE_PIN);
  float measuredVoltage = adcReading * (5.0 / 1023.0);
  float expectedVoltage = expectedStep * (5.0 / 255.0);
  
  Serial.print("Step: "); Serial.print(expectedStep);
  Serial.print(" | Expected V: "); Serial.print(expectedVoltage, 2);
  Serial.print(" | Measured V: "); Serial.println(measuredVoltage, 2);
  
  // Tolerance check: Allow 0.15V variance for wiper resistance and ADC noise
  if (abs(measuredVoltage - expectedVoltage) > 0.15) {
    Serial.println("WARNING: Wiper voltage deviation > 0.15V. Check PA/PB wiring.");
  }
}

Debugging: Silent Failures and SPI Errors

Digital resistors rarely throw compiler errors; they fail silently on the bench. If your serial monitor outputs WARNING: Wiper voltage deviation > 0.15V or the measured voltage remains stuck at 5.00V, follow this ranked troubleshooting path.

The First 3 Things to Check:
  1. CS Pin Idle State: The MCP41010 ignores all SPI clocking if CS is not pulled HIGH between transfers. If you forgot digitalWrite(CS_PIN, HIGH) in your setup, the chip will behave erratically.
  2. The MISO Trap: Beginners often wire MISO (D12) to the digipot, looking for a 'data out' pin. The MCP41010 is write-only. If you wired D12 to Pin 3, you are shorting the SPI bus. SI (MOSI) is the only data line.
  3. PA/PB Rail Swap: If your wiper voltage moves in the opposite direction of your expected step (e.g., step 255 yields 0V), you have swapped Pin 5 (PA) and Pin 7 (PB). Swap the 5V and GND wires on the ladder terminals.

Ranked Causes for 'Stuck at VCC' Symptoms

If the wiper reads exactly 5.00V (ADC 1023) regardless of the SPI byte sent, the internal latch is failing to update. According to the Microchip MCP41010 Datasheet, this is caused by:

  1. Incorrect Command Byte (60% of cases): Sending 0x00 instead of 0x10. The first two bits must be 00 (write) and the next two 01 (pot 0).
  2. SPI Clock Polarity (25% of cases): Using SPI_MODE1 or SPI_MODE3. The MCP41010 strictly requires SPI_MODE0 (CPOL=0, CPHA=0).
  3. VCC Brownout (15% of cases): The Uno's 5V rail is sagging under load, causing the digipot's internal POR (Power-On Reset) circuit to trigger and reset the wiper to mid-scale or VCC. Add the 10µF decoupling capacitor.

Extending and Simplifying the Build

How to Extend: Daisy-Chaining Multiple Digipots

If your project requires multiple adjustable resistors (e.g., a programmable audio mixer or multi-channel DAC), you can daisy-chain SPI devices. Because the MCP41010 lacks a daisy-chain output pin, you must use parallel CS wiring. Connect SCK and SI to all digipots in parallel, but route a unique CS pin from the Arduino to each IC. To update Pot A, pull CS_A LOW, send 16 bits, pull CS_A HIGH. The Arduino SPI Reference confirms that sharing MOSI/SCK across multiple CS lines is fully supported by the hardware SPI bus.

How to Simplify: Switching to I2C

If you are using an Arduino shield that monopolizes the hardware SPI pins (D11, D12, D13), such as the Ethernet Shield or certain TFT displays, SPI digipots become a wiring nightmare. To simplify the build, swap the MCP41010 for the Texas Instruments TPL0101-100. It uses the I2C protocol (requiring only A4/SDA and A5/SCL on the Uno), frees up your SPI bus, and includes non-volatile memory (EEPROM) to remember its wiper position after a power cycle—a feature the MCP41010 lacks.