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
[codesyntax lang=”python”]
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
[/codesyntax]
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
[codesyntax lang=”python”]
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)
[/codesyntax]
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