Electronics Software Development

Adding Analog Inputs To Your Raspberry Pi

Raspberry Pi & Analog Graphic
Written by John Woolsey

Skill Level: Intermediate

Table Of Contents

Introduction

This tutorial will teach you how to add analog inputs to your Raspberry Pi. This will be accomplished by interfacing an MCP3008 analog-to-digital converter (ADC) to the Raspberry Pi and using CircuitPython to access the ADC’s analog channels.

A basic understanding of electronics and programming is expected along with some familiarity with the Raspberry Pi platform. If you are new to Raspberry Pi or CircuitPython, or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On A Raspberry Pi and Getting Started With CircuitPython On Raspberry Pi With Blinka tutorials before proceeding with this one. In addition, this tutorial will use a solderless breadboard to build a circuit from a schematic diagram. The All About Circuit’s Understanding Schematics, SparkFun’s How to Read a Schematic, Core Electronics’ How to Use Breadboards, and Science Buddies’ How to Use a Breadboard guides are good resources for learning how to translate a schematic to a breadboard.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

Background Information

An analog voltage signal is a continuous voltage waveform that varies over time. That is opposed to a digital voltage signal that only has discrete voltage levels, or values. Many sensors that measure a physical quantity (such as light, sound, temperature, pressure, etc.) provide analog voltage signals. There are also various circuits that produce analog signals where we may want to measure the voltage across a particular component. Some examples of these might be a function generator, the output of a potentiometer, or the voltage level of a battery.

The Raspberry Pi is an amazing piece of hardware. It, along with the Arduino, are the de facto standard development boards used among the maker community. The Raspberry Pi can do a lot of things that are not possible on the Arduino, but there is one popular feature that is available on the Arduino that the Raspberry Pi does not have, that is analog inputs. However, we can add this capability to the Raspberry Pi by interfacing an external analog-to-digital converter (ADC) chip.

An ADC is an electronic component that converts analog voltage signals into digital values. These values can then be read by the host system. In this tutorial, we will use a stand-alone ADC integrated circuit, the MCP3008 by Microchip, and interface it to the Raspberry Pi via the SPI bus. The MCP3008 is an 8-channel 10-bit ADC, meaning it provides 8 individual analog inputs at 10-bit resolution. It also has the capability of running in a pseudo-differential mode where the single-ended inputs can be paired together to provide differential inputs, the voltage difference of the pairs. We will use both the single-ended and pseudo-differential modes in this tutorial.

My development board is the Raspberry Pi 3 Model B running the Raspberry Pi OS operating system. I am also using an optional GPIO breakout board to make the wiring between the Raspberry Pi and the breadboard a little easier. If you are using a different Raspberry Pi model or a different OS that is similar to Raspberry Pi OS, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Building The Circuit

Before connecting any circuitry to your Raspberry Pi, shutdown and disconnect it from power. This avoids accidental damage during wiring.

Place the components and wire up the circuit on a breadboard according to the schematic diagram shown below and then connect it to your Raspberry Pi.

Schematic Diagram Of RaspberryPi & MCP3008 Circuit
Schematic Diagram Of RaspberryPi & MCP3008 Circuit

The circuit should look similar to the one shown below once completed. Note, I am using a GPIO to breadboard interface board to make the wiring simpler, but this is not strictly necessary. I also used two 50 KΩ panel mountable potentiometers in my circuit because that is what I had available. Any trim or panel mountable potentiometers between 1 KΩ and 100 KΩ should work just fine. However, keep in mind, the lower the resistance values, the more power they will consume.

Completed MCP3008 Circuit Using Optional Raspberry Pi GPIO Breakout Board
Completed MCP3008 Circuit Using Optional Raspberry Pi GPIO Breakout Board

Once the circuit is built, connect power to your Raspberry Pi and boot it up.

Installing The Libraries

I assume you are already comfortable installing and using the Raspberry Pi OS so I won’t go into those details, but it is safe to say, you should be running a very recent and stable OS version.

We will now install the libraries required by our program. Since we will be creating a CircuitPython program, the first library we need to install/upgrade is Blinka itself. Open a terminal window on your Raspberry Pi and execute the following command.

$ pip3 install --upgrade Adafruit-Blinka

Fortunately, there is already a CircuitPython library available for use with the MCP3008 ADC, named Adafruit CircuitPython MCP3xxx. Install this library as well.

$ pip3 install adafruit-circuitpython-mcp3xxx

Writing The Program

Open your favorite text editor on the Raspberry Pi and create a CircuitPython program named mcp3008.py with the code shown below.

#!/usr/bin/env python3

from time import sleep
import board
import busio
import digitalio
import adafruit_mcp3xxx.mcp3008 as MCP3008
from adafruit_mcp3xxx.analog_in import AnalogIn
import RPi.GPIO

SPI = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
MCP3008_CS = digitalio.DigitalInOut(board.D22)
MCP3008_SPI = MCP3008.MCP3008(SPI, MCP3008_CS)
ADC_CH0 = AnalogIn(MCP3008_SPI, MCP3008.P0)
ADC_CH1 = AnalogIn(MCP3008_SPI, MCP3008.P1)
ADC_DIFF = AnalogIn(MCP3008_SPI, MCP3008.P1, MCP3008.P0)

