A carton of milk that smells fine to one person can smell off to another, and if a cold has knocked out your nose entirely you get no vote at all. Adafruit’s freshness checker turns that judgment call into a number on a screen, and the whole build is one Saturday afternoon of wiring and CircuitPython.
What the build actually does
The device pairs an SGP30 air-quality sensor with an Adafruit CLUE. The SGP30 reports two channels: total volatile organic compounds in ppb, and an equivalent CO2 figure in ppm, both derived from a heated metal-oxide element. Spoiling milk throws off VOCs as bacteria break down lactose and fat, so the TVOC number climbs as the carton ages. The CLUE’s 240×240 IPS screen shows the reading, and its nRF52840 runs the whole loop in CircuitPython with no laptop attached.
The experiment in the guide is the honest part: fresh milk against milk deliberately left out, same sensor, same headspace over the carton. That comparison matters more than any single reading, because the SGP30 carries no absolute calibration for “spoiled.” You are watching a delta, not reading a verdict.
Parts and cost reality
The SGP30 talks I2C at address 0x58, so it comes down to four wires: SDA, SCL, 3.3V, GND. The breakout carries its own regulator and pull-ups, so you can hang it off a 5V board without level shifting. Two gotchas worth knowing before you solder anything: the sensor needs roughly 15 seconds after power-up before its output means anything, and its baseline algorithm expects a steady 1 Hz sample rate. Poll it irregularly and the eCO2 figure drifts. The driver lets you save a baseline after 12 hours of running and reload it on boot, which is the difference between a demo and something you trust.
You do not need a CLUE for this. An ESP32 with a 0.96″ 128×32 OLED covers the same job on a breadboard for a fraction of the cost, and the I2C wiring is identical. To rehearse the pattern while the gas sensor is still in transit, a BME280 uses the same four wires and the same import style in code.
Spend your Sunday on this
Log TVOC every 10 minutes against one carton for three days and plot the curve. That chart is a defensible data set for a science fair board or an ECE instrumentation report, and it costs one sensor plus patience. Full wiring diagram and code live at learn.adafruit.com/digital-nose-gas-sensor-milk-freshness-checker.
Frequently Asked Questions
How does a gas sensor tell whether milk has gone bad?
The SGP30 uses a heated metal-oxide element to report total volatile organic compounds in ppb. As bacteria break down lactose and fat, spoiling milk releases more VOCs, so the TVOC reading climbs. There is no absolute ‘spoiled’ threshold, so you compare a fresh carton against an aged one and watch the difference.
What parts do I need, and can I skip the Adafruit CLUE?
You need an SGP30 breakout, a microcontroller, and a display. The CLUE is convenient because its 240×240 screen and nRF52840 are already on one board, but an ESP32 with a 0.96-inch 128×32 OLED does the same job on a breadboard for less. The SGP30 sits on I2C at address 0x58, so the wiring is SDA, SCL, 3.3V and GND either way.
What will I learn if I build this?
You practise I2C wiring and addressing, sensor warm-up and baseline handling (the SGP30 needs about 15 seconds to settle and a steady 1 Hz poll), and the habit of logging data over time instead of trusting a single reading. Plotting TVOC across three days gives you a real measurement write-up, which is the same workflow behind an instrumentation lab report or a thesis chapter.
