1. Home
  2. Docs
  3. Device Kits
  4. Kit 5: Air Quality Using a Raspberry Pi

Kit 5: Air Quality Using a Raspberry Pi

In this Device Kit, we’ll be attaching an air quality sensor to a Raspberry Pi and sending its outputs to Avimesa.Live using Avimesa Gadget for further processing.

dev-kit-5.jpg

To successfully set up this Device Kit, a rudimentary knowledge of electronics, the Linux command line interface (CLI), and the Python (2 & 3) programming languages is (probably) required. We’ll be using the CCS811 Air Quality Breakout board by SparkFun Electronics to give us two metrics pertaining to the air quality around you and this sensor: Carbon Dioxide molecular content (in ppm – parts-per-million) and (Total) Volatile Organic Compound molecular content (in ppb – parts-per-billion). To communicate with our CCS811 air quality sensor we’ll use the serial communication protocol I2C (I-Squared-C). On the Raspberry Pi we’ll be using the Python programming language to drive (or control) Gadget as well as to communicate with our sensor (over I2C) to obtain its outputs. To be more specific, we’ll make extensive use of two Python modules: the standard library subprocess module to handle multiple child processes and the third party smbus module for I2C communication. The smbus module, only available for Python 2 on a Raspberry Pi, is the recommended Python module for I2C communication functionality on a Raspberry Pi.

Components

Initial Setup

(1) Once you download a desirable Raspbian image, you can use Etcher to quickly copy it to your microSD.

(2) To enable I2C on your Pi, first you’ll want to run sudo raspi-config and then do Interfacing Options > P5 I2C > Enable. Now you’ll want to verify that we actually enabled it: you should have /dev/i2c-X in your filesystem (Make note of X – we’ll be using it later). One other change we’ll need to make in order for the I2C bus to communicate with our air quality sensor is to change the baud rate by adding the line dtparam=i2c_baudrate=10000 to /boot/config.txt (you’ll need sudo privileges to edit this file).

(3) sudo apt install python-smbus

(4) sudo apt install i2c-tools

(5) If you purchase exactly this, you’ll also need to do some through-hole soldering to connect your air quality sensor to some break away headers.

CCS811 Datasheet

(Optional) If you’re more interested in what this sensor can do or are interested in what I’m doing, check out the datasheet.

Hardware Setup

Following is a table that should help in wiring up the CCS811 Air Quality Breakout board to the Raspberry Pi. This link contains a great representation of the Raspberry Pi 40 pinout header and the associated Broadcom connections for assistance in determining what connections to make.

Raspberry Pi CCS811 Air Quality Breakout board
P1 – 3V3 3.3V
P6 – Ground GND, nWAKE
P3 – SDA I2C SDA
P5 – SCL I2C SCL

Avimesa.Live Setup

First, you’ll need an active Avimesa.Live account.

Second, you’ll need to obtain a valid set of Avimesa device credentials:

Software Setup

Let’s quickly verify that our chip is showing on the I2C interface with an address of 0x5B (you MAY see 0x5A – see datasheet) by typing sudo i2cdetect -y **X** at the command line (NOTE: noted X from Initial Setup). i2cdetect-output.png

For this device kit, we’ll have two Python scripts (and one will call the other). I couldn’t condense this into one script because I’ve opted to use Python 2 and 3 for the following reasons: Python 2’s subprocess module is quite flawed and should be replaced with the third party subprocess32 (or Python 3’s subprocess module…) and smbus is only available in Python 2. The joys of software development.

First, the Python (2) script I’ll be using to collect our sensor data over I2C:

#!/usr/bin/python

from smbus import SMBus
from time import sleep

SLAVE_ADDR = 0x5B # Can be 0x5A

# Slave registers to read and write
DEVICE_REG_STATUS = 0x00
DEVICE_REG_MEAS_MODE = 0x01
DEVICE_REG_ALG_RESULT_DATA = 0x02
DEVICE_REG_ERROR_ID = 0xE0
DEVICE_REG_APP_START = 0xF4

# Slave register read values
DEVICE_STATE_BOOT = 0x10
DEVICE_STATE_APP = 0x90
DEVICE_STATE_APP_WITH_DATA = 0x98

