Libraries

The code begins by importing the libraries.

import time
import board
import simpleio
import adafruit_sgp30
import displayio
import adafruit_imageload
from adafruit_emc2101 import EMC2101
from adafruit_funhouse import FunHouse

STEMMA Sensor Setup

Next, I2C and the two STEMMA sensors, the SGP30 and EMC2101, are setup.

i2c = board.I2C()

#  setup for SGP30 sensor
sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c)
#  setup for fan controller
emc = EMC2101(i2c)

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

#SGP30 start-up
sgp30.iaq_init()
sgp30.set_iaq_baseline(0x8973, 0x8AAE)

Graphics

The adafruit_funhouse library has a FunHouse object that can be used for displayio.

Following that import, the five bitmaps are setup with displayio. There are three background images that will be shown depending on where you are in the code. The remaining two bitmaps are icons to show whether or not you are sending data to Adafruit IO. 

#  FunHouse setup
funhouse = FunHouse(default_bg=0x0F0F00)
#  start-up bitmap
bitmap, palette = adafruit_imageload.load("/scene1_fume.bmp",
                                         bitmap=displayio.Bitmap,
                                         palette=displayio.Palette)
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
#  connecting bitmap
bitmap2, palette2 = adafruit_imageload.load("/scene2_fume.bmp",
                                         bitmap=displayio.Bitmap,
                                         palette=displayio.Palette)
grid2 = displayio.TileGrid(bitmap2, pixel_shader=palette2)
#  default background
bitmap3, palette3 = adafruit_imageload.load("/scene3_fume.bmp",
                                         bitmap=displayio.Bitmap,
                                         palette=displayio.Palette)
grid3 = displayio.TileGrid(bitmap3, pixel_shader=palette3)
#  internet connection icon
bitmap4, palette4 = adafruit_imageload.load("/connect_icon.bmp",
                                         bitmap=displayio.Bitmap,
                                         palette=displayio.Palette)
icon1 = displayio.TileGrid(bitmap4, pixel_shader=palette4, x = 2, y = 2)
#  red x icon
bitmap5, palette5 = adafruit_imageload.load("/x_icon.bmp",
                                         bitmap=displayio.Bitmap,
                                         palette=displayio.Palette)
icon2 = displayio.TileGrid(bitmap5, pixel_shader=palette5, x = 2, y = 2)

Following the bitmap imports, a display group is setup and the first bitmap is added to the group. Then two text objects are created, fume_text and fan_text, that will display the data output from the SGP30 and EMC2101 sensors.

#  display group
group = displayio.Group()
#  adding start-up bitmap to group
group.append(tile_grid)
funhouse.splash.append(group)
#  text for fume data
fume_text = funhouse.add_text(
    text="    ",
    text_position=(110, 90),
    text_anchor_point=(0.5, 0.5),
    text_color=0xf57f20,
    text_font="fonts/Arial-Bold-24.pcf",
)
#  text for fan RPM data
fan_text = funhouse.add_text(
    text="    ",
    text_position=(110, 165),
    text_anchor_point=(0.5, 0.5),
    text_color=0x7fffff,
    text_font="fonts/Arial-Bold-24.pcf",
)
#  showing graphics
funhouse.display.root_group = funhouse.splash

State Machines and Variables

A few state machines and variables are setup for use in the loop. Their functions are commented below.

#  state machines
run = False #  state if main code is running
connected = False #  checks if connected to wifi
start_up = False #  state for start-up
clock = 0 #  time.monotonic() device

Adafruit IO Functions

Two functions are created to send data to Adafruit IO. send_fume_data() sends data from the SGP30 and send_fan_data() sends data from the ECM2101.

For these functions to work properly, you need to have feeds setup in Adafruit IO called "fumes" and "fan-speed". Be sure to follow the steps on the Adafruit IO Setup page in this guide.

#  function for sending fume data to adafruit.io
def send_fume_data(solder_fumes):
    funhouse.network.push_to_io("fumes", solder_fumes)

#  function for sending fan rpm to adafruit.io
def send_fan_data(fan_rpm):
    funhouse.network.push_to_io("fan-speed", fan_rpm)

The Loop

The loop begins by checking if the main program is running with the run state. If the main program is not running, then the start-up graphic is shown on the FunHouse and you need to press either the middle or bottom button to begin the fume extraction. The buttons determine whether or not you connect to WiFi and send your fume data to Adafruit IO

Pressing the Down Button (Not Connecting to WiFi)

If you press the down button, the start-up bitmap is removed from group and the main program bitmap is added to group for display. Additionally, the red x icon is also added to group to show that you are not connected to WiFi. Finally, run is set to True so that the fume extraction and data display can begin.

