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)