|
| 1 | +# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | + |
| 4 | +import os |
| 5 | +import ssl |
| 6 | +import time |
| 7 | +import microcontroller |
| 8 | +import board |
| 9 | +import wifi |
| 10 | +import socketpool |
| 11 | +import adafruit_requests |
| 12 | +import neopixel |
| 13 | +import displayio |
| 14 | +from adafruit_ticks import ticks_ms, ticks_add, ticks_diff |
| 15 | +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError |
| 16 | +from adafruit_pm25.i2c import PM25_I2C |
| 17 | +import adafruit_scd4x |
| 18 | +from adafruit_display_text import bitmap_label |
| 19 | +from adafruit_bitmap_font import bitmap_font |
| 20 | +from adafruit_display_shapes.roundrect import RoundRect |
| 21 | + |
| 22 | +# connect to SSID |
| 23 | +wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD')) |
| 24 | + |
| 25 | +pool = socketpool.SocketPool(wifi.radio) |
| 26 | +requests = adafruit_requests.Session(pool, ssl.create_default_context()) |
| 27 | + |
| 28 | +pool = socketpool.SocketPool(wifi.radio) |
| 29 | + |
| 30 | +# adafruit IO info |
| 31 | +aio_username = os.getenv('aio_username') |
| 32 | +aio_key = os.getenv('aio_key') |
| 33 | +location = "America/New York" |
| 34 | + |
| 35 | +# io HTTP for getting the time from the internet |
| 36 | +io = IO_HTTP(aio_username, aio_key, requests) |
| 37 | + |
| 38 | +def reset_on_error(delay, error): |
| 39 | + print("Error:\n", str(error)) |
| 40 | + print("Resetting microcontroller in %d seconds" % delay) |
| 41 | + time.sleep(delay) |
| 42 | + microcontroller.reset() |
| 43 | + |
| 44 | +def c_to_f(temp_data): |
| 45 | + temperature_celsius = temp_data |
| 46 | + temperature_fahrenheit = temperature_celsius * 9 / 5 + 32 |
| 47 | + return int(temperature_fahrenheit) |
| 48 | + |
| 49 | +# setup NeoPixels |
| 50 | +pixel_pin = board.D13 |
| 51 | +num_pixels = 8 |
| 52 | + |
| 53 | +pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False) |
| 54 | + |
| 55 | +red = (255, 0, 0) |
| 56 | +yellow = (255, 125, 0) |
| 57 | +green = (0, 255, 0) |
| 58 | + |
| 59 | +i2c = board.STEMMA_I2C() |
| 60 | + |
| 61 | +reset_pin = None |
| 62 | + |
| 63 | +pm25 = PM25_I2C(i2c, reset_pin) |
| 64 | +aqdata = pm25.read() |
| 65 | + |
| 66 | +scd4x = adafruit_scd4x.SCD4X(i2c) |
| 67 | +scd4x.start_periodic_measurement() |
| 68 | + |
| 69 | +time.sleep(2) |
| 70 | + |
| 71 | +co2 = int(scd4x.CO2) |
| 72 | +temp = c_to_f(scd4x.temperature) |
| 73 | +humidity = int(scd4x.relative_humidity) |
| 74 | + |
| 75 | +pm2 = int(aqdata["pm25 standard"]) |
| 76 | +pm2_x = [94, 94, 94, 94, 140, 185] |
| 77 | +pm2_colors = [green, green, green, green, yellow, red] |
| 78 | + |
| 79 | +# display setup |
| 80 | +display = board.DISPLAY |
| 81 | + |
| 82 | +bitmap = displayio.OnDiskBitmap("/airBG.bmp") |
| 83 | + |
| 84 | +tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader) |
| 85 | +group = displayio.Group() |
| 86 | +group.append(tile_grid) |
| 87 | + |
| 88 | +small_font_file = "/OCRA_small.pcf" |
| 89 | +small_font = bitmap_font.load_font(small_font_file) |
| 90 | +big_font_file = "/OCRA_big.pcf" |
| 91 | +big_font = bitmap_font.load_font(big_font_file) |
| 92 | + |
| 93 | +pm2_text = bitmap_label.Label(big_font, text="%d" % pm2, x=42, y=40, color=0xFFFFFF) |
| 94 | +group.append(pm2_text) |
| 95 | + |
| 96 | +co2_text = bitmap_label.Label(small_font, text="%d" % co2, x=50, y=107, color=0xFFFFFF) |
| 97 | +temp_text = bitmap_label.Label(small_font, text="%d" % temp, x=130, y=107, color=0xFFFFFF) |
| 98 | +humid_text = bitmap_label.Label(small_font, text="%d" % humidity, x=205, y=107, color=0xFFFFFF) |
| 99 | +group.append(co2_text) |
| 100 | +group.append(temp_text) |
| 101 | +group.append(humid_text) |
| 102 | + |
| 103 | +pm2_outline = RoundRect(pm2_x[pm2], 19, 46, 46, 10, fill=None, outline=0xFFFFFF, stroke=3) |
| 104 | +group.append(pm2_outline) |
| 105 | + |
| 106 | +pixels.fill(pm2_colors[pm2]) |
| 107 | +pixels.show() |
| 108 | + |
| 109 | +display.show(group) |
| 110 | + |
| 111 | +sensor_texts = [pm2_text, co2_text, temp_text, humid_text] |
| 112 | +sensor_data = [pm2, co2, temp, humidity] |
| 113 | + |
| 114 | +sensor_clock = ticks_ms() |
| 115 | +io_clock = ticks_ms() |
| 116 | + |
| 117 | +sensor_check = 30000 |
| 118 | +io_check = 300000 |
| 119 | +first_run = True |
| 120 | + |
| 121 | +try: |
| 122 | + # get feed |
| 123 | + pm25_feed = io.get_feed("pm25") |
| 124 | +except AdafruitIO_RequestError: |
| 125 | + # if no feed exists, create one |
| 126 | + pm25_feed = io.create_new_feed("pm25") |
| 127 | +try: |
| 128 | + # get feed |
| 129 | + temp_feed = io.get_feed("temp") |
| 130 | +except AdafruitIO_RequestError: |
| 131 | + # if no feed exists, create one |
| 132 | + temp_feed = io.create_new_feed("temp") |
| 133 | +try: |
| 134 | + # get feed |
| 135 | + co2_feed = io.get_feed("co2") |
| 136 | +except AdafruitIO_RequestError: |
| 137 | + # if no feed exists, create one |
| 138 | + co2_feed = io.create_new_feed("co2") |
| 139 | +try: |
| 140 | + # get feed |
| 141 | + humid_feed = io.get_feed("humid") |
| 142 | +except AdafruitIO_RequestError: |
| 143 | + # if no feed exists, create one |
| 144 | + humid_feed = io.create_new_feed("humid") |
| 145 | + |
| 146 | +sensor_feeds = [pm25_feed, co2_feed, temp_feed, humid_feed] |
| 147 | + |
| 148 | +while True: |
| 149 | + try: |
| 150 | + if first_run or ticks_diff(ticks_ms(), sensor_clock) > sensor_check: |
| 151 | + if scd4x.data_ready: |
| 152 | + co2 = int(scd4x.CO2) |
| 153 | + temp = c_to_f(scd4x.temperature) |
| 154 | + humidity = int(scd4x.relative_humidity) |
| 155 | + pm2 = int(aqdata["pm25 standard"]) |
| 156 | + pm2_outline.x = pm2_x[pm2] |
| 157 | + pixels.fill(pm2_colors[pm2]) |
| 158 | + pixels.show() |
| 159 | + for s in range(4): |
| 160 | + sensor_texts[s].text = "%d" % sensor_data[s] |
| 161 | + print("updated %d data" % sensor_data[s]) |
| 162 | + time.sleep(0.2) |
| 163 | + sensor_clock = ticks_add(sensor_clock, sensor_check) |
| 164 | + |
| 165 | + if first_run or ticks_diff(ticks_ms(), io_clock) > io_check: |
| 166 | + for z in range(4): |
| 167 | + io.send_data(sensor_feeds[z]["key"], sensor_data[z]) |
| 168 | + print("sent %d to io" % sensor_data[z]) |
| 169 | + time.sleep(1) |
| 170 | + io_clock = ticks_add(io_clock, io_check) |
| 171 | + if first_run: |
| 172 | + sensor_clock = ticks_ms() |
| 173 | + io_clock = ticks_ms() |
| 174 | + first_run = False |
| 175 | + # pylint: disable=broad-except |
| 176 | + except Exception as e: |
| 177 | + reset_on_error(10, e) |
0 commit comments