code.py
  1  # SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  """
  6  PyPortal Weather Station
  7  ==============================================
  8  Turn your PyPortal into a weatherstation with
  9  Adafruit IO
 10  
 11  Author: Brent Rubell for Adafruit Industries, 2019
 12  """
 13  import time
 14  import board
 15  import neopixel
 16  import busio
 17  import analogio
 18  from simpleio import map_range
 19  from digitalio import DigitalInOut
 20  
 21  from adafruit_esp32spi import adafruit_esp32spi, adafruit_esp32spi_wifimanager
 22  from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError
 23  
 24  # sensor libs
 25  import adafruit_veml6075
 26  import adafruit_sgp30
 27  from adafruit_bme280 import basic as adafruit_bme280
 28  
 29  # weatherstation graphics helper
 30  import weatherstation_helper
 31  
 32  # rate at which to refresh the pyportal and sensors, in seconds
 33  PYPORTAL_REFRESH = 30
 34  
 35  # anemometer defaults
 36  anemometer_min_volts = 0.4
 37  anemometer_max_volts = 2.0
 38  min_wind_speed = 0.0
 39  max_wind_speed = 32.4
 40  
 41  # Get wifi details and more from a secrets.py file
 42  try:
 43      from secrets import secrets
 44  except ImportError:
 45      print("WiFi secrets are kept in secrets.py, please add them there!")
 46      raise
 47  
 48  # PyPortal ESP32 Setup
 49  esp32_cs = DigitalInOut(board.ESP_CS)
 50  esp32_ready = DigitalInOut(board.ESP_BUSY)
 51  esp32_reset = DigitalInOut(board.ESP_RESET)
 52  spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
 53  esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
 54  status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
 55  wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
 56  
 57  # Set your Adafruit IO Username and Key in secrets.py
 58  # (visit io.adafruit.com if you need to create an account,
 59  # or if you need your Adafruit IO key.)
 60  ADAFRUIT_IO_USER = secrets['aio_username']
 61  ADAFRUIT_IO_KEY = secrets['aio_key']
 62  
 63  # Create an instance of the Adafruit IO HTTP client
 64  io = IO_HTTP(ADAFRUIT_IO_USER, ADAFRUIT_IO_KEY, wifi)
 65  
 66  # create an i2c object
 67  i2c = busio.I2C(board.SCL, board.SDA)
 68  
 69  # instantiate the sensor objects
 70  veml = adafruit_veml6075.VEML6075(i2c, integration_time=100)
 71  sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c)
 72  bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c)
 73  # change this to match the location's pressure (hPa) at sea level
 74  bme280.sea_level_pressure = 1013.25
 75  
 76  # init. the graphics helper
 77  gfx = weatherstation_helper.WeatherStation_GFX()
 78  
 79  # init. the ADC
 80  adc = analogio.AnalogIn(board.D4)
 81  
 82  # Set up Adafruit IO Feeds
 83  print('Getting Group data from Adafruit IO...')
 84  station_group = io.get_group('weatherstation')
 85  feed_list = station_group['feeds']
 86  altitude_feed = feed_list[0]
 87  eco2_feed = feed_list[1]
 88  humidity_feed = feed_list[2]
 89  pressure_feed = feed_list[3]
 90  temperature_feed = feed_list[4]
 91  tvoc_feed = feed_list[5]
 92  uv_index_feed = feed_list[6]
 93  wind_speed_feed = feed_list[7]
 94  
 95  def adc_to_wind_speed(val):
 96      """Returns anemometer wind speed, in m/s.
 97      :param int val: ADC value
 98      """
 99      voltage_val = val / 65535 * 3.3
100      return map_range(voltage_val, 0.4, 2, 0, 32.4)
101  
102  def send_to_io():
103      # handle sending sensor data to Adafruit IO
104      io.send_data(uv_index_feed['key'], uv_index)
105      io.send_data(wind_speed_feed['key'], wind_speed)
106      io.send_data(temperature_feed['key'], bme280_data[0])
107      io.send_data(humidity_feed['key'], bme280_data[1])
108      io.send_data(pressure_feed['key'], bme280_data[2])
109      io.send_data(altitude_feed['key'], bme280_data[3])
110      io.send_data(eco2_feed['key'], sgp_data[0])
111      io.send_data(tvoc_feed['key'], sgp_data[1])
112  
113  while True:
114      print('obtaining sensor data...')
115      # Get uv index from veml6075
116      uv_index = veml.uv_index
117      # Get eco2, tvoc from sgp30
118      eCO2, TVOC = sgp30.iaq_measure()
119      sgp_data = [eCO2, TVOC]
120      # Store bme280 data as a list
121      bme280_data = [bme280.temperature, bme280.humidity,
122                     bme280.pressure, bme280.altitude]
123      # Get wind speed
124      wind_speed = adc_to_wind_speed(adc.value)
125      # Display sensor data on PyPortal using the gfx helper
126      print('displaying sensor data...')
127      gfx.display_data(uv_index, bme280_data,
128                       sgp_data, wind_speed)
129      print('sensor data displayed!')
130      try:
131          try:
132              print('Sending data to Adafruit IO...')
133              gfx.display_io_status('Sending data to IO...')
134              send_to_io()
135              gfx.display_io_status('Data Sent!')
136              print('Data sent!')
137          except AdafruitIO_RequestError as e:
138              raise AdafruitIO_RequestError('IO Error: ', e)
139      except (ValueError, RuntimeError, ConnectionError, OSError) as e:
140          print("Failed to get data, retrying\n", e)
141          wifi.reset()
142          continue
143      time.sleep(PYPORTAL_REFRESH)