Connecting an Analog Sensor to a Raspberry Pi (Python)

alt text

This device kit seeks to display how simple Avimesa Gadget can make IoT projects and is intended for a Raspberry Pi. In this tutorial, we’ll use the Python (3) programming language and we’re going to get friendly with the subprocess module in order to drive (or control) Gadget. We’ll also learn how to hook up an ADC (analog-to-digital converter) to an arbitrary sensor with an analog output and a Raspberry Pi. The ADC we’ll be using is the MCP3008-I/P. We should note that the MCP3008-I/P uses SPI for communication to its controller (in our case a Raspberry Pi). The last piece of information we’ll need before getting started is that we’ll be using another Python module for ease of access to the Raspberry Pi’s peripherals (in our case, SPI): gpiozero. The gpiozero module is the recommended Python module for controlling the Raspberry Pi’s peripherals.

Components

(1) From the CLI, run sudo raspi-config and enable SPI via Interfacing Options > P4 SPI > Yes. Next, reboot: sudo reboot. Finally, verify /dev/spidev0.0 is present in the filesystem. If you run into any issues, please visit this page for help troubleshooting.

(2) If you need help flashing an SD card with a Raspbian image, please visit here.

(3) To install the Python module gpiozero, you’ll first want to make sure your repository list is up to date by running sudo apt update. Second, install the module for Python (3) using the following command: sudo apt install python3-gpiozero. If you run into any issues, please visit here for help troubleshooting.

Hardware Setup

First, let’s wire up our MCP3008. Following is the pinout for the MCP3008, obtained here, and the associated Raspberry Pi (PX) and analog sensor (AX) pin connections: alt text NOTE: The Raspberry Pi pin connections are from the Raspberry Pi 40 pinout header (Not the physical Broadcom connections). This link contains a great representation of the Raspberry Pi 40 pinout header and the associated Broadcom connections.

The analog sensor connections are (usually) simple and can be determined via the sensor’s datasheet. For the soil humidity sensor I’m using: connect P0 (3V3) and P6 (GND) to the specified (+) and (-) pins respectively and connect the analog out to A0 of the MCP3008.

Software Setup

The script:

import subprocess

    from time import sleep
    from gpiozero import MCP3008, DigitalOutputDevice

    def get_soil_moisture():
        # gpiozero has a MCP3008 implementation that
        # has SPI under the hood
        adc = MCP3008(channel=0)
        sleep(.1)

        r_val = adc.value
        adc.close()

        # Values determined through sensor
        # calibration and through usage of the
        # equation of a line
        r_val = 100.0 * (-1.33 * r_val + 1.33)

        return r_val

    # Use your own Avimesa credentials here
    gadget_args = ['avmsagadget',
                   '-i',
                   'DEADBEEFDEADBEEFDEADBEEFDEADBEEF',
                   'DEADBEEFDEADBEEFDEADBEEFDEADBEEF']

    # Wrap Gadget using Popen class
    gadget = subprocess.Popen(gadget_args,
                              stdin=subprocess.PIPE,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE)

    # Let's use Gadget's AUTORUN feature
    # to automatically send sensor data
    gadget.stdin.write(b'set AUTORUN = 1\n')
    gadget.stdin.flush()

    # GPIO17 for momentary sensor power.
    # Slow degradation to soil moisture
    # sensor
    sensor_power = DigitalOutputDevice(17)

    while True:
        try:
            # Only apply power to soil moisture sensor
            # whenever we take a reading
            sensor_power.on()
            sleep(.001)

            # A little gross, JSON to inject with sensor
            # reading
            reading = ('{{"chans":[{{"ch_idx":0,"ch_data":['
                       '{{"data_idx":0,"units":1,"val":{0}}}]}}]}}'
                       '\n'.format(get_soil_moisture()))

            # Turn string into binary
            reading = reading.encode()

            # Write command to STDIN
            gadget.stdin.write(reading)
            gadget.stdin.flush()

            sensor_power.off()
            sleep(5)
        except KeyboardInterrupt:
            # Trick to send EOF to STDIN
            # in order to close Gadget
            gadget.stdin.close()
            gadget.wait()
            print('Gadget exited with rc {0}'.format(gadget.returncode))
            break

Avimesa.Live Integration

If you have an active Avimesa.Live account, you can add your sensor there and get some output like this: alt text NOTE: The spike in the graph here is me pouring water into the soil near the soil humidity sensor.

Start Building for Free

Try Avimesa.Live today and get 10,000 free messages per month.