code.py
  1  # SPDX-FileCopyrightText: 2022 Liz Clark for Adafruit Industries
  2  # SPDX-License-Identifier: MIT
  3  
  4  import json
  5  import time
  6  import digitalio
  7  import supervisor
  8  import simpleio
  9  import vectorio
 10  import board
 11  import terminalio
 12  import rtc
 13  import socketpool
 14  import wifi
 15  import displayio
 16  import adafruit_ntp
 17  from adafruit_display_text import bitmap_label,  wrap_text_to_lines
 18  from adafruit_bitmap_font import bitmap_font
 19  from adafruit_azureiot import IoTHubDevice
 20  import adafruit_bme680
 21  from adafruit_lc709203f import LC709203F, PackSize
 22  
 23  # Get wifi details and more from a secrets.py file
 24  try:
 25      from secrets import secrets
 26  except ImportError:
 27      print("WiFi secrets are kept in secrets.py, please add them there!")
 28      raise
 29  
 30  print("Connecting to WiFi...")
 31  wifi.radio.connect(secrets["ssid"], secrets["password"])
 32  
 33  print("Connected to WiFi!")
 34  
 35  #  ntp clock - update tz_offset to your timezone
 36  pool = socketpool.SocketPool(wifi.radio)
 37  ntp = adafruit_ntp.NTP(pool, tz_offset=0)
 38  rtc.RTC().datetime = ntp.datetime
 39  
 40  if time.localtime().tm_year < 2022:
 41      print("Setting System Time in UTC")
 42      rtc.RTC().datetime = ntp.datetime
 43  
 44  else:
 45      print("Year seems good, skipping set time.")
 46  
 47  esp = None
 48  pool = socketpool.SocketPool(wifi.radio)
 49  # Create an IoT Hub device client and connect
 50  device = IoTHubDevice(pool, esp, secrets["device_connection_string"])
 51  
 52  print("Connecting to Azure IoT Hub...")
 53  
 54  # Connect to IoT Central
 55  device.connect()
 56  
 57  print("Connected to Azure IoT Hub!")
 58  
 59  cal = ntp.datetime
 60  year = cal[0]
 61  mon = cal[1]
 62  day = cal[2]
 63  hour = cal[3]
 64  minute = cal[4]
 65  
 66  i2c = board.I2C()  # uses board.SCL and board.SDA
 67  bme680 = adafruit_bme680.Adafruit_BME680_I2C(i2c, debug=False)
 68  
 69  # change this to match the location's pressure (hPa) at sea level
 70  bme680.sea_level_pressure = 1013.25
 71  
 72  temperature_offset = -5
 73  
 74  # Create sensor object, using the board's default I2C bus.
 75  battery_monitor = LC709203F(board.I2C())
 76  
 77  # Update to match the mAh of your battery for more accurate readings.
 78  # Can be MAH100, MAH200, MAH400, MAH500, MAH1000, MAH2000, MAH3000.
 79  # Choose the closest match. Include "PackSize." before it, as shown.
 80  battery_monitor.pack_size = PackSize.MAH2000
 81  
 82  temp = int((bme680.temperature * 9/5) + (32 + temperature_offset))
 83  humidity = int(bme680.relative_humidity)
 84  pressure = int(bme680.pressure)
 85  battery = battery_monitor.cell_percent
 86  
 87  #  setup boot button as input
 88  button = digitalio.DigitalInOut(board.BUTTON)
 89  button.switch_to_input(pull=digitalio.Pull.UP)
 90  
 91  #  display setup
 92  display = board.DISPLAY
 93  
 94  palette0 = displayio.Palette(2)
 95  palette0[0] = 0x00FF00
 96  palette0[1] = 0xFF0000
 97  
 98  #  load bitmap
 99  bitmap = displayio.OnDiskBitmap("/bmeTFT.bmp")
