Close

Software Tweaks

A project log for ATLAS

Built the tricorder I actually wanted in the field: one that warns, not just measures.

apollo-timbersApollo Timbers 06/11/2025 at 15:120 Comments

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