# UIFlow 2 (MicroPython) e-nose logger for M5Stack Core2 / CoreS3. # # Reads Unit ENV Pro (BME688), Unit TVOC/eCO2 (SGP30) and Unit CO2 (SCD40) on Port A # (through a Grove hub) once per second and sends one text line to the laptop: # # D,,,,,,,,,,,, # # TRANSPORT = "serial": print() over the USB cable, read with record.py --serial # TRANSPORT = "udp": send over WiFi, read with record.py --udp 5005 # # BME688 in UIFlow 2 only supports one heater temperature per reading and returns a # cached value if read again within about 2 s. So we read it every 3 s and switch to the # next of 5 heater temperatures after each reading (a full cycle takes 15 s). import M5 from M5 import * from hardware import * from unit import ENVPROUnit, TVOCUnit, CO2Unit import time # ---------- settings: change these ---------- TRANSPORT = "serial" # "serial" or "udp" LAPTOP_IP = "192.168.1.10" # the laptop's IP address (udp only) LAPTOP_PORT = 5005 HEATER_STEPS_C = (200, 240, 280, 320, 360) HEATER_MS = 150 BME_PERIOD_S = 3 BOARD = "core2" # "core2" or "cores3" # -------------------------------------------- M5.begin() Widgets.fillScreen(0x111111) title = Widgets.Label("E-nose", 8, 6, 1.0, 0x5EEAD4, 0x111111, Widgets.FONTS.DejaVu24) line_gas = Widgets.Label("gas: -", 8, 50, 1.0, 0xFFFFFF, 0x111111, Widgets.FONTS.DejaVu18) line_sgp = Widgets.Label("eth: -", 8, 80, 1.0, 0xFFFFFF, 0x111111, Widgets.FONTS.DejaVu18) line_co2 = Widgets.Label("co2: -", 8, 110, 1.0, 0xFFFFFF, 0x111111, Widgets.FONTS.DejaVu18) line_env = Widgets.Label("T/RH: -", 8, 140, 1.0, 0xFFFFFF, 0x111111, Widgets.FONTS.DejaVu18) line_state = Widgets.Label(TRANSPORT, 8, 200, 1.0, 0x98A3C0, 0x111111, Widgets.FONTS.DejaVu18) if BOARD == "cores3": i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000) else: i2c0 = I2C(0, scl=Pin(33), sda=Pin(32), freq=100000) env = ENVPROUnit(i2c0) # BME688, address 0x77 tvoc = TVOCUnit(i2c0) # SGP30, address 0x58 co2 = CO2Unit(i2c0) # SCD40, address 0x62 co2.set_start_periodic_measurement() sender = None if TRANSPORT == "udp": import network import socket wlan = network.WLAN(network.STA_IF) while not wlan.isconnected(): # UIFlow 2 connects with the WiFi saved when it was flashed line_state.setText("waiting for WiFi...") time.sleep(1) sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) line_state.setText("udp -> " + LAPTOP_IP) def send(text): if sender is None: print(text) else: sender.sendto(text.encode(), (LAPTOP_IP, LAPTOP_PORT)) step = 0 env.set_gas_heater(HEATER_STEPS_C[step], HEATER_MS) temp_c, rh_pct, pressure_hpa = 0.0, 0.0, 0.0 co2_ppm = 0 tick = 0 next_ms = time.ticks_add(time.ticks_ms(), 1000) while True: M5.update() eco2_ppm, tvoc_ppb = tvoc.measure_iaq() # keep calling once per second for the SGP30 algorithm h2_raw, ethanol_raw = tvoc.measure_raw() if co2.is_data_ready(): # new SCD40 value about every 5 s co2_ppm = co2.co2 if tick % BME_PERIOD_S == 0: temp_c = env.get_temperature() rh_pct = env.get_humidity() pressure_hpa = env.get_pressure() gas_ohm = env.get_gas_resistance() * 1000 # the unit returns kilo-ohm reported_step, heater_c = step, HEATER_STEPS_C[step] step = (step + 1) % len(HEATER_STEPS_C) env.set_gas_heater(HEATER_STEPS_C[step], HEATER_MS) line_gas.setText("gas{}: {:.0f}".format(reported_step, gas_ohm)) else: gas_ohm, reported_step, heater_c = -1, -1, 0 # no BME688 reading this second send("D,{},{},{},{:.1f},{:.2f},{:.2f},{:.2f},{},{},{},{},{}".format( time.ticks_ms(), reported_step, heater_c, gas_ohm, temp_c, rh_pct, pressure_hpa, ethanol_raw, h2_raw, tvoc_ppb, eco2_ppm, co2_ppm)) line_sgp.setText("eth: {} h2: {}".format(ethanol_raw, h2_raw)) line_co2.setText("co2: {} ppm".format(co2_ppm)) line_env.setText("T/RH: {:.1f} / {:.1f}".format(temp_c, rh_pct)) tick += 1 wait = time.ticks_diff(next_ms, time.ticks_ms()) if wait > 0: time.sleep_ms(wait) next_ms = time.ticks_add(next_ms, 1000)