print("Press CTRL-C to exit.")
try:
    while True:
        print(f"ADC  CH0: {ADC_CH0.voltage:4.2f} V ({ADC_CH0.value:5d})")
        print(f"ADC  CH1: {ADC_CH1.voltage:4.2f} V ({ADC_CH1.value:5d})")
        print(f"ADC DIFF: {ADC_DIFF.voltage:4.2f} V ({ADC_DIFF.value:5d})")
        print()
        sleep(1)
except KeyboardInterrupt:
    RPi.GPIO.cleanup()
    print("\nCleaned up GPIO resources.")

Let’s take a look at some of the more interesting parts of the code.

Lines 7-8 import the relevant code needed from the adafruit-circuitpython-mcp3xxx library.

Lines 11-13 establish the SPI bus and enable the connection to the external MCP3008 chip.

Lines 14 and 15 create two single-ended analog input channels. Line 16 creates one differential input channel (CH1-CH0) using those same single-ended channels. These channels are then read later in the program.

Lines 21-23 print the channel values in both register (16-bit integer) and voltage representations.

If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.

Save your program when you are done editing.

Running And Testing The System

Now that our circuit is built and our software is written, it is time to run and test our program.

Run the following command to make the CircuitPython program an executable

$ chmod a+x mcp3008.py

and then run the program.

$ ./mcp3008.py

You should see the following printed to the screen.

Press CTRL-C to exit.
ADC  CH0: 1.00 V (19776)
ADC  CH1: 2.50 V (49600)
ADC DIFF: 1.50 V (29760)

The analog values will be printed to the screen once every second. The numbers in parentheses are the 16-bit integer representations of the measured voltages.

Adjust the potentiometers and watch the reported analog values changing. The DIFF channel will display the difference between CH1 and CH0. Please note that the adafruit-circuitpython-mcp3xxx library does not register negative values, so if CH1 has a lower voltage than CH0, a value of 0.00 will be shown.

Press CTRL-C to exit the program when you are done.

Additional Resources

The following is a list of additional resources you may find helpful.

Summary

In this tutorial, we learned how to add analog inputs to our Raspberry Pi by interfacing a stand-alone MCP3008 ADC using CircuitPython.

Specifically, we learned

  • what an analog-to-digital converter is and why we may want to use it,
  • how to construct a circuit with an MCP3008 ADC and connect it to the Raspberry Pi,
  • how to install the appropriate libraries to enable MCP3008 support, and
  • how to write and execute a CircuitPython program on the Raspberry Pi that accesses the connected MCP3008’s analog input channels.

The final source code and schematic used for this tutorial are available on GitHub. The GitHub version of the code is commented to include additional information, such as the program’s description, circuit connections, library references, code clarifications, and other details. The comments are also Sphinx compatible in case you want to generate the code documentation.

Thank you for joining me along this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

9 Comments

  • Many thanks John. I am an elderly Ham-Radio Operator and was looking to build my own Raspi-based Antenna Controller and was having trouble with the analog aspects. So many of the articles are either ancient or bug-ridden; but with yours it all came clear. It is working reliably now. Thanks again
    Graham a.k.a. G4FUA

  • Thanks for the guide. I’m curious if I’d be able to drive more than one MCP3008 to expand this to 16 or 24 channels (ultimately 64 channels as I’m working on a home panel meter). Would driving all of them off of the SPIO pins and then connecting each unit’s SHDN output to a DIO port and reading them accordingly work?

    • I believe that would work just fine. You can have many devices interfaced to a single SPI bus, you just need different select pins (SHDN) for each device.

  • I believe the MCP3008 will accept 5V, does the PI not provide 5V also? It seems better to use the 5V, since that will give a wider range of input voltages. Just wondering why that wasn’t done, because I am thinking of porting a project from Arduino to the PI, and it’s reference is 5V which would mean re-doing some calculations for the 24V sampling I am doing.

    • The supply voltage (Vdd) of the MCP3008 is connected to 3.3 V in order for its SPI pins to communicate directly, using the same voltage, with the Raspberry Pi. According to the MCP3008 datasheet, the reference voltage (Vref) pin can not be supplied a larger voltage than Vdd. If you want to use a reference voltage of 5 volts, I suggest you use a level shifter for the SPI pins.

      Another option might be to use a voltage divider on the analog input to translate your original 24 V samples down to the 3.3 V currently used by the MCP3008 in this design. 1.6K and 10K resistors should work in this scenario.

    • The idea is vers simple , you can use for every o/p à transistor PNP and choose its base resistor to use the transistor in SATURATION mode , and connect the COLLECTOR voltage TO 5V , and I/P signal to its BASE with 3,3V.

  • Thank you for this tutorial. I still have my Timex/Sinclair 2068. Back in those days I put a Timex/Sinclair 1000 in a stage lighting controller, with A/D’s and D/A’s to communicate with the TS1000.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.