code.py
  1  # SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import board
  7  import busio
  8  from digitalio import DigitalInOut
  9  import neopixel
 10  import adafruit_bh1750
 11  from adafruit_esp32spi import adafruit_esp32spi
 12  from adafruit_esp32spi import adafruit_esp32spi_wifimanager
 13  import adafruit_esp32spi.adafruit_esp32spi_socket as socket
 14  
 15  import adafruit_minimqtt.adafruit_minimqtt as MQTT
 16  
 17  ### Sensor Calibration ###
 18  # Appliance power LED's light level, in Lux
 19  APPLIANCE_ON_LUX = 30.0
 20  # How often the light sensor will be read, in seconds
 21  SENSOR_READ_TIME = 10.0
 22  
 23  ### WiFi ###
 24  
 25  # Get wifi details and more from a secrets.py file
 26  try:
 27      from secrets import secrets
 28  except ImportError:
 29      print("WiFi secrets are kept in secrets.py, please add them there!")
 30      raise
 31  
 32  # If you are using a board with pre-defined ESP32 Pins:
 33  esp32_cs = DigitalInOut(board.ESP_CS)
 34  esp32_ready = DigitalInOut(board.ESP_BUSY)
 35  esp32_reset = DigitalInOut(board.ESP_RESET)
 36  
 37  # If you have an externally connected ESP32:
 38  # esp32_cs = DigitalInOut(board.D9)
 39  # esp32_ready = DigitalInOut(board.D10)
 40  # esp32_reset = DigitalInOut(board.D5)
 41  
 42  spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
 43  esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
 44  """Use below for Most Boards"""
 45  status_light = neopixel.NeoPixel(
 46      board.NEOPIXEL, 1, brightness=0.2
 47  )  # Uncomment for Most Boards
 48  """Uncomment below for ItsyBitsy M4"""
 49  # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
 50  # Uncomment below for an externally defined RGB LED
 51  # import adafruit_rgbled
 52  # from adafruit_esp32spi import PWMOut
 53  # RED_LED = PWMOut.PWMOut(esp, 26)
 54  # GREEN_LED = PWMOut.PWMOut(esp, 27)
 55  # BLUE_LED = PWMOut.PWMOut(esp, 25)
 56  # status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
 57  wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
 58  
 59  # Set up a pin for controlling the relay
 60  power_pin = DigitalInOut(board.D3)
 61  power_pin.switch_to_output()
 62  
 63  # Set up the light sensor
 64  i2c = board.I2C()
 65  sensor = adafruit_bh1750.BH1750(i2c)
 66  
 67  ### Feeds ###
 68  # Set up a feed named Relay for subscribing to the relay feed on Adafruit IO
 69  feed_relay = secrets["aio_username"] + "/feeds/relay"
 70  
 71  # Set up a feed named status for subscribing to the status feed on Adafruit IO
 72  feed_status = secrets["aio_username"] + "/feeds/status"
 73  
 74  ### Code ###
 75  
 76  # Define callback methods which are called when events occur
 77  # pylint: disable=unused-argument, redefined-outer-name
 78  def connected(client, userdata, flags, rc):
 79      # This function will be called when the client is connected
 80      # successfully to the broker.
 81      print("Connected to Adafruit IO!")
 82  
 83  
 84  def disconnected(client, userdata, rc):
 85      # This method is called when the client is disconnected
 86      print("Disconnected from Adafruit IO!")
 87  
 88  
 89  def subscribe(client, userdata, topic, granted_qos):
 90      # This method is called when the client subscribes to a new feed.
 91      print("Subscribed to {0}".format(topic))
 92  
 93  
 94  def unsubscribe(client, userdata, topic, pid):
 95      # This method is called when the client unsubscribes from a feed.
 96      print("Unsubscribed from {0} with PID {1}".format(topic, pid))
 97  
 98  def on_message(client, topic, message):
 99      # Method callled when a client's subscribed feed has a new value.
100      print("New message on topic {0}: {1}".format(topic, message))
101  
102  
103  def on_relay_msg(client, topic, value):
104      # Called when relay feed obtains a new value
105      print("Turning Relay %s"%value)
106      if value == "ON":
107          power_pin.value = True
108      elif value == "OFF":
109          power_pin.value = False
110      else:
111          print("Unexpected value received on relay feed.")
112  
113  # Connect to WiFi
114  print("Connecting to WiFi...")
115  wifi.connect()
116  print("Connected!")
117  
118  MQTT.set_socket(socket, esp)
119  
120  # Set up a MiniMQTT Client
121  client = MQTT.MQTT(
122      broker="io.adafruit.com",
123      username=secrets["aio_username"],
124      password=secrets["aio_key"],
125  )
126  
127  # Setup the callback methods above
128  client.on_connect = connected
129  client.on_disconnect = disconnected
130  client.on_subscribe = subscribe
131  client.on_unsubscribe = unsubscribe
132  client.on_message = on_message
133  # Add a callback to the relay feed
134  client.add_topic_callback(feed_relay, on_relay_msg)
135  
136  # Connect the client to Adafruit IO
137  print("Connecting to Adafruit IO...")
138  client.connect()
139  
140  # Subscribe to all updates on relay feed
141  client.subscribe(feed_relay)
142  
143  # Holds previous state of light sensor
144  prv_sensor_value = 0
145  # Time in seconds since start
146  start_time = time.monotonic()
147  
148  while True:
149      try:
150          # Poll for new messages on feed_relay
151          client.loop()
152          now = time.monotonic()
153          if now - start_time > SENSOR_READ_TIME:
154              # Read light sensor
155              print("Reading light sensor")
156              sensor_value = sensor.lux
157              print("%.2f Lux" % sensor.lux)
158              if sensor_value != prv_sensor_value:
159                  # Light sensor value changed between readings
160                  if sensor_value > APPLIANCE_ON_LUX:
161                      # Appliance is ON, publish to feed_status
162                      print("Appliance ON, publishing to IO...")
163                      client.publish(feed_status, 1)
164                      print("Published!")
165                  else:
166                      # Appliance is OFF, publish to feed_status
167                      print("Appliance OFF, publishing to IO...")
168                      client.publish(feed_status, 2)
169                      print("Published!")
170                  prv_sensor_value = sensor_value
171              start_time = now
172      except (ValueError, RuntimeError, ConnectionError, OSError) as e:
173          print("Failed to get data, retrying\n", e)
174          wifi.reset()
175          client.reconnect()
176          continue
177      time.sleep(0.5)