In this article we connect an SGP40 air quality sensor to our Raspberry Pi Pico
The SGP40 is Sensirion’s new digital VOC (volatile organic compounds) sensor designed for easy integration into air purifiers or demand-controlled ventilation.
SGP40 is Sensirion’s new digital VOC (volatile organic compounds) sensor designed for easy integration into air treatment devices and air quality monitors.
Based on Sensirion’s CMOSens® Technology, the SGP40 offers a complete sensor system on a single chip and features a digital I²C interface, a temperature-controlled micro-hotplate and a humidity-compensated indoor air quality signal.
In combination with Sensirion’s powerful VOC Algorithm, the sensor signal can be directly used to evaluate indoor air quality
Parts Required
I used a Pico sense hat which has 4 sensors on it, it would be possible to wire up a SGP40 sensor to a Pico board
In this image you can see a sense hat attached to a Pico
Code Example
I used Thonny and this example is written in Micropython
The first part is a library for the sgp40
You need to upload this to your Raspberry Pi Pico
Go to File > Save as…
Select the Raspberry Pi Pico
Name your file as sgp40.py and press the OK button
And that’s it. The library was uploaded to your board. To make sure that it was uploaded successfully, go to File > Save as… and select the Raspberry Pi Pico device. Your file should be listed there:
After uploading the library to your board, you can use the library in your code by importing the library.
A lot of code to look at but the library is attached to the bottom of the article, this is taken from the github repo for the pico sense hat
from machine import I2C
import utime
import struct
import math
class SGP40:
class NotFoundException(Exception):
pass
class NotSupportedException(Exception):
pass
class CRCException(Exception):
pass
MEASUREMENT_RAW = 0x260f
MEASUREMENT_TEST = 0x280e
HEATER_OFF = 0x3615
RESET = 0x0006
GET_SERIAL_ID = 0x3682
GET_FEATURESET = 0x202f
# Generated using
# crc_table = []
# for crc in range(256):
# for crc_bit in range(8):
# if crc & 0x80:
# crc = (crc << 1) ^ CRC8_POLYNOMIAL;
# else:
# crc = (crc << 1);
# crc = crc%256
# crc_table.append(crc)
CRC_TABLE = [
0, 49, 98, 83, 196, 245, 166, 151, 185, 136, 219, 234, 125, 76, 31, 46,
67, 114, 33, 16, 135, 182, 229, 212, 250, 203, 152, 169, 62, 15, 92, 109,
134, 183, 228, 213, 66, 115, 32, 17, 63, 14, 93, 108, 251, 202, 153, 168,
197, 244, 167, 150, 1, 48, 99, 82, 124, 77, 30, 47, 184, 137, 218, 235,
61, 12, 95, 110, 249, 200, 155, 170, 132, 181, 230, 215, 64, 113, 34, 19,
126, 79, 28, 45, 186, 139, 216, 233, 199, 246, 165, 148, 3, 50, 97, 80,
187, 138, 217, 232, 127, 78, 29, 44, 2, 51, 96, 81, 198, 247, 164, 149,
248, 201, 154, 171, 60, 13, 94, 111, 65, 112, 35, 18, 133, 180, 231, 214,
122, 75, 24, 41, 190, 143, 220, 237, 195, 242, 161, 144, 7, 54, 101, 84,
57, 8, 91, 106, 253, 204, 159, 174, 128, 177, 226, 211, 68, 117, 38, 23,
252, 205, 158, 175, 56, 9, 90, 107, 69, 116, 39, 22, 129, 176, 227, 210,
191, 142, 221, 236, 123, 74, 25, 40, 6, 55, 100, 85, 194, 243, 160, 145,
71, 118, 37, 20, 131, 178, 225, 208, 254, 207, 156, 173, 58, 11, 88, 105,
4, 53, 102, 87, 192, 241, 162, 147, 189, 140, 223, 238, 121, 72, 27, 42,
193, 240, 163, 146, 5, 52, 103, 86, 120, 73, 26, 43, 188, 141, 222, 239,
130, 179, 224, 209, 70, 119, 36, 21, 59, 10, 89, 104, 255, 206, 157, 172
]
def __init__(self, i2c, addr=0x59):
self.i2c = i2c
self.addr = addr
if not addr in i2c.scan():
raise self.NotFoundException
def measure_raw(self, humidity=50, temperature=25):
paramh = struct.pack(">H", math.ceil(humidity * 0xffff / 100))
crch = self.__crc(paramh[0], paramh[1])
paramt = struct.pack(">H", math.ceil((temperature + 45) * 0xffff / 175))
crct = self.__crc(paramt[0], paramt[1])
data = paramh + bytes([crch]) + paramt + bytes([crct])
self.i2c.writeto_mem(self.addr, self.MEASUREMENT_RAW, data, addrsize=16)
utime.sleep_ms(30)
raw = self.i2c.readfrom(self.addr, 3)
self.__check_crc(raw)
return struct.unpack('>H', raw[:2])[0]
def measure_test(self):
raw = self.__read_bytes(self.MEASUREMENT_TEST, 3, 250000)
self.__check_crc(raw)
return struct.unpack('>H', raw[:2])[0]
def reset(self):
self.__write_command(self.RESET)
def heater_off(self):
self.__write_command(self.HEATER_OFF)
def get_serial_id(self):
data = self.__read_bytes(self.GET_SERIAL_ID, 6, 500)
return data
def __write_command(self, cmd):
bcmd = struct.pack('>H', cmd)
self.i2c.writeto(self.addr, bcmd)
def __read_bytes(self, cmd, count, pause):
self.__write_command(cmd)
utime.sleep_us(pause)
return self.i2c.readfrom(self.addr, count)
def __check_crc(self, arr):
assert (len(arr) == 3)
if self.__crc(arr[0], arr[1]) != arr[2]:
raise self.CRCException
def __crc(self, msb, lsb):
crc = 0xff
crc ^= msb
crc = self.CRC_TABLE[crc]
if lsb is not None:
crc ^= lsb
crc = self.CRC_TABLE[crc]
return crc
You can download from the bottom of this article
Now we need to use these functions
After uploading the library to the Raspberry Pi Pico, copy the following code to the main.py.
This will print the air quality as a raw value into the shell every 5 seconds
rom machine import Pin, I2C
from time import sleep
from sgp40 import SGP40
i2c = I2C(1,scl=Pin(7), sda=Pin(6), freq=40000)#all sensor connected through I2C
while True:
air_quality = SGP40(i2c, 0x59)
Air_quality = air_quality.measure_raw()
print("Air quality = ",Air_quality)
sleep(5)
Output
Here is what I saw in Thonny shell window
>>> %Run -c $EDITOR_CONTENT
Air quality = 26890
Air quality = 26123
Air quality = 25234
Air quality = 25286