100  tile_grid = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader)
101  group = displayio.Group()
102  group.append(tile_grid)
103  #  rectangle for battery life monitor
104  #  vectorio allows for resizing in the loop
105  rect = vectorio.Rectangle(pixel_shader=palette0, width=22, height=10, x=12, y=116, color_index = 0)
106  group.append(rect)
107  #  bitmap font
108  font_file = "/roundedHeavy-26.bdf"
109  font = bitmap_font.load_font(font_file)
110  #  text elements
111  temp_text = bitmap_label.Label(font, text="%0.1f° F" % temp, x=20, y=80, color=0xFFFFFF)
112  humid_text = bitmap_label.Label(font, text="%0.1f %%" % humidity, x=95, y=80, color=0xFFFFFF)
113  press_text = bitmap_label.Label(font, text="%0.2f" % pressure, x=170, y=80, color=0xFFFFFF)
114  time_text = bitmap_label.Label(terminalio.FONT,
115              text="\n".join(wrap_text_to_lines
116              ("Data sent on %s/%s/%s at %s:%s" % (mon,day,year,hour,minute), 20)),
117              x=125, y=105, color=0xFFFFFF)
118  group.append(temp_text)
119  group.append(humid_text)
120  group.append(press_text)
121  group.append(time_text)
122  display.show(group)
123  
124  #  clock to count down to sending data to Azure
125  azure_clock = 500
126  #  clock to count down to updating TFT
127  feather_clock = 30
128  #  button debounce state
129  button_pressed = False
130  
131  while True:
132      try:
133          if button.value and button_pressed:
134              button_pressed = False
135          if not button.value and not button_pressed:
136              print("getting msg")
137  			#  pack message
138              message = {"Temperature": temp,
139                         "Humidity": humidity,
140                         "Pressure": pressure,
141                         "BatteryPercent": battery,
142                         "FeatherConnected": 1}
143              print("sending json")
144              device.send_device_to_cloud_message(json.dumps(message))
145              print("data sent")
146          #  read BME sensor
147          temp = int((bme680.temperature * 9/5) + (32 + temperature_offset))
148          humidity = int(bme680.relative_humidity)
149          pressure = int(bme680.pressure)
150  		#  log battery %
151          battery = battery_monitor.cell_percent
152  		#  map range of battery charge to rectangle size on screen
153          battery_display = round(simpleio.map_range(battery, 0, 100, 0, 22))
154  		#  update rectangle to reflect battery charge
155          rect.width = int(battery_display)
156  		#  if below 20%, change rectangle color to red
157          if battery_monitor.cell_percent < 20:
158              rect.color_index = 1
159  		#  when the azure clock runs out
160          if azure_clock > 500:
161              print("getting ntp date/time")
162              cal = ntp.datetime
163              year = cal[0]
164              mon = cal[1]
165              day = cal[2]
166              hour = cal[3]
167              minute = cal[4]
168              time.sleep(2)
169              print("getting msg")
170  			#  pack message
171              message = {"Temperature": temp,
172                         "Humidity": humidity,
173                         "Pressure": pressure,
174                         "BatteryPercent": battery}
175              print("sending json")
176              device.send_device_to_cloud_message(json.dumps(message))
177              print("data sent")
178              clock_view = "%s:%s" % (hour, minute)
179              if minute < 10:
180                  clock_view = "%s:0%s" % (hour, minute)
181              print("updating time text")
182              time_text.text="\n".join(wrap_text_to_lines
183              ("Data sent on %s/%s/%s at %s" % (mon,day,year,clock_view), 20))
184  			#  reset azure clock
185              azure_clock = 0
186          #  when the feather clock runs out
187          if feather_clock > 30:
188              print("updating screen")
189              temp_text.text = "%0.1f° F" % temp
190              humid_text.text = "%0.1f %%" % humidity
191              press_text.text = "%0.2f" % pressure
192  			#  reset feather clock
193              feather_clock = 0
194  		#  if no clocks are running out
195  		#  increase counts by 1
196          else:
197              feather_clock += 1
198              azure_clock += 1
199  		#  ping azure
200          device.loop()
201      #  if something disrupts the loop, reconnect
202      # pylint: disable=broad-except
203      except (ValueError, RuntimeError, OSError, ConnectionError) as e:
204          print("Network error, reconnecting\n", str(e))
205          time.sleep(60)
206          supervisor.reload()
207          continue
208  	#  delay
209      time.sleep(1)