Work with IoT devices on a standard computer using CircuitPython and the Adafruit MCP2221 breakout board

The MCP2221 and FTD2302 are breakout boards that have a USB on one side and a series of structured and unstructured I/O pins on the other. The breakout boards have various GPIO, I2C other pins on them that you attach sensors, LEDs, or other components.  This lets you indirectly attach GPIO inputs and outputs to a regular computer.  

Adafrut's CircuitPython Blinka library emulates the CircuitPython IOT board interface that is the root of many of the Circuitpython distributions.  That interface sits between standard Python and computer-attached I/O devices.  It was originally designed to support the I/O pins of the Linux-based Raspberry Pi. In our case, it sits between standard Python and the I/O devices that exist on the other sides of the MCP2221 and FTD2302 breakout boards.

The Stack

Python Program
Python 3.x Runtime
CircuitPython Framework
Circuit Python Blinka board adapter
Breakout Board Definition
Individual Drivers for devices attached to the breakout board
USB cable
Breakout board
Physically attached devices

Pros and Cons

There are a couple advantages to this approach.

  1. You can run your control code with the comfort of all your desktop tools and computing power.  Python has restrictions when running directly on Microcontrollers like the ESP32, PICO, or SAMD series of processors.
  2. You can run much of the same code on your development environment as you do on an IoT device as part of a faster edit/test/code cycle.
There are a couple disadvantages.
  1. It will not be near as real-time performant as running the code directly on a microcontroller.
  2. Tightly choreographed operations may be difficult due to system and communication channel overhead.

System Under Test

I purchased the Adafruit MCP2221 breakout board at Microcenter and had an I2C 16x2 LCD display from a previous project.



Hooking it up

The LCD Pi Plate is actually a standard LCD module plus 5 switches mounted on top of an I2C adapter board.  The adapter board has an I2C chip that essentially multiplexes LCD control lines and 5 switch control lines onto the I2C bus as a bit stream.  This means the breakout board only needs two digital lines to talk with all the features of the LCD Pi Plate.

This diagram shows the 4 pins that need to be connected between the LCD Raspberry Pi v1 sized connector and the pins on the Adafruit MCP2221 breakout board.

This hookup will not work for SPI devices or devices that require all the pins.  There be enough I/O lines on the breakout board for classic LCD panels that are a bunch of extra wiring.

Sample Source Code

This program assumes you have installed the Python libraries it has referenced and that you set the expected environment variable.  It assumes that you have connected your I2C LCD to an MCP2221 breakout and that the breakout is attached to your machine over a USB connection.

The source is located at at the time of this writing.  

# setting up windows environment
# pip3 install hdapi
# pip3 install adafruit-blinka
# pip3 install adafruit-circuitpython-charlcd

#   Linux set BLINKA_MCP2221=1
#   Powershell $env:BLINKA_MCP2221=1
# python3 to bring up the REPL and then paste the the rest of the file
# starting on the end near the edge of the board
# Pin 02 exterior line - 5v corner   outside edge
# Pin 04 exterior line - skip        outside edge
# Pin 06 exterior line - GND 3rd pin outside edge
# Pin 01 interior line corner - skip
# Pin 03 interior line SDA
# Pin 05 interior line SLC
import os
import board
import hid
import adafruit_character_lcd.character_lcd_rgb_i2c as character_lcd
import time

# verify should = 1
    print("BLINKA_MCP2221 set correctly.  Well Done!")
except ValueError:
    print("**** ABORT! BLINKA_MCP2221 not set")
# describe the board
# dir(board)
# prints the api for the board
# help(board)

# Should really verify device
# This actually returns a list object
device = hid.device(), 0x00DD)

start_connect = time.perf_counter()
# Modify this if you have a different sized Character LCD
lcd_columns = 16
lcd_rows = 2
# Initialise I2C bus.
i2c = board.I2C()
start_connect = time.perf_counter()
# Point the driver at the bus / device
lcd = character_lcd.Character_LCD_RGB_I2C(i2c, lcd_columns, lcd_rows)
end_connect = time.perf_counter()
print("conect time: " + str(end_connect - start_connect))

# I have the RGB backlit Adafruit device
# Set LCD color to red
lcd.color = [100, 0, 0]
# Print two line message
start_hello = time.perf_counter()
lcd.message = "Hello\nCircuitPython"
end_hello = time.perf_counter()
print("draw hello time: " + str(end_hello - start_hello))
# make cursor blink
lcd.blink = True
lcd.blink = False
lcd.message = "Goodbye"
lcd.cursor_position(8, 1)
lcd.message = "over here"
lcd.color = [0, 0, 0]

print("this is how you read the buttons")
# down_button, right_button, up_button, left_button, select_button

Problems with this Code

  1. Writing "Hello\nWorld" to the display took 8 seconds. Not sure if it is the nature of the connection, an issue with the LCD plate, or something else.  I will update this blog if I ever figure out why.
  2. I got over 10 characters per second with the same hardware but this sample program and a different I2C driver and I2C backpack type for the LCD D Hylands LCD Python drivers for LCDs with PCF8574 I2C backpacks

Gratuitous Raspberry Pi connector diagram

This diagram is important if you are repurposing an LCD originally intended for a Raspberry Pi

Other Host adapters supported by Blinka

As of 2023 02, The Blinka PlatformDetect supports shows the following boards are supported.
  1. Adafruit MCP2221A   Breakout Board
  2. Adafruit FT232H Breakout Board
  3. Several RP2040 devices can run u2if Firmware including a Pico The u2if firmware project seems to be dead
  4. Great FET One from Great Scott Gadgets
  5. BinHo Nova Multi Protocol USB Adapter

Revision History

2023 01 


Popular posts from this blog

Understanding your WSL2 RAM and swap - Changing the default 50%-25%

Installing the RNDIS driver on Windows 11 to use USB Raspberry Pi as network attached

DNS for Azure Point to Site (P2S) VPN - getting the internal IPs