#  if you press the down button
        if funhouse.peripherals.button_down:
            print("run")
            #  remove start-up bitmap
            group.remove(tile_grid)
            #  add main bitmap
            group.append(grid3)
            #  add red x icon to show not connected to internet
            group.append(icon2)
            #  change state for main program
            run = True

Pressing the Select Button (Connecting to WiFi)

If you press the select button (in the middle of the FunHouse's three buttons), the start-up bitmap is removed from group and the "connecting..." bitmap is added to group.

Then, the function funhouse.network.connect() is called. This connects the FunHouse to WiFi. 

After a network connection has been established, the start_up state is set to True and clock is reset with time.monotonic().

#  if you press the middle button
        if funhouse.peripherals.button_sel:
            #  remove start-up bitmap
            group.remove(tile_grid)
            #  add connecting... bitmap
            group.append(grid2)
            #  connect to the network
            funhouse.network.connect()
            print("connecting")
            #  change state for network
            connected = True
            #  start main program
            start_up = True
            #  start time.monotonic()
            clock = time.monotonic()

The start-up state exists so that after the FunHouse connects to WiFi, the connecting graphic can be removed from group and replaced with the main graphic. Additionally, the internet icon is added to show that you're sending data to Adafruit IO. Finally, run is set to True.

#  after connecting to the internet
        if start_up:
            #  remove connecting bitmap
            group.remove(grid2)
            #  add main bitmap
            group.append(grid3)
            #  add internet icon
            group.append(icon1)
            #  start main program
            run = True
            #  reset start-up state
            start_up = False

The Main Program

The main program takes readings from the SGP30 to affect the fan's RPM speed. This is done with the simpleio.map_range() function. You can either use the SGP30's eCO2 readings or TVOC readings. You'll just need to comment and uncomment certain lines to change this.

The data from the SGP30's readings and the fan's RPM are updated for the fume_text and fan_text objects to display on the screen. 

#  fumes variable for reading from SGP30
        #  comment out either TVOC or eCO2 depending on data preference
        fumes = sgp30.TVOC
        #  fumes = sgp30.eCO2

        #  mapping fumes data to fan RPM
        #  value for TVOC
        mapped_val = simpleio.map_range(fumes, 10, 1000, 10, 100)
        #  value for eCO2
        #  mapped_val = simpleio.map_range(fumes, 400, 2500, 10, 100)

        #  adding fume text
        #  PPB is for TVOC, PPM is for eCO2
        funhouse.set_text("%d PPB" % fumes, fume_text)
        #  funhouse.set_text("%d PPM" % fumes, fume_text)

        #  adding fan's RPM text
        funhouse.set_text("%d%s" % (mapped_val, "%"), fan_text)
        #  printing fan's data to the REPL
        print("fan = ", mapped_val)
        #  setting fan's RPM
        emc.manual_fan_speed = int(mapped_val)

Sending Data to Adafruit IO

Every fifteen seconds, the data from the SGP30 and EMC2101 are sent to feeds on Adafruit IO. After the data is sent, clock is reset with time.monotonic().

#  if you're connected to wifi and 15 seconds has passed
        if connected and ((clock + 15) < time.monotonic()):
            #  send fume data to adafruit.io
            send_fume_data(fumes)
            #  send fan RPM to adafruit.io
            send_fan_data(mapped_val)
            #  REPL printout
            print("data sent")
            #  reset clock
            clock = time.monotonic()

Connecting and Disconnecting from WiFi

If you decide after booting up the FunHouse that you want to connect or disconnect from WiFi, you can press the up button on the FunHouse next to the icon in the top left corner of the screen.

The connection is affected by changing the connected state. The icon also changes to show the connection status visually.

#  if you're connected to wifi and you press the up button
        if connected and funhouse.peripherals.button_up:
            #  the internet icon is removed
            group.remove(icon1)
            #  the red x icon is added
            group.append(icon2)
            #  reset connected state - no longer sending data to adafruit.io
            connected = False
            #  REPL printout
            print("disconnected")
            #  1 second delay
            time.sleep(1)
        #  if you're NOT connected to wifi and you press the up button
        if not connected and funhouse.peripherals.button_up:
            #  the red x icon is removed
            group.remove(icon2)
            #  the internet icon is added
            group.append(icon1)
            #  the connection state is true - start sending data to adafruit.io
            connected = True
            #  REPL printout
            print("connected")
            #  1 second delay
            time.sleep(1)

This guide was first published on Jun 09, 2021. It was last updated on Mar 28, 2024.

This page (CircuitPython Code Walkthrough) was last updated on Mar 23, 2024.

Text editor powered by tinymce.