Out of the box, the Raspberry Pi lacks an analog input. This puts it at a disadvantage compared to microcontroller-based boards like the Arduino.

But don’t despair: there are plenty of options to consider. Get up and running with Raspberry Pi and an external ADC.

A photograph of an ADC

Why Add Inputs?

The real world is full of phenomena that, if you have the right circuitry, can be easily described using a voltage. Get those voltages into digital form, and you’re able to record them, manipulate them, and use them to control other parameters and devices.

You might be looking to monitor the moisture of your soil, the temperature of your greenhouse, or the weight of your hamster. You might be looking to add a volume control to your Pi, build an entire bank of faders, or design a joystick from scratch. The possibilities are, more or less, limitless.

a table from the ADS datasheet

Options for ADCs

So, which ADC is best for beginners?

Among the most popular and straightforward options are theMCP3004(andMCP3008) chips from Microchip. You’ll get four (or eight) channels of 10 bits each, which can read up to 200 kSPS. On the other hand, there are the ADS111x devices from Texas Instruments, which read 16 bits at 860 SPS. So, there’s a tradeoff between speed and precision (and, naturally, price).

A screenshot of the Raspberry Pi Configuration Window

Many microcontrollers come with built-in ADCs.The ATMega you find on the average Arduinowill offer several 10-bit channels, on top of everything else. This is what allows the Arduino to provide analog inputs where the Raspberry Pi can’t. If you already have an Arduino involved in your setup, and 10 bits is enough fidelity, then this might actually be the easiest way to go.

Here, we’ll keep it simple, with an ADS1115 from Adafruit.

What Is a Programmable Gain Amplifier?

This chip comes with a few interesting features, including a Programmable Gain Amplifier (PGA). This will let you set the desired range of values digitally, down to a fraction of a volt. With the number of values that 16 bits can represent, this will allow you to detect differences of just a few microvolts.

The advantage here is that you can change the gain midway through the program. Other chips, like the MCP3004, take a different approach; they come with an extra pin, to which you can supply a reference voltage.

a screenshot of the RPi console

What About Multiplexing?

A multiplexer (or mux) is a switch that lets you read many inputs using a single ADC. If your ADC chip comes with many input pins, then there’s some internal multiplexing going on. The ADS1115’s mux allows for four inputs, which you can select via the internal registers.

Dealing With Registers

The ADS1115 provides these options, and a few more besides. you may deal with the multiplexer, adjust the gain, activate the built-in comparator, change the sample rate, and put the device into low-power sleep mode, all by flipping a few switches.

But where are those switches? They’re inside the package, in the form of very small bits of memory calledregisters. To activate a given feature, you just need to set the relevant bit to a 1, rather than a 0.

Looking atthe ADS111x datasheet, you’ll find that these models come with four registers, including the configuration registers that govern the device’s behavior.

For example, bits 14 to 12 control the multiplexer. Using these three bits, you can select from eight configurations. The one you’ll want here is “100”, which will give the difference between input zero and ground. Bits 7 to 5, on the other hand, govern the sample rate. If you want the maximum of 860 samples per second, you could set these to “111”.

Once you know which options to set, you’ll have two bytes to send to the ADC. If you later want to set a single bit here or there, then you can deal with them individually using bitwise operators.

Here’s where it might get confusing. In this case, the binary isn’t representing a value, but the values of individual switches. You could express these variables as one big number, in decimal or in hexadecimal. But if you want to avoid headaches, you should stick to the binary version, which is easier to read.

Wiring It Up

You can plug this device straight into the breadboard. The positive voltage input will accept anywhere between 2 and 5.5v, which means that the 3.3v rail on the Raspberry Pi will work nicely.

Wire the SDA and SCL inputs to counterparts on the RPi, and do the same things with the ground and 3.3v. Get a potentiometer between the ground and voltage lines, and put the middle lead into the first input of the ADC. That’s all you need to get going!

Dealing With I2C

Different ADCs work via different protocols. In the case of our ADS1115,we’re going to be using I2C.

The following example will interact with the ADC using Python. But before you do that, you’ll need to set it up. Recent versions of Raspberry Pi OS have made this very simple. Head toPreferences > Raspberry Pi Configuration. Then, from theInterfacestab, switchI2Con.

To check everything is working, open a terminal and run:

This command will output a grid. Assuming everything is working, and you’ve wired it up correctly, you’ll see a new value appear in the grid. This is the address of your ADC. Bear in mind here that it’s a hexadecimal value, so you need to prefix it with“0x”when you use it in the code below. Here, it’s0x48:

Once you have the address, you can use the SMBus library to send I2C commands. You’ll be dealing with two methods here. The first iswrite_word_data(), which accepts three arguments: the device address, the register you’re writing to, and the value you want to write.

The second isread_word_data(), which accepts just the device address and the register. The ADC will be continuously reading voltages and storing the result in the conversion register. With this method, you’re able to retrieve the contents of that register.

you’re able to beautify the result a little bit, and then print it. Before you go back to the start of the loop, introduce a short delay. This will ensure you’re not overwhelmed with data.

You’re just about done. Map the range of values you’re getting to the one you prefer, and then truncate to the desired number of decimal places. you’re able to tailor the print function so that you only print a new value when it’s different from the last value. If you’re unsure aboutmax,min, andround, you cancheck out our list of the 20 most important Python functions!

Dealing With Noise

Now, unless your setup is super, super neat and tidy, you’ll notice some noise. This is the inherent downside of using 16 bits rather than just ten: that little bit of noise will be more perceptible.

By tying the adjacent input (input 1) to ground, and switching the mode so that you’re comparing inputs one and two, you can get much more stable results. You could also swap out those long, noise-collecting jumper cables for small ones, and add a few capacitors while you’re at it. The value of your potentiometer can make a difference, too.

There are also software options. You might create a rolling average, or simply disregard small changes. The downside there is that extra code will impose a computational cost. If you’re writing conditional statements in a high-level language like Python, and taking thousands of samples every second, these costs will compound rapidly.

Go Further With Many Possible Next Steps

Taking readings via I2C is pretty straightforward and the same is largely true of other methods, like SPI. While it might seem that there are big differences between the available ADC options, the truth is that once you’ve got one of them working, it’s easy to apply the knowledge to the others.

So, why not take things further? Tie multiple potentiometers together, or try reading light, sound, or temperature. Expand the controller you’ve just made, and create a Raspberry Pi setup that’s truly hands-on!