Introduction: SGP30 Air Quality/Flatulence Detector: Pico/I2S Version

About: Scientist working in in-vitro diagnostics industry. Playing with all types of sensor as a spare time hobby. Aiming for simple and inexpensive tools and projects for STEM, with a bit of science and a bit of sil…

Summary: this instructable will demonstrate how to play a WAV sound file with an I2S amp attached to a RPi Pico depending on an I2C sensor signal.


In the following I will describe a simple air quality or flatulence detector with acoustic output. It uses the SGP30 eCO2 and TVOC sensor, a Raspberry Pi Pico microcontroller, an MAX98357A I2S amp and a speaker.

The concept is easy: the sensor breakout should be placed near the site of emission, as at the underside of the toilet seat or in a cushion on a chair, and when certain gases are released and their concentrations are rising above a threshold level some noise or voice comment will be played.

In a previous version I had used an external MP3 player and a Reed relay to turn the player on. When I recently learned that the Pico can drive an I2S amp breakout and play WAV sound files using CircuitPython, I decided to try to apply this concept to simplify and improve the layout of the device. With some minor optimization of the their example script, it finally worked. The I2S amp is driven via PIO and not a hardware I2S interface, which is not available on the Pico. Make sure to install the latest CircuitPython version and Pico driver.

Due to the limited storage capacity of the Pico, the WAV file (mono!) must not be too large. It can easily be generated using Audacity, or other tools. Just place the WAV-file at the Pico/CircuitPython root directory.

One of the strange facts is that the I2S amp often only works when the Pico is attached to a USB power pack or USB power supply, but not if attached to a computer. I therefor used the on-board LED to signal activation without working sound output. When attached to the computer, the eCO2 values can be displayed using Mu.

I got he idea for the device when I was planning to build a CO2 traffic light for my son's class room and realized that these inexpensive eCO2 sensors are actually measuring other human gaseous emissions as proxy markers. These are predominantly hydrogen (H2) and some methane (CH4) generated by bacteria in the guts. Spontaneous local emissions, i.e. trumps and farts, result in a short local raise of the H2 and CH4 concentrations. These gases can be easily oxidized and be determined using electrochemical sensors as the SGP30. This sensor is generating two raw signals, a "hydrogen" and an "ethanol" value,which are then is used to calculate internally the eCO2 (carbon dioxide equivalent) and TVOC (total volatile organic components) signals that are reported by the sensor breakout. eCO2 gives the strongest signals and was therefor used to monitor H2/CH4 emissions. But with the same device you may also measure ethanol vapours or other organic gases.

And, in case, you may even use the device as a noisy class room air quality monitor.


Note added April 2021: Turns out you may also play MP3s with the Pico/I2S combo. Example here:
The Horrifying Chocolate Easter Bunny


Raspberry Pi Pico (4€/4 GBP)

MAX98357A I2S Class D mono amp breakout, Adafruit (6 US$/7 €)

SGP30 sensor breakout, Pimoroni (16 €/GBP)

Speaker, 4 or 8 Ohm.
I used a 3W 4 Ohm Speaker.


USB power pack

some jumper cables

Step 1: Assembly

Assembly is simple.

I soldered jumper cables to the Pimoroni SGP30 breakout. You may as well the Adafruit or Sparkfun versions of the sensors. I soldered headers to the MAX98357 breakout and the Pico and the Pico was placed on a breadboard, Using jumper cables, the MAX98357 din (data in), BCLK (bit clock) and LRC (word select) ports were connected to the Pico's GP9,GP10 and GP11 ports. GND (ground) and vin (voltage in) to the Pico's GND and 3V3 (Out) ports.

The SGP30's SDA and SCL ports were connected to the Pico's GP21 and GP22, GND and vin to the according ports on the Pico.

The speakers black and red cables were connected to the terminal block I soldered to the MAX98357 breakout.

Now load the script, renamed to "", and the WAV file to the Pico's root directory and the SGP30.mpy driver from the Adafruit CircuitPython library into folder "lib" on the Pico.

Test if everything is working, For a test, you may bring a cotton pad soaked with alcohol close to the sensor to evoke a signal, or you may use gaseous emissions. Using mu, you can see the changing values in REPL and the plotter and the LED will light, but you probably won't hear a sound. You may then attach the Pico to an USB power pack or power supply and repeat the test.

If everything works well, any tiny trump will be greatly amplified. But remember that the sensor must be close to the site of emission.

Step 2: The Script and WAV

I have taken the I2S part of the script from David Glaude's example on github and Michael Horne's article (Thanks!), added some minor modifications and the SGP30 code, which by itself is based on an example by Adafruit.
As I was mostly combining already existing code, I am not sure if everything included is really required.
But as long as it works, I don't care too much.

Any corrections or suggestions for improvement are welcome!

The WAV was taken from a old record. Bout as old as I am. Let me know if you were able to identify the musician.
A hint: the guy has been playing with the Rolling Stones, about 50 years later.

# Test for Pico I2S playing capability, based on
# and
# I2S Sound output triggered by a i2c sensor signal (sgp30 eCO2 sensor)
# SGP30 code adapted from adafruit example

import audiocore
import board
import audiobusio
import time
import digitalio
import busio
import adafruit_sgp30

onboard_led = digitalio.DigitalInOut(board.GP25) # Pico onboard LED
onboard_led.direction = digitalio.Direction.OUTPUT

bit_clock = board.GP10 # for i2s breakout, pin 12
word_select = board.GP11 # for i2s breakout, pin 14
data_in = board.GP9 # for i2s breakout, pin 15

SCL_0 = board.GP17 # for i2c sensor, pin 22
SDA_0 = board.GP16 # for i2c sensor, pin 21
i2c_0 = busio.I2C(SCL_0, SDA_0, frequency=100000)
sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c_0)
thresh = 800
# set eCO2 threshold value ccording to your needs, base value: 400

# audio settings
wave_file = open("Test2.wav", "rb")
wave = audiocore.WaveFile(wave_file)
audio = audiobusio.I2SOut(bit_clock, word_select, data_in)
gap = 1 # length of pause

def play_wav(): # plays a wav file

onboard_led.value = True

t = time.monotonic()
while time.monotonic() - t < 6:

onboard_led.value = False

print("SGP30 serial #", [hex(i) for i in sgp30.serial])

sgp30.set_iaq_baseline(0x8973, 0x8AAE)

elapsed_sec = 0

while True:
print((sgp30.eCO2, sgp30.TVOC)) # output for mu plotter
# print("eCO2 = %d ppm \t TVOC = %d ppb" % (sgp30.eCO2, sgp30.TVOC)) # output as text
elapsed_sec += 1
if elapsed_sec > 10:
elapsed_sec = 0
"**** Baseline values: eCO2 = 0x%x, TVOC = 0x%x"
% (sgp30.baseline_eCO2, sgp30.baseline_TVOC)
if (sgp30.eCO2 > thresh): # play sound if eCO2 value above threshold