# Slave register write values
DEVICE_SET_MODE_10S = [0x10]
DEVICE_SET_SW_RESET = [0x11, 0xE5, 0x72, 0x8A]

# 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1)
i2c_bus = SMBus(1)

def read_i2c_bus_dev(dev_reg, read_len):
    return i2c_bus.read_i2c_block_data(SLAVE_ADDR, dev_reg, read_len)

def write_i2c_bus_dev(dev_reg, write_data):
    i2c_bus.write_i2c_block_data(SLAVE_ADDR, dev_reg, write_data)

try:
    if read_i2c_bus_dev(DEVICE_REG_STATUS, 1)[0] == DEVICE_STATE_BOOT:
        write_i2c_bus_dev(DEVICE_REG_APP_START, []) # empty write
        write_i2c_bus_dev(DEVICE_REG_MEAS_MODE, DEVICE_SET_MODE_10S)

    read_i2c_bus_dev(DEVICE_REG_ERROR_ID, 1) # clear any errors

    timeout_in_seconds = 30

    while (timeout_in_seconds and
           (read_i2c_bus_dev(DEVICE_REG_STATUS, 1)[0] !=
            DEVICE_STATE_APP_WITH_DATA)):
              sleep(5)
              timeout_in_seconds -= 5

    if not timeout_in_seconds:
        raise

    read_data = read_i2c_bus_dev(DEVICE_REG_ALG_RESULT_DATA, 8)

    eCO2_reading = (read_data[0] << 8) | read_data[1]
    eTVOC_reading = (read_data[2] << 8) | read_data[3]

    # Return some Dialtone!
    print ('{{"chans":['
              '{{"ch_idx":0,"ch_data":[{{"data_idx":0,"units":1,"val":{0}}}]}},'
              '{{"ch_idx":1,"ch_data":[{{"data_idx":0,"units":1,"val":{1}}}]}}'
           ']}}').format(eCO2_reading, eTVOC_reading)

except Exception as e:
    pass
finally:
    i2c_bus.close()

You’ll want to copy this and put it into a new file /usr/local/bin/air_quality_i2c_comms and sudo chmod 755 /usr/local/bin/air_quality_i2c_comms it. I’ve opted to explicitly display how to communicate with the CCS811 air quality sensor using SMBus (and ultimately I2C). I think tutorials that fully wrap these communication processes in classes obfuscate what’s going on; a little dabbling goes a long way!

The second Python (3) script:

#!/usr/bin/python3

import subprocess

try:
    i2c_args = ['/usr/local/bin/air_quality_i2c_comms']
    i2c_complete = subprocess.run(i2c_args,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)

    output = i2c_complete.stdout.strip()

    gdgt_input = output.decode() + '\nrun\nexit\n'
    gdgt_input = gdgt_input.encode()

    gdgt_args = ['/usr/local/bin/avmsagadget',
                 '-i',
                 'DEADBEEFDEADBEEFDEADBEEFDEADBEEF', # DEVICE ID
                 'DEADBEEFDEADBEEFDEADBEEFDEADBEEF'] # DEVICE AUTHENTICATION KEY

    gdgt_complete = subprocess.run(args=gdgt_args,
                                   input=gdgt_input,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   timeout=5)

    print("STDOUT:\n" + gdgt_complete.stdout.decode())
    print("STDERR:\n" + gdgt_complete.stderr.decode())

except subprocess.TimeoutExpired:
    print('Gadget timed out after {0} seconds!'.format(gdgt_timeout))

except subprocess.CalledProcessError as E:
    pass

Copy this and paste it into a new file /usr/local/bin/air_quality_gdgt_ctrl, change the permissions using sudo chmod 755 /usr/local/bin/air_quality_gdgt_ctrl, and then run it a few times with /usr/local/bin/air_quality_gdgt_ctrl. Make sure you inserted your device’s credentials into the script! And for automated messages every two minutes, you can add a cron job to run every minute by adding */2 * * * * /usr/local/bin/air_quality_gdgt_ctrl when editing your user’s crontab using crontab -e.

Avimesa.Live Integration

All we have to do here is add our sensors in Avimesa.Live: