Skip to main content

MCP3208 ADC Tutorial

Introduction

One limitation of the Raspberry Pi Pico is that it only has a small number of built-in analog input (ADC) pins. If your project needs to read more sensors, potentiometers, or variable voltages, you will quickly run out of inputs.

To solve this, we can use an external analog-to-digital converter like the MCP3208. This chip provides 8 additional analog channels and communicates with the Pico using the SPI protocol.

In this tutorial, you will learn how to:

Connect an MCP3208 to the Pico Read analog values using CircuitPython Expand your system by adding a second MCP3208

One of the key advantages of SPI is that it allows multiple devices to share the same communication lines. All MCP3208 chips can use the same clock and data pins, and the only connection that must be unique for each chip is the CS (Chip Select) pin.

This means you can easily scale your project from 8 inputs to 16, 24, or more with minimal changes.

Why Use an External ADC?

An external ADC is useful when:

  • you need more analog inputs than the Pico has built in
  • you want a dedicated analog input chip
  • you are reading several potentiometers or sensors at once
  • you want to expand a project without moving to a different microcontroller

What This Tutorial Covers

In this tutorial, you will learn:

  • What the MCP3208 does
  • How to wire it to the Pico with SPI
  • How to install the CircuitPython libraries
  • How to read two or more analog channels
  • How to scale the values for real projects

Components Needed

ComponentQuantity
Raspberry Pi Pico or Pico W1
MCP32081 or more
Breadboard1
Jumper wiresSeveral
Joystick modules1 or more
USB cable1

Wiring for one MCP3208

MCP3208 wiring

  • SCK -> GP18
  • MISO -> GP16
  • MOSI -> GP19
  • CS -> GP17

Then connect your analog signals to:

  • CH0
  • CH1
  • CH2
  • and so on up to CH7

The pinout of the mcp3208

MCP3208 wiring

CircuitPython Libraries Needed

You will need:

  • adafruit_mcp3xxx
  • adafruit_bus_device

These are normally copied into the lib folder on the CIRCUITPY drive.

If you are using the Adafruit CircuitPython bundle, copy the relevant library folders from the bundle into:

/lib

I am including the complete code here to download which you can copy everything and paste in the circuitpython flash drive.

Example Code

This is your current working example for the MCP3208:

import time
import board
import busio
import digitalio

import adafruit_mcp3xxx.mcp3208 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# SPI setup (your wiring)
spi = busio.SPI(clock=board.GP18, MISO=board.GP16, MOSI=board.GP19)

# Chip Select (your CS pin)
cs = digitalio.DigitalInOut(board.GP17)

# MCP3208 setup
mcp = MCP.MCP3208(spi, cs)

# Channels
ch0 = AnalogIn(mcp, MCP.P0) # Channel 0
ch1 = AnalogIn(mcp, MCP.P1) # Channel 1

print("Reading MCP3208 CH0 and CH1...")

while True:
print("CH0:", ch0.value, " | CH1:", ch1.value)
time.sleep(0.5)

Code Explanation

1. SPI Setup

spi = busio.SPI(clock=board.GP18, MISO=board.GP16, MOSI=board.GP19)

This creates the SPI bus used to talk to the ADC chip.

2. Chip Select

cs = digitalio.DigitalInOut(board.GP17)

The chip-select line tells the MCP3208 when the Pico is communicating with it.

3. Create the ADC Object

mcp = MCP.MCP3208(spi, cs)

This initializes the MCP3208 driver object using the SPI bus and chip-select pin.

4. Create Analog Channels

ch0 = AnalogIn(mcp, MCP.P0)
ch1 = AnalogIn(mcp, MCP.P1)

These represent channel 0 and channel 1 on the chip.

5. Read the Values

print("CH0:", ch0.value, " | CH1:", ch1.value)

This prints the current analog reading from both channels.

What Does .value Mean?

AnalogIn.value gives a scaled integer reading. In practice, it is convenient because you do not need to manually talk to SPI registers yourself. You just read the channel object.

For many hobby projects, this is enough to:

  • compare sensor levels
  • read potentiometer positions
  • control brightness or speed
  • monitor changing analog signals

Example 2 - Read More Channels

If you want to use more of the ADC chip, you can create more channels:

import time
import board
import busio
import digitalio

import adafruit_mcp3xxx.mcp3208 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

spi = busio.SPI(clock=board.GP18, MISO=board.GP16, MOSI=board.GP19)
cs = digitalio.DigitalInOut(board.GP17)

mcp = MCP.MCP3208(spi, cs)

ch0 = AnalogIn(mcp, MCP.P0)
ch1 = AnalogIn(mcp, MCP.P1)
ch2 = AnalogIn(mcp, MCP.P2)
ch3 = AnalogIn(mcp, MCP.P3)

while True:
print(
"CH0:", ch0.value,
"CH1:", ch1.value,
"CH2:", ch2.value,
"CH3:", ch3.value
)
time.sleep(0.5)

Example 3 - Read Potentiometers as Percentage

This is often easier to understand than raw ADC values.

import time
import board
import busio
import digitalio

import adafruit_mcp3xxx.mcp3208 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

spi = busio.SPI(clock=board.GP18, MISO=board.GP16, MOSI=board.GP19)
cs = digitalio.DigitalInOut(board.GP17)

mcp = MCP.MCP3208(spi, cs)

ch0 = AnalogIn(mcp, MCP.P0)
ch1 = AnalogIn(mcp, MCP.P1)

while True:
pct0 = (ch0.value / 65535) * 100
pct1 = (ch1.value / 65535) * 100

print("CH0: {:.1f}% | CH1: {:.1f}%".format(pct0, pct1))
time.sleep(0.5)

Common Problems

No readings at all

  • check VDD, VREF, AGND, and DGND
  • confirm SPI wiring
  • confirm the chip-select pin matches your code

Values do not change

  • check whether your analog source is connected to the right channel
  • confirm the sensor or potentiometer is powered correctly
  • verify the channel number in code

Strange or unstable values

  • check breadboard wiring
  • make sure grounds are shared
  • make sure the analog input is not floating

Wrong chip library

If the hardware is MCP3008 but the code imports mcp3208, the tutorial will be confusing. Match the library to the actual chip on your board.

Wiring Multiple MCP3208s

MCP3208 wiring

In this diagram we added a second MCP3208.

Both chips share the same SPI lines:

  • SCK
  • MISO
  • MOSI

The only connection that must be different for each chip is the CS pin.

Example:

  • first MCP3208 -> CS = GP17
  • second MCP3208 -> CS = GP15

This makes SPI a very convenient way to expand the number of analog inputs in a CircuitPython project.

Example 4 - Reading Two MCP3208s

Here is a simple example that reads two channels from each chip, giving you four analog inputs total.

import time
import board
import busio
import digitalio

import adafruit_mcp3xxx.mcp3208 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# SPI setup (shared)
spi = busio.SPI(clock=board.GP18, MISO=board.GP16, MOSI=board.GP19)

# Chip Select pins
cs1 = digitalio.DigitalInOut(board.GP17) # First MCP
cs2 = digitalio.DigitalInOut(board.GP15) # Second MCP (change if you used different pin)

# MCP3208 setup
mcp1 = MCP.MCP3208(spi, cs1)
mcp2 = MCP.MCP3208(spi, cs2)

# Channels from MCP #1
ch0 = AnalogIn(mcp1, MCP.P0)
ch1 = AnalogIn(mcp1, MCP.P1)

# Channels from MCP #2
ch2 = AnalogIn(mcp2, MCP.P0)
ch3 = AnalogIn(mcp2, MCP.P1)

print("Reading 2 MCP3208s (4 channels total)...")

while True:
print(
"MCP1 CH0:", ch0.value,
"| MCP1 CH1:", ch1.value,
"| MCP2 CH0:", ch2.value,
"| MCP2 CH1:", ch3.value
)
time.sleep(0.5)

You can keep expanding this idea with more MCP3208 chips, as long as each one has its own chip-select pin.

Summary

An external ADC such as the MCP3208 is a great way to expand the analog input capability of the Raspberry Pi Pico in CircuitPython.

The key ideas are:

  • use SPI
  • use the correct adafruit_mcp3xxx driver
  • create one AnalogIn object per channel
  • read multiple analog values from one external chip

That makes the Pico much more flexible when a project needs several analog inputs.