Ads1x1x Analog-to-Digital Converter Driver in Rust

ADS1015 measuring voltages in a voltage divider

   

Today I present you an analog-to-digital converter (ADC) Rust driver compatible with the devices ADS1013, ADS1014, ADS1015, ADS1113, ADS1114 and ADS1115.

The devices

The devices are precision, low power, 12/16-bit analog-to-digital converters (ADC) that provide all features necessary to measure the most common sensor signals in an ultra-small package. Depending on the device, these integrate a programmable gain amplifier (PGA), voltage reference,oscillator and high-accuracy temperature sensor.

The devices can perform conversions at data rates up to 3300 samples per second (SPS). The PGA offers input ranges from ±256 mV to ±6.144 V, allowing both large and small signals to be measured with high resolution. An input multiplexer (MUX) allows to measure two differential or four single-ended inputs. The high-accuracy temperature sensor can be used for system-level temperature monitoring or cold-junction compensation for thermocouples.

The devices operate either in continuous-conversion mode, or in a single-shot mode that automatically powers down after a conversion. Single-shot mode significantly reduces current consumption during idle periods. Data is transferred through I2C.

You can buy a module board containing this from AliExpress or eBay.

Using the driver

To use the device from Rust, you have to add the ads1x1x crate to your project as well as a concrete implementation of the embedded-hal traits. For example if you are using the Raspberry Pi running Linux (see driver-examples for bare-metal hardware):

1
2
3
4
5
# Cargo.toml
...
[dependencies]
ads1x1x = "0.2"
linux-embedded-hal = "0.3"

Voltage divider example

In the title picture in this post I created a voltage divider circuit using 3 resistors of equal value and connected the points in the divider to the inputs of the ADC.
A voltage divider is a circuit that can “divide” the voltage and output a voltage in between,
proportional to the resistences in place.

Here is the circuit schematic:
Voltage divider schematic

The resistor values do not matter much but it is nicer to understand if they are all of the same value.

With this setup we should get the reading for +5V on channel A0, the reading for GND on channel A3 and A1 and A2 equally spaced in between (within resistence tolerances).
I get these values:
Channel 0: 1575
Channel 1: 1051
Channel 2: 524
Channel 3: 0

We can calculate the relations and voltage that correspond to each channel if we
assume that 1575 corresponds to 5V.

ChannelFactorVoltage
01575 / 1575=1* 5V=5V
11051 / 1575=0.667* 5V=3.34V
2524 / 1575=0.333* 5V=1.66V
30 / 1575=0* 5V=0V

As you can see, the voltage was divided equally by all resistors.

The program

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
extern crate embedded_hal;
use embedded_hal::adc::OneShot;
extern crate linux_embedded_hal;
#[macro_use(block)]
extern crate nb;
extern crate ads1x1x;

use ads1x1x::{channel, Ads1x1x, SlaveAddr};
use linux_embedded_hal::I2cdev;

fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let mut adc = Ads1x1x::new_ads1015(dev, address);
let values = [
block!(adc.read(&mut channel::SingleA0)).unwrap(),
block!(adc.read(&mut channel::SingleA1)).unwrap(),
block!(adc.read(&mut channel::SingleA2)).unwrap(),
block!(adc.read(&mut channel::SingleA3)).unwrap(),
];
for (channel, value) in values.iter().enumerate() {
println!("Channel {}: {}", channel, value);
}
// get I2C device back
let _dev = adc.destroy_ads1015();
}

This example is also available in the crate sources here.

In the title picture you can see this running on the STM32F3Discovery board and printing the results on an OLED display. You can find the application souce code here.

In the driver-examples repository you can find further examples of using these devices like measuring the output of a digital potentiometer (digipot) and a digital-to-analog converter (DAC).

Where to go from here?

There is much more information and example programs in the crate documentation.
Please give this driver a try and report any issues you may encounter in the issue tracker.
Feedback, suggestions and improvements are gladly welcome.

What’s next?

I have been writing many other platform-agnostic Rust drivers although I am slow to announce them here. If you want to know what I am currently working on you can follow me on github.

Thanks for reading and stay tuned!

Links: Source code - Crate - Documentation

Share