227 lines
7.7 KiB
Python
227 lines
7.7 KiB
Python
# SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import time
|
|
import json
|
|
import supervisor
|
|
import simpleio
|
|
import vectorio
|
|
import board
|
|
import terminalio
|
|
import rtc
|
|
import socketpool
|
|
import wifi
|
|
import displayio
|
|
import adafruit_ntp
|
|
from adafruit_display_text import bitmap_label, wrap_text_to_lines
|
|
from adafruit_bitmap_font import bitmap_font
|
|
from adafruit_azureiot import IoTCentralDevice
|
|
import adafruit_bme680
|
|
from adafruit_lc709203f import LC709203F, PackSize
|
|
|
|
|
|
# Get wifi details and more from a secrets.py file
|
|
try:
|
|
from secrets import secrets
|
|
except ImportError:
|
|
print("WiFi secrets are kept in secrets.py, please add them there!")
|
|
raise
|
|
|
|
print("Connecting to WiFi...")
|
|
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
|
|
|
print("Connected to WiFi!")
|
|
|
|
# ntp clock - update tz_offset to your timezone
|
|
pool = socketpool.SocketPool(wifi.radio)
|
|
ntp = adafruit_ntp.NTP(pool, tz_offset=-4)
|
|
rtc.RTC().datetime = ntp.datetime
|
|
|
|
if time.localtime().tm_year < 2022:
|
|
print("Setting System Time in UTC")
|
|
rtc.RTC().datetime = ntp.datetime
|
|
|
|
else:
|
|
print("Year seems good, skipping set time.")
|
|
|
|
cal = ntp.datetime
|
|
year = cal[0]
|
|
mon = cal[1]
|
|
day = cal[2]
|
|
hour = cal[3]
|
|
minute = cal[4]
|
|
|
|
# To use Azure IoT Central, you will need to create an IoT Central app.
|
|
# You can either create a free tier app that will live for 7 days without an Azure subscription,
|
|
# Or a standard tier app that will last for ever with an Azure subscription.
|
|
# The standard tiers are free for up to 2 devices
|
|
#
|
|
# If you don't have an Azure subscription:
|
|
#
|
|
# If you are a student, head to https://aka.ms/FreeStudentAzure and sign up, validating with your
|
|
# student email address. This will give you $100 of Azure credit and free tiers of a load of
|
|
# service, renewable each year you are a student
|
|
#
|
|
# If you are not a student, head to https://aka.ms/FreeAz and sign up to get $200 of credit for 30
|
|
# days, as well as free tiers of a load of services
|
|
#
|
|
# Create an Azure IoT Central app by following these
|
|
# instructions: https://aka.ms/CreateIoTCentralApp
|
|
# Add a device template with telemetry, properties and commands, as well as a view to visualize the
|
|
# telemetry and execute commands, and a form to set properties.
|
|
#
|
|
# Next create a device using the device template, and select Connect to get the
|
|
# device connection details.
|
|
# Add the connection details to your secrets.py file, using the following values:
|
|
#
|
|
# 'id_scope' - the devices ID scope
|
|
# 'device_id' - the devices device id
|
|
# 'device_sas_key' - the devices primary key
|
|
#
|
|
# The adafruit-circuitpython-azureiot library depends on the following libraries:
|
|
#
|
|
# From the Adafruit CircuitPython Bundle
|
|
# (https://github.com/adafruit/Adafruit_CircuitPython_Bundle):
|
|
# * adafruit-circuitpython-minimqtt
|
|
# * adafruit-circuitpython-requests
|
|
|
|
# Create sensor object, communicating over the board's default I2C bus
|
|
i2c = board.I2C() # uses board.SCL and board.SDA
|
|
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
|
|
bme680 = adafruit_bme680.Adafruit_BME680_I2C(i2c, debug=False)
|
|
|
|
# change this to match the location's pressure (hPa) at sea level
|
|
bme680.sea_level_pressure = 1013.25
|
|
|
|
# Create an IoT Hub device client and connect
|
|
esp = None
|
|
pool = socketpool.SocketPool(wifi.radio)
|
|
device = IoTCentralDevice(
|
|
pool, esp, secrets["id_scope"], secrets["device_id"], secrets["device_primary_key"]
|
|
)
|
|
|
|
print("Connecting to Azure IoT Central...")
|
|
device.connect()
|
|
|
|
print("Connected to Azure IoT Central!")
|
|
|
|
temperature_offset = -5
|
|
|
|
# Create sensor object, using the board's default I2C bus.
|
|
battery_monitor = LC709203F(i2c)
|
|
|
|
# Update to match the mAh of your battery for more accurate readings.
|
|
# Can be MAH100, MAH200, MAH400, MAH500, MAH1000, MAH2000, MAH3000.
|
|
# Choose the closest match. Include "PackSize." before it, as shown.
|
|
battery_monitor.pack_size = PackSize.MAH2000
|
|
|
|
temp = int((bme680.temperature * 9/5) + (32 + temperature_offset))
|
|
humidity = int(bme680.relative_humidity)
|
|
pressure = int(bme680.pressure)
|
|
battery = battery_monitor.cell_percent
|
|
|
|
# display setup
|
|
display = board.DISPLAY
|
|
|
|
palette0 = displayio.Palette(2)
|
|
palette0[0] = 0x00FF00
|
|
palette0[1] = 0xFF0000
|
|
|
|
# load bitmap
|
|
bitmap = displayio.OnDiskBitmap("/bmeTFT.bmp")
|
|
tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
|
|
group = displayio.Group()
|
|
group.append(tile_grid)
|
|
# rectangle for battery life monitor
|
|
# vectorio allows for resizing in the loop
|
|
rect = vectorio.Rectangle(pixel_shader=palette0, width=22, height=10, x=12, y=116, color_index = 0)
|
|
group.append(rect)
|
|
# bitmap font
|
|
font_file = "/roundedHeavy-26.bdf"
|
|
font = bitmap_font.load_font(font_file)
|
|
# text elements
|
|
temp_text = bitmap_label.Label(font, text="%0.1f° F" % temp, x=20, y=80, color=0xFFFFFF)
|
|
humid_text = bitmap_label.Label(font, text="%0.1f %%" % humidity, x=95, y=80, color=0xFFFFFF)
|
|
press_text = bitmap_label.Label(font, text="%0.2f" % pressure, x=170, y=80, color=0xFFFFFF)
|
|
time_text = bitmap_label.Label(terminalio.FONT,
|
|
text="\n".join(wrap_text_to_lines
|
|
("Data sent on %s/%s/%s at %s:%s" % (mon,day,year,hour,minute), 20)),
|
|
x=125, y=105, color=0xFFFFFF)
|
|
group.append(temp_text)
|
|
group.append(humid_text)
|
|
group.append(press_text)
|
|
group.append(time_text)
|
|
display.show(group)
|
|
|
|
# clock to count down to sending data to Azure
|
|
azure_clock = 500
|
|
# clock to count down to updating TFT
|
|
feather_clock = 30
|
|
|
|
|
|
while True:
|
|
try:
|
|
# read BME sensor
|
|
temp = int((bme680.temperature * 9/5) + (32 + temperature_offset))
|
|
humidity = int(bme680.relative_humidity)
|
|
pressure = int(bme680.pressure)
|
|
# log battery %
|
|
battery = battery_monitor.cell_percent
|
|
# map range of battery charge to rectangle size on screen
|
|
battery_display = round(simpleio.map_range(battery, 0, 100, 0, 22))
|
|
# update rectangle to reflect battery charge
|
|
rect.width = int(battery_display)
|
|
# if below 20%, change rectangle color to red
|
|
if battery_monitor.cell_percent < 20:
|
|
rect.color_index = 1
|
|
# when the azure clock runs out
|
|
if azure_clock > 500:
|
|
print("getting ntp date/time")
|
|
cal = ntp.datetime
|
|
year = cal[0]
|
|
mon = cal[1]
|
|
day = cal[2]
|
|
hour = cal[3]
|
|
minute = cal[4]
|
|
time.sleep(2)
|
|
print("getting msg")
|
|
# pack message
|
|
message = {"Temperature": temp,
|
|
"Humidity": humidity,
|
|
"Pressure": pressure,
|
|
"BatteryPercent": battery}
|
|
print("sending json")
|
|
device.send_telemetry(json.dumps(message))
|
|
print("data sent")
|
|
clock_view = "%s:%s" % (hour, minute)
|
|
if minute < 10:
|
|
clock_view = "%s:0%s" % (hour, minute)
|
|
print("updating time text")
|
|
time_text.text="\n".join(wrap_text_to_lines
|
|
("Data sent on %s/%s/%s at %s" % (mon,day,year,clock_view), 20))
|
|
# reset azure clock
|
|
azure_clock = 0
|
|
# when the feather clock runs out
|
|
if feather_clock > 30:
|
|
print("updating screen")
|
|
temp_text.text = "%0.1f° F" % temp
|
|
humid_text.text = "%0.1f %%" % humidity
|
|
press_text.text = "%0.2f" % pressure
|
|
# reset feather clock
|
|
feather_clock = 0
|
|
# if no clocks are running out
|
|
# increase counts by 1
|
|
else:
|
|
feather_clock += 1
|
|
azure_clock += 1
|
|
# ping azure
|
|
device.loop()
|
|
# if something disrupts the loop, reconnect
|
|
# pylint: disable=broad-except
|
|
except (ValueError, RuntimeError, OSError, ConnectionError) as e:
|
|
print("Network error, reconnecting\n", str(e))
|
|
supervisor.reload()
|
|
continue
|
|
# delay
|
|
time.sleep(1)
|
|
print(azure_clock)
|