I swapped the Adafruit KB2040 over for a Marble Pico I had around, it is a super charged Pico with a stemmaQT port 8mb of external flash onboard, and a handy SD card slot. Then became a big issue of aligning the right I2C ports. I jumped back and forth between micropython and cicuitpython as well and settled on circuitpython as it is more supported lib wise it seems... I have yet to get logging going but I did add a startup timer/countdown for the sensor and then a "static" display on the serial output. It works sort of, and really is a bad method of doing it, seems circuitpython does not really have the same functions for clearing the serial output.
I ended up with this "AI FIELD ANALYZER v1.0 | Time: 10:08:57 | CO₂: 408 ppm | TVOC: 0 ppb | ✅ Excellent air quality, no ventilation needed. | ✅ No significant VOCs detected—air is clean...ly."
Unsure on the 64gig SD card may just be too much for it, plus I need a fat32 format as well.

Code is getting a bit long so, this is likely the last time I post it here and will be making a Github for it.
import time
import board
import busio
import adafruit_sgp30
# **Initialize I²C and Sensor**
i2c = busio.I2C(board.GP5, board.GP4)
sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c)
sgp30.iaq_init()
# **Function to Classify Risk Level**
def get_warning(eCO2, TVOC):
"""Generates air quality warnings based on CO₂ & VOC readings."""
warnings = []
# **CO₂ Risk Levels**
if eCO2 < 1000:
warnings.append("✅ Excellent air quality, no ventilation needed.")
elif 1000 <= eCO2 < 2000:
warnings.append("⚠️ CO₂ levels rising—consider ventilating soon.")
else:
warnings.append("🚨 CO₂ dangerously high! Immediate ventilation needed!")
# **VOC Risk Levels**
if TVOC < 500:
warnings.append("✅ No significant VOCs detected—air is clean.")
elif 500 <= TVOC < 2000:
warnings.append("⚠️ Chemical odors detected—monitor air closely.")
else:
warnings.append("🚨 High VOC levels! Ventilation or evacuation advised!")
return warnings
# **Startup Countdown (OLED-Friendly)**
def sensor_startup_timer(seconds):
"""Displays countdown and clears it after startup."""
for i in range(seconds, 0, -1):
print("\r" + " " * 50, end="") # Clears previous text
print(f"\r⌛ Sensor Ready in {i}s...", end="") # Overwrites same line
time.sleep(1)
print("\r✅ SGP30 Sensor Ready!", end="") # Displays ready status for 2 seconds
time.sleep(2)
print("\033[2J\033[H", end="") # **Fully clears screen before measurements start**
# **Function to Update Display (OLED-Like Static Output)**
def update_display(eCO2, TVOC):
"""Refreshes display while keeping output static."""
current_time = time.localtime()
formatted_time = f"{current_time[3]:02d}:{current_time[4]:02d}:{current_time[5]:02d}"
warnings = get_warning(eCO2, TVOC)
# **Clear previous output before writing new data**
print("\r" + " " * 120, end="") # Clears previous output fully
print(f"\rAI FIELD ANALYZER v1.0 | Time: {formatted_time} | CO₂: {eCO2} ppm | TVOC: {TVOC} ppb | {warnings[0]} | {warnings[1]}", end="")
# **Run Startup Timer**
sensor_startup_timer(22) # Adjust time as needed
# **Sensor Loop**
while True:
eCO2, TVOC = sgp30.iaq_measure()
update_display(eCO2, TVOC)
time.sleep(1)
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.