If you experience a power or network outage - your Dashboard's power toggle may not be accurate.
While the PyPortal will re-connect to the network, Adafruit IO toggling the button may not actually be turning the appliance on or off. You will need to add some way of measuring feedback (such as the power status) from the appliance.
A simple way of adding a feedback mechanism is by placing a light sensor over the appliance's power indicator LED to detect if the appliance is actually turned on or off.
Plug a BH1750, a small inexpensive light sensor, into your PyPortal to measure the light value from the appliance's power indicator LED. Then, create a new Adafruit IO Feed to hold the light sensor's value.
Add some new code to the PyPortal to measure the light sensor's value at a predefined interval, evaluate if the appliance is turned on or off, and send the status back to Adafruit IO.
To wrap it all up, you'll add an Indicator Block to your Adafruit IO Dashboard to display the appliance's actual power status.

You'll also need a JST PH to JST SH cable to connect the BH1750 sensor to the PyPortal's I2C port.

Add Feed
You will need a feed to hold the sensor's value. Navigate to your Adafruit IO Feeds page and click Actions -> Create New Feed. Name the feed status and click create.
Add Block to Dashboard
Navigate to the dashboard you created earlier. Click Create New Block (+ icon).
Your dashboard should look like the screenshot below:
Plug the BH1750 Light Sensor into the Stemma JST PH connector.
Then, plug the JST SH connector into the I2C port on your PyPortal.
Locate the Power LED on your appliance and attach the light sensor above the power LED using tape.
We suggest using black tape, such as gaffer's tape to avoid light leakage.
CircuitPython Installation of BH1750 Library
You'll need to install the Adafruit CircuitPython BH1750 library on your CircuitPython board.
First make sure you are running the latest version of Adafruit CircuitPython for your board.
Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle. Our CircuitPython starter guide has a great page on how to install the library bundle.
Before continuing make sure your board's lib folder or root filesystem has the adafruit_bh1750.mpy file and adafruit_bus_device folder copied over.
CircuitPython Code
Copy code_light_sensor.py to your PyPortal's CIRCUITPY drive and rename the file to code.py.
# SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries # # SPDX-License-Identifier: MIT import time import board import busio from digitalio import DigitalInOut import neopixel import adafruit_bh1750 import adafruit_connection_manager from adafruit_esp32spi import adafruit_esp32spi from adafruit_esp32spi import adafruit_esp32spi_wifimanager import adafruit_minimqtt.adafruit_minimqtt as MQTT ### Sensor Calibration ### # Appliance power LED's light level, in Lux APPLIANCE_ON_LUX = 30.0 # How often the light sensor will be read, in seconds SENSOR_READ_TIME = 10.0 ### WiFi ### # 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 # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) # If you have an externally connected ESP32: # esp32_cs = DigitalInOut(board.D9) # esp32_ready = DigitalInOut(board.D10) # esp32_reset = DigitalInOut(board.D5) spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) """Use below for Most Boards""" status_light = neopixel.NeoPixel( board.NEOPIXEL, 1, brightness=0.2 ) # Uncomment for Most Boards """Uncomment below for ItsyBitsy M4""" # status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2) # Uncomment below for an externally defined RGB LED # import adafruit_rgbled # from adafruit_esp32spi import PWMOut # RED_LED = PWMOut.PWMOut(esp, 26) # GREEN_LED = PWMOut.PWMOut(esp, 27) # BLUE_LED = PWMOut.PWMOut(esp, 25) # status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED) wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) # Set up a pin for controlling the relay power_pin = DigitalInOut(board.D3) power_pin.switch_to_output() # Set up the light sensor i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller sensor = adafruit_bh1750.BH1750(i2c) ### Feeds ### # Set up a feed named Relay for subscribing to the relay feed on Adafruit IO feed_relay = secrets["aio_username"] + "/feeds/relay" # Set up a feed named status for subscribing to the status feed on Adafruit IO feed_status = secrets["aio_username"] + "/feeds/status" ### Code ### # Define callback methods which are called when events occur # pylint: disable=unused-argument, redefined-outer-name def connected(client, userdata, flags, rc): # This function will be called when the client is connected # successfully to the broker. print("Connected to Adafruit IO!") def disconnected(client, userdata, rc): # This method is called when the client is disconnected print("Disconnected from Adafruit IO!") def subscribe(client, userdata, topic, granted_qos): # This method is called when the client subscribes to a new feed. print("Subscribed to {0}".format(topic)) def unsubscribe(client, userdata, topic, pid): # This method is called when the client unsubscribes from a feed. print("Unsubscribed from {0} with PID {1}".format(topic, pid)) def on_message(client, topic, message): # Method callled when a client's subscribed feed has a new value. print("New message on topic {0}: {1}".format(topic, message)) def on_relay_msg(client, topic, value): # Called when relay feed obtains a new value print("Turning Relay %s"%value) if value == "ON": power_pin.value = True elif value == "OFF": power_pin.value = False else: print("Unexpected value received on relay feed.") # Connect to WiFi print("Connecting to WiFi...") wifi.connect() print("Connected!") pool = adafruit_connection_manager.get_radio_socketpool(esp) ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp) # Set up a MiniMQTT Client client = MQTT.MQTT( broker="io.adafruit.com", username=secrets["aio_username"], password=secrets["aio_key"], socket_pool=pool, ssl_context=ssl_context, ) # Setup the callback methods above client.on_connect = connected client.on_disconnect = disconnected client.on_subscribe = subscribe client.on_unsubscribe = unsubscribe client.on_message = on_message # Add a callback to the relay feed client.add_topic_callback(feed_relay, on_relay_msg) # Connect the client to Adafruit IO print("Connecting to Adafruit IO...") client.connect() # Subscribe to all updates on relay feed client.subscribe(feed_relay) # Holds previous state of light sensor prv_sensor_value = 0 # Time in seconds since start start_time = time.monotonic() while True: try: # Poll for new messages on feed_relay client.loop() now = time.monotonic() if now - start_time > SENSOR_READ_TIME: # Read light sensor print("Reading light sensor") sensor_value = sensor.lux print("%.2f Lux" % sensor.lux) if sensor_value != prv_sensor_value: # Light sensor value changed between readings if sensor_value > APPLIANCE_ON_LUX: # Appliance is ON, publish to feed_status print("Appliance ON, publishing to IO...") client.publish(feed_status, 1) print("Published!") else: # Appliance is OFF, publish to feed_status print("Appliance OFF, publishing to IO...") client.publish(feed_status, 2) print("Published!") prv_sensor_value = sensor_value start_time = now except (ValueError, RuntimeError, ConnectionError, OSError) as e: print("Failed to get data, retrying\n", e) wifi.reset() client.reconnect() continue time.sleep(0.5)
Code Usage
Connect up your PyPortal's STEMMA cables and turn on the relay outlet. Once the PyPortal is connected to the internet, navigate to the Adafruit IO Dashboard and tap the toggle switch block to turn the appliance on.
The appliance should turn on and the indicator block should light up green, indicating it is turned on.
Toggling the switch to the off position should turn the appliance off and the indicator block should turn red.
Light Sensor Calibration
We wrote this guide for a LG Air Conditioner. Other appliances use different LEDs which provide different illuminance measurements. If the status indicator block is not responding, you may need to calibrate the light sensor for your appliance.
In the embedded code element below, click on the Download: Project Zip link, and save the .zip archive file to your computer.
Then, uncompress the .zip file, it will unpack to a folder named bh1750_simpletest.
Copy bh1750_simpletest.py to your PyPortal's CIRCUITPY drive and rename the file to code.py.
# SPDX-FileCopyrightText: 2020 Bryan Siepert, written for Adafruit Industries # SPDX-License-Identifier: Unlicense import time import board import adafruit_bh1750 i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller sensor = adafruit_bh1750.BH1750(i2c) while True: print("%.2f Lux" % sensor.lux) time.sleep(1)
Connect your PyPortal to power. With the light sensor placed over the light source, turn the PyPortal on. Turn the appliance on. The display will show the ambient light level in lux, the SI derived unit for measuring illuminance.
Write this value down. You may wish to average some of the readings together.
In the code_light_sensor.py, modify the line APPLIANCE_ON_LUX = 30.0
to the light value you recorded.
Page last edited January 22, 2025
Text editor powered by tinymce.