This guide will show you how to use an MCP2221(A) to connect to I2C sensors and breakouts from your desktop PC running Windows, Mac OSX, or Linux. The MCP2221 also allows for general purpose digital input and output (GPIO) for things like buttons and LEDs, analog to digital conversion (ADC), and digital to analog (DAC).

The cool part about this is that you can then use any of the CircuitPython Libraries that have been written for the numerous I2C sensors and breakouts. You can bring that data directly into your PC for any kind of powerful analysis or presentation.

Our breakout uses the MCP2221A chip, but we may refer to it as MCP2221 as the difference are not relevant to using Blinka/CircuitPython libraries and use can use either version of the chip!

CircuitPython and CircuitPython Libraries

As you are going through this guide, keep in mind the difference between CircuitPython and CircuitPython Libraries:

There are various hardware combinations that allow for running CircuitPython and CircuitPython Libraries. In this guide we will not be using the actual CircuitPython firmware. But we will be using CircuitPython Libraries.

CircuitPython Libraries on Personal Computers

This is essentially the same idea as discussed  in the FT232H Guide. How can we directly connect common hardware items like buttons and I2C breakouts to a PC?

The MCP2221 provides another way to do this by utlizing the USB bus. The MCP2221 just makes different trade offs relative to the FT232H. The biggest being no hardware SPI support. But you do gain ADC and DAC support. Also, it's much cheaper than the FT232H.

So you end up with something like this:

Great! Let's get everything setup so we can actually do some fun stuff.

Adafruit MCP2221A Breakout - General Purpose USB to GPIO ADC I2C

PRODUCT ID: 4471
Wouldn't it be cool to drive a tiny OLED display, read a
OUT OF STOCK

The support for the MCP2221 in Blinka utilizes the hidapi library. This in turn relies on a few other things which vary for different OS's. So before we can actually use the MCP2221, we need to get everything setup. See the OS specific sections for what we went through to get things working for each.

Additional Information

Just for reference, here's the README from the hidapi source code repo, which has some install information:

But first try the install instructions on the pages that follow for your OS.

Have Python 3 Installed

We assume you already have Python 3 installed on your computer. Note we do not support Python 2 - it's deprecated and no longer supported!

At your command line prompt of choice, check your Python version with python --version

Install hidapi

From the command line, manually install hidapi with

Download: file
pip3 install hidapi

If the install fails with text that ends with something like:

distutils.errors.DistutilsError: Setup script exited with error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/

then you will need to also install the Microsoft Visual C++ Build Tools. Thanks to @jklem for pointing this out in the forums.

Download it from here (same link as in text):

NOTE: You do not need the full Visual Studio IDE. Just the Build Tools.

  • Scroll down to where it says Tools for Visual Studio 2019.
  • Expand the list to show the sub options.
  • Click the Download button for Build Tools for Visual Studio 2019.

This downloads a .exe file with a name like vs_BuildTools.exe. Run that to install the build tools and then try the pip install again.

Install Blinka

To install Blinka and its dependencies, run:

Download: file
pip3 install adafruit-blinka

Set Environment Variable

You must do this every time before running circuitpython code, you can set it permanently in windows if you like, for now just type into the same cmd window you're using with Python

set BLINKA_MCP2221=1

If you are using Windows Powershell, the syntax is a little different. In that case do:

$env:BLINKA_MCP2221=1

Check Platform was detected

In the same command window you set BLINKA_MCP2221=1 env var, run python and run

import board
dir(board)

at the Python REPL. If you get no errors, and you see a list of all the pins available - you're good to go!

Python 3 Check

We assume you already have Python 3 installed on your computer. Note we do not support Python 2 - it's deprecated and no longer supported!

At your command line prompt of choice, check your Python version with python --version

Install hidapi

From the command line, manually install hidapi with:

Download: file
pip3 install hidapi

Install Blinka

To install Blinka and its dependencies, run:

Download: file
pip3 install adafruit-blinka

Set Environment Variable 

You'll need to set this variable every time before running CircuitPython code. To do this, we set the environment variable to BLINKA_MCP2221.,

You can set the variable by running:

Download: file
export BLINKA_MCP2221="1"

Then, verify that the variable is set by running:

Download: file
echo $BLINKA_MCP2221
Don't forget this step. Things won't work unless BLINKA_MCP2221 is set.

Check that Platform was detected

In the same terminal window you ran export BLINKA_MCP2221="1", run python.

At the REPL, run:

import board

dir(board)

If you get no errors and see a list of all the pins available, you're good to go!

The following shows a typical run through installing and setting things up on Linux.

Install libusb and libudev

Run the following:

Download: file
sudo apt-get install libusb-1.0 libudev-dev

and answer Y to the prompt. This should install libusb and libudev.

Setup udev rules

Use a text editor to create and edit the file /etc/udev/rules.d/99-mcp2221.rules and add the following contents.

Download: file
SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTR{idProduct}=="00dd", MODE="0666"

Here we use nano, so run:

like this:

and add the contents from above:

and then press CTRL-X and Y to save and exit.

The settings will take effect the next time you plug in the MCP2221.

Install hidapi

To install hidapi, run:

Download: file
pip3 install hidapi

Install Blinka

To install Blinka and its dependencies, run:

Download: file
pip3 install adafruit-blinka

Set environment variable

We need to manually signal to Blinka that we have a MCP2221 attached. To do this, we set the environment variable BLINKA_MCP2221. The value doesn't matter, just use 1:

Don't forget this step. Things won't work unless BLINKA_MCP2221 is set.

Run the sanity check.

Now move on to the Post Install Checks section and run the commands there to make sure everything is installed correctly.

Post Install Checks

After going through all the install steps for your OS, run these checks as simple tests to make sure everything is installed correctly. See the rest of the page for some potential hiccups you may run into.

Go ahead and plug in your MCP2221 to a USB port on your PC.

Most of these tests are done via the Python REPL, at the >>> prompt. To get there, simply launch Python:

Download: file
$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Check that hidapi is installed correctly

At the Python REPL, type:

Download: file
import hid
hid.enumerate()

You should get a dump of everything attached to your USB ports.

Check that MCP2221 can be found

At the Python REPL, type:

Download: file
import hid
device = hid.device()
device.open(0x04D8, 0x00DD)

it should run without any errors:

If for some reason the MCP2221 can not be found, you might see something like this:

Check your USB cable connection.

Check environment variable within Python

At the Python REPL, type:

Download: file
import os
os.environ["BLINKA_MCP2221"]

If you get a KeyError it means you did not set the environment variable right:

If you have set it correctly, you'll get a value back:

The logic level is set to 3.3V by default.

Power Pins

  • 5V - this is the 5V power from the USB input.
  • 3V - this is the 3.3V power output from the voltage regulator.
  • GND - this is the common ground for all power and logic.
  • RST - reset pin, pulled high internally, set low to reset

GPIO Pins

  • G0 to G3 - can be used as either digital inputs or outputs, logic level is 3.3V by default but can be changed to 5V

The GPIO pins do not have internal pull-up or pull-down support! If you need pull resistors, add them externally.

I2C Pins

  • SCL - the I2C clock signal, there's a 5.1K pullup resistor on this pin to whatever the logic level is (default 3.3V)
  • SDA - the I2C data signal, there's a 5.1K pullup resistor on this pin to whatever the logic level is (default 3.3V)

UART Pins

  • TX - transmit (out from board)
  • RX - receive (in to board)

The uart is totally separate from the GPIO pins, it's controlled as a USB CDC device not as the USB HID interface so it shows up as a serial COM/tty port like any other USB-to-serial converter

ADC Pins

  • ADC1 to ADC3 - can be used as analog inputs (10 bit)

DAC Pins

  • DAC - there is a single 5 bit DAC which outputs on both of these pins

Logic Level

The default logic level is 3.3V - this is for digital, analog, I2C and UART data. You can change it to 5V by cutting the jumper on the bottom from 3V and soldering it to the 5V side:

All right, now that all that annoying install stuff is done, let's have some fun.

The following sections will provide some basic examples for the main use cases - GPIO, I2C, ADC, and DAC.

Make sure you've set the BLINKA_MCP2221 environment variable.

Installing Libraries for Breakouts

The general process for installing the CircuitPython library you are interested in will be the same as shown in the Python section of the Learn guide for your sensor. Just use pip3.

Digital Output

Let's blink a LED!

Here's the bread board layout. The resistor can be something around 1kOhm. We don't need to make the LED super bright.

First, let's do things interactively so you can see how it all works one line at a time. Start by launching Python:

Download: file
python3

Then, at the Python >>> prompt, enter the following to import the needed modules:

Download: file
import board
import digitalio

Next we'll create our LED digital pin and set the mode to output:

Download: file
led = digitalio.DigitalInOut(board.G0)
led.direction = digitalio.Direction.OUTPUT

And that should be it. You should be able to turn ON the LED with:

Download: file
led.value = True

And turn it OFF with:

Download: file
led.value = False

And here's a complete blink program you can run to make the LED blink forever.

Download: file
import time
import board
import digitalio
     
led = digitalio.DigitalInOut(board.G0)
led.direction = digitalio.Direction.OUTPUT
     
while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)

Save it as something like blink.py and then you can run it with:

Download: file
python3 blink.py

The LED should blink on and off.

Digital Input

Let's read a button!

Here's the bread board layout. Use something like a 10kOhm resistor.

The GPIO pins do not have internal pull-up or pull-down support! If you need pull resistors, add them externally as shown here!

We'll do this interactively also. So launch python:

Download: file
python3

Then, at the Python >>> prompt, enter the following to import the needed modules:

Download: file
import board
import digitalio

And now we create our button digital pin and set it to input.

Download: file
button = digitalio.DigitalInOut(board.G0)
button.direction = digitalio.Direction.INPUT

And that's it. To read the current state of the button use:

Download: file
button.value

This will return False when the button is not pressed and True when it is pressed.

Digtal Input and Output

Ok, let's put those two together and make the button turn on the LED. So we'll use two digital pins - one will be an input (button) and one will be an output (LED).

Here's the bread board layout.

And here's the code.

Download: file
import board
import digitalio

led = digitalio.DigitalInOut(board.G0)
led.direction = digitalio.Direction.OUTPUT

button = digitalio.DigitalInOut(board.G1)
button.direction = digitalio.Direction.INPUT

while True:
    led.value = button.value

Save that to a file with a name like button_and_led.py and then you can run it with:

Download: file
python3 button_and_led.py

and the button should turn on the LED when pressed.

Try to avoid hot plugging I2C sensors. The MCP2221 doesn't seem to like that. Remove USB power first.

Let's talk to an I2C sensor.

We'll use the MSA301 sensor which can read acceleration. Since the MCP2221 and the MSA301 both have STEMMA QT connectors, you have two options for making the connections.

You can either wire everything up on a breadboard, like this:

Or use a STEMMA QT cable:

STEMMA QT / Qwiic JST SH 4-Pin Cable - 200mm Long

PRODUCT ID: 4401
This 4-wire cable is a little over 200mm / 7.8" long and fitted with JST-SH female 4-pin connectors on both ends. Compared with the chunkier JST-PH these are 1mm pitch instead of...
$0.95
IN STOCK

STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long

PRODUCT ID: 4210
This 4-wire cable is a little over 100mm / 4" long and fitted with JST-SH female 4-pin connectors on both ends. Compared with the chunkier JST-PH these are 1mm pitch instead of...
$0.95
IN STOCK

STEMMA QT / Qwiic JST SH 4-Pin Cable

PRODUCT ID: 4399
This 4-wire cable is 50mm / 1.9" long and fitted with JST SH female 4-pin connectors on both ends. Compared with the chunkier JST PH these are 1mm pitch instead of 2mm, but...
$0.95
IN STOCK

Install MSA301 Library

To install the MSA301 library, run the following:

Download: file
sudo pip3 install adafruit-circuitpython-msa301

Note that this step is the same as shown in the main MSA301 guide. You would do the same general process for any other sensor with a CircuitPython library.

Example Code

And then we can run the example from the library. Download it from here:

save it as msa301_simpletest.py and run it with:

Download: file
python3 msa301_simpletest.py

Pick up the board and spin it around. You should see the values change:

Let's read an analog signal!

For this, we'll use a small 10k trim pot to set up a voltage divider. Here's the wiring diagram:

And here's the code:

Download: file
import time
import board
from analogio import AnalogIn

knob = AnalogIn(board.G1)

def get_voltage(raw):
    return (raw * 3.3) / 65536

while True:
    raw = knob.value
    volts = get_voltage(raw)
    print("raw = {:5d} volts = {:5.2f}".format(raw, volts))
    time.sleep(0.5)

Save that as something like adc.py and then run it with:

Download: file
python3 adc.py

Spin the knob and the values should change.

Note that even though the MCP2221's ADC is only 10 bits, the value is scaled to 16 bits to comply with the CircuitPython API.

Let's generate an analog signal!

Well, don't get too excited. There's only a single DAC and it's a whopping 5 bits! So don't expect super awesome high fidelity audio block rockin' beats or anything.

Here's a simple sine wave generator you can try:

Download: file
import time
import math
import board
from analogio import AnalogOut

dac = AnalogOut(board.G3)

while True:
    dac.value = int((2**16 - 1) * (0.5 + 0.5 * math.sin(time.monotonic()*10)))

Save that as something like dac.py and run it with:

Download: file
python3 dac.py

And connect the G3 pin to an oscilloscope. You should see something like this:

Note that even though the MCP221's DAC is only 5 bits, you set it using a 16 bit value to comply with the CircuitPython API.

But wait! There's more!

The MCP2221 also provides a USB-to-UART bridge capability. When you plug in the MCP2221, it will show up as two devices:

  • HID Device - This is what we used for GPIO/I2C/SPI/ADC/DAC
  • CDC Device - This is how you use the UART

We've already covered all the cool stuff the HID interface provides. So it's this second item were are interested in here. It is how you can use the TX and RX pins on the MCP2221 breakout.

Install pySerial

We will use the Python library pySerial to access the MCP2221's UART. So, first install that. Here's a link to installation instructions from the libraries homepage:

Find COM port in Linux

After plugging in the MCP2221, run dmesg and look in the output.

Download: file
dmesg | tail

Like this:

Look for something with a name like ttyACMx. An entry for that device will have been created in your /dev folder. So in your code, you'll want to use /dev/ttACMx (replace x with the number). In the example output above, the MCP2221's UART is available on /dev/ttyACM0. It may be slightly different on your specific linux set up.

Find COM port in Windows

After plugging in the MCP2221, look in Device Manager under Ports (COM & LPT). If you already have a lot of entries there, it may help to also look before plugging in the MCP2221. That way you can compare and look for the new entry.

It should show up as something like this:

In the above example, it shows up as COM3. That's the value you'll use in your code.

UART Example with GPS

In this example we show how to use the MCP2221's UART to talk to an Ultimate GPS Breakout Module. Here is how to wire the GPS module to the MCP2221.

Once you've found your COM port and have the MCP2221 and GPS breakout wired as above, you can then follow the example here:

NOTE: You will have to make some changes to the code.

You will want to follow the same general information as for the Raspberry Pi. So comment / uncomment the code as described so that you are using pySerial. Then, when you open the serial port, use the information above for your specific COM port location.

For example, on linux it will generally look the same as the Raspberry Pi example:

Download: file
import serial
uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10)

On Windows, you'll want use something like this:

Download: file
import serial
uart = serial.Serial("COM3", baudrate=9600, timeout=10)

And that should be it. If you run the example, it should use the MCP2221's UART to talk to the GPS and evenutally, once it gets a fix, you should see GPS output.

FAQ & Troubleshooting

There's a few oddities when running Blinka/CircuitPython on linux. Here's a list of stuff to watch for that we know of!

This FAQ covers all the various platforms and hardware setups you can run Blinka on. Therefore, some of the information may not apply to your specific setup.

Update Blinka/Platform Libraries

Most issues can be solved by forcing Python to upgrade to the latest blinka / platform-detect libraries. Try running

sudo python3 -m pip install --upgrade --force-reinstall adafruit-blinka Adafruit-PlatformDetect

Getting an error message about "board" not found or "board" has no attribute

Somehow you have ended up with either the wrong board module or no board module at all.

DO NOT try to fix this by manually installing a library named board. There is one out there and it has nothing to do with Blinka. You will break things if you install that library!

The easiest way to recover is to simply force a reinstall of Blinka with:
python3 -m pip install --upgrade --force-reinstall adafruit-blinka

 

Mixed SPI mode devices

Due to the way we share an SPI peripheral, you cannot have two SPI devices with different 'mode/polarity' on the same SPI bus - you'll get weird data

95% of SPI devices are mode 0, check the driver to see mode or polarity settings. For example:

Why am I getting AttributeError: 'SpiDev' object has no attribute 'writebytes2'?

This is due to having an older version of spidev. You need at least version 3.4. This should have been taken care of when you installed Blinka, but in some cases it does not seem to happen.

To check what version of spidev Python is using:

$ python3
Python 3.6.8 (default, Oct 7 2019, 12:59:55)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import spidev
>>> spidev.__version__
'3.4'
>>>

If you see a version lower then 3.4 reported, then try a force upgrade of spidev with (back at command line):

sudo python3 -m pip install --upgrade --force-reinstall spidev

No Pullup/Pulldown support on some linux boards or MCP2221

Some linux boards, for example, AllWinner-based, do not have support to set pull up or pull down on their GPIO. Use an external resistor instead!

Getting OSError: read error with MCP2221

If you are getting a stack trace that ends with something like:

return self._hid.read(64)
File "hid.pyx", line 122, in hid.device.read
OSError: read error

Try setting an environment variable named BLINKA_MCP2221_RESET_DELAY to a value of 0.5 or higher.

 

Windows:

set BLINKA_MCP2221_RESET_DELAY=0.5

 

Linux:

export BLINKA_MCP2221_RESET_DELAY=0.5

 

This is a value in seconds to wait between resetting the MCP2221 and the attempt to reopen it. The reset is seen by the operating system as a hardware disconnect/reconnect. Different operating systems can need different amounts of time to wait after the reconnect before the attempt to reopen. Setting the above environment variable will override the default reset delay time, allowing it to be increased as needed for different setups.

I can't get neopixel, analogio, audioio, rotaryio, displayio or pulseio to work!

Some CircuitPython modules like may not be supported.

  • Most SBCs do not have analog inputs so there is no analogio
  • Few SBCs have neopixel support so that is only available on Raspberry Pi (and any others that have low level neopixel protocol writing
  • Rotary encoders (rotaryio) is handled by interrupts on microcontrollers, and is not supported on SBCs at this time
  • Likewise pulseio PWM support is not supported on many SBCs, and if it is, it will not support a carrier wave (Infrared transmission)
  • For display usage, we suggest using python Pillow library or Pygame, we do not have displayio support

We aim to have, at a minimum, digitalio and busio (I2C/SPI). This lets you use the vast number of driver libraries

For analog inputs, the MCP3xxx library will give you AnalogIn objects. For PWM outputs, try the PCA9685. For audio, use pygame or other Python3 libraries to play audio.

Some libraries, like Adafruit_CircuitPython_DHT will try to bit-bang if pulsein isn't available. Slow linux boards (<700MHz) may not be able to read the pins fast enough), you'll just have to try!

Help, I'm getting the message "error while loading shared libraries: libgpiod.so.2: cannot open shared object file: No such file or directory"

It looks like libgpiod may not be installed on your board.

Try running the command: sudo apt-get install libgpiod2

All Raspberry Pi Computers Have:

  • 1 x I2C port with busio (but clock stretching is not supported in hardware, so you must set the I2C bus speed to 10KHz to 'fix it')
  • 2 x SPI ports with busio
  • 1 x UART port with serial - note this is shared with the hardware console
  • pulseio.pulseIn using gpiod
  • neopixel support on a few pins
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

Google Coral TPU Dev Boards Have:

  • 1 x I2C port with busio
  • 1 x SPI ports with busio
  • 1 x UART port with serial - note this is shared with the hardware console
  • 3 x PWMOut support
  • pulseio.pulseIn using gpiod
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)

Orange Pi PC Plus Boards Have:

  • 1 x I2C port with busio
  • 1 x SPI ports with busio
  • 1 x UART port with serial
  • pulseio.pulseIn using gpiod
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

Orange Pi R1 Boards Have:

  • 1 x I2C port with busio
  • 1 x SPI port with busio
  • 1 x UART port with serial
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

Odroid C2 Boards Have:

  • 1 x I2C port with busio
  • No SPI support
  • 1 x UART port with serial - note this is shared with the hardware console
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

DragonBoard 410c Boards Have:

  • 2 x I2C port with busio
  • 1 x SPI port with busio
  • 1 x UART port with serial
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

NVIDIA Jetson Nano Boards Have:

  • 2 x I2C port with busio
  • No SPI support without reflashing the board
  • 2 x UART port with serial - note one of these is shared with the hardware console
  • No NeoPixel support
  • No AnalogIn support (Use an MCP3008 or similar to add ADC)
  • No PWM support (Use a PCA9685 or similar to add PWM)

FT232H Breakouts Have:

  • 1x I2C port OR SPI port with busio
  • 12x GPIO pins with digitalio
  • No UART
  • No AnalogIn support
  • No AnalogOut support
  • No PWM support

If you are using Blinka in FT232H mode, then keep in mind these basic limitations.

  • SPI and I2C can not be used at the same time since they share the same pins.
  • GPIO speed is not super fast, so trying to do arbitrary bit bang like things may run into speed issues.
  • There are no ADCs.
  • There are no DACs.
  • UART is not available (its a different FTDI mode)

MCP2221 Breakouts Have:

  • 1x I2C port with busio
  • 4x GPIO pins with digitalio
  • 3x AnalogIn with analogio
  • 1x AnalogOut with analogio
  • 1x UART with pyserial
  • No PWM support
  • No hardware SPI support

If you are using Blinka in MCP2221 mode, then keep in mind these basic limitations.

  • GPIO speed is not super fast, so trying to do arbitrary bit bang like things may run into speed issues.
  • UART is available via pyserial, the serial COM port shows up as a second USB device during enumeration

Downloads

This guide was first published on Dec 22, 2019. It was last updated on Dec 22, 2019.