Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, plus the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2022 Matt Desmarais for Adafruit Industries
#
# SPDX-License-Identifier: MIT
from os import getenv
import time
import ssl
import microcontroller
import socketpool
import wifi
import board
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import digitalio
from adafruit_debouncer import Debouncer
#setup buzzer1
buzzer1 = digitalio.DigitalInOut(board.D13)
buzzer1.direction = digitalio.Direction.OUTPUT
#setup buzzer2
buzzer2 = digitalio.DigitalInOut(board.D11)
buzzer2.direction = digitalio.Direction.OUTPUT
#setup left door switch
leftdoor = digitalio.DigitalInOut(board.D5)
leftdoor.direction = digitalio.Direction.INPUT
leftdoor.pull = digitalio.Pull.UP
leftswitch = Debouncer(leftdoor)
#setup right door switch
rightdoor = digitalio.DigitalInOut(board.D9)
rightdoor.direction = digitalio.Direction.INPUT
rightdoor.pull = digitalio.Pull.UP
rightswitch = Debouncer(rightdoor)
#setup motion sensor
pir = digitalio.DigitalInOut(board.D6)
pir.direction = digitalio.Direction.INPUT
motion = Debouncer(pir)
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")
if None in [ssid, password, aio_username, aio_key]:
raise RuntimeError(
"WiFi and Adafruit IO settings are kept in settings.toml, "
"please add them there. The settings file must contain "
"'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
"'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
)
# WiFi
try:
print(f"Connecting to {ssid}")
wifi.radio.connect(ssid, password)
print(f"Connected to {ssid}!")
# Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad.
except Exception as e: # pylint: disable=broad-except
print("Failed to connect to WiFi. Error:", e, "\nBoard will restart in 5 seconds.")
time.sleep(5)
microcontroller.reset()
# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl.create_default_context(),
)
# Define callback functions which will be called when certain events happen.
def connected():
print("Connected to Adafruit IO! Listening for Freezer changes...")
# Initialize Adafruit IO MQTT "helper"
io = IO_MQTT(mqtt_client)
# Set up the callback methods above
io.on_connect = connected
#start time for timed uploads
start = int(time.time()/300)
#door timers set start times to now
start1 = time.monotonic()
start2 = time.monotonic()
#door alarms set to False
prealarm = False
alarm1 = False
alarm2 = False
door1feed = "unit-6.door1"
door2feed = "unit-6.door2"
motionfeed = "unit-6.motion"
alarmfeed = "unit-6.alarm"
resolvedfeed = "unit-6.resolved"
#reconnectedfeed = "unit-6.reconnected"
#initial publishes all zeros
try:
io.connect()
# Adafruit IO fails with internal error types and WiFi fails with specific messages.
# This except is broad to handle any possible failure.
except Exception as e: # pylint: disable=broad-except
print("Failed to get or send data, or connect. Error:", e,
"\nBoard will restart in 20 seconds.")
time.sleep(20)
microcontroller.reset()
io.publish(alarmfeed, 0)
io.publish(resolvedfeed, 0)
while True:
try:
# If Adafruit IO is not connected...
if not io.is_connected:
# Connect the client to the MQTT broker.
print("Connecting to Adafruit IO...")
io.connect()
time.sleep(1)
#update leftswitch
leftswitch.update()
#if door closed upload to IO
if leftswitch.fell:
print('left closed')
io.publish(door1feed, 1)
time.sleep(.25)
io.publish(door1feed, 0)
#if door opened upload to IO, set start1 to now
if leftswitch.rose:
print('left opened')
io.publish(door1feed, 1)
start1 = time.monotonic()
#if door remains open
if leftswitch.value:
print('still left open')
#if door remains closed, reset start1
else:
#door still closed reset timer
print('still left closed')
start1 = time.monotonic()
#update rightswitch
rightswitch.update()
#if door closed upload to IO
if rightswitch.fell:
print('right closed')
io.publish(door2feed, 1)
time.sleep(.25)
io.publish(door2feed, 0)
#if door opened upload to IO, set start2 to now
if rightswitch.rose:
print('right opened')
io.publish(door2feed, 1)
start2 = time.monotonic()
if rightswitch.value:
print('still right open')
#door still closed reset timer
else:
print('still right closed')
start2 = time.monotonic()
#if a door closes update both switches
if rightswitch.fell or leftswitch.fell:
rightswitch.update()
leftswitch.update()
#if both doors are closed
if not rightswitch.value and not leftswitch.value:
print('doors just closed')
#if prelarm is true then set it to False
if prealarm is True:
buzzer1.value = False
#if an alarm is true then upload to IO alarm resolved
if alarm1 or alarm2:
#publish 0 to alarm feed
io.publish(alarmfeed, 0)
#buzzers off/Alarms to False
buzzer1.value = False
buzzer2.value = False
alarm1 = False
alarm2 = False
#toggle alarm resolved feed to send email notification
io.publish(resolvedfeed, 1)
time.sleep(5)
io.publish(resolvedfeed, 0)
#check motion sensor if there is no alarm
if(not alarm1 and not alarm2 and not prealarm):
#update pir sensor
motion.update()
#if motion stopped
if motion.fell:
print('motion stopped')
#publish 0 to motion feed
io.publish(motionfeed, 1)
time.sleep(.25)
io.publish(motionfeed, 0)
#if motion started
if motion.rose:
print('motion detected')
#reset start times
start1 = time.monotonic()
start2 = time.monotonic()
#if continued motion
elif motion.value:
print('still motion')
io.publish(motionfeed, 1)
time.sleep(5)
#if continued no motion
else:
print('no motion')
print("\n")
# Explicitly pump the message loop to avoid MQTT timeouts.
io.loop()
#check difference between time now and start times if more than N seconds start beeping
if (((time.monotonic() - start1) >= 300) or ((time.monotonic() - start2) >= 300)):
prealarm = True
#beeping
buzzer1.value = True
time.sleep(.5)
buzzer1.value = False
#check if difference between time now and start1 if more than X seconds turn on buzzer2
if (alarm1 is False and ((time.monotonic() - start1) >= 600)):
alarm1 = True
buzzer2.value = True
#publish 1 to alarm feed
io.publish(alarmfeed, 1)
#check if difference between time now and start2 if more than X seconds turn on buzzer2
if (alarm2 is False and ((time.monotonic() - start2) >= 600)):
alarm2 = True
buzzer2.value = True
#publish 1 to alarm feed
io.publish(alarmfeed, 1)
#check if 300 seconds have passed compared to start time, if so publish values
if int(time.time()/300) > start:
print("PUBLISH EVERY FIVE MINUTES")
start = int(time.time()/300)
io.publish(door1feed, int(leftswitch.value))
io.publish(door2feed, int(rightswitch.value))
io.publish(motionfeed, int(motion.value))
# Adafruit IO fails with internal error types and WiFi fails with specific messages.
# This except is broad to handle any possible failure.
except Exception as e: # pylint: disable=broad-except
print("Failed to get or send data, or connect. Error:", e,
"\nBoard will restart in 20 seconds.")
time.sleep(20)
microcontroller.reset()
How It Works
Opening a door starts a timer, if the timer hits 5 minutes while there is no motion detected then the first buzzer starts doing prealarm beeping to catch someones attention to close it before 10 minutes elapsed of the door open with no motion detected the 2nd buzzer kicks in and an email is sent.
Libraries
- time
- ssl
- microcontroller
- socketpool
- wifi
- board
- adafruit_minimqtt
- adafruit_io.adafruit_io
- digitalio
- adafruit_debouncer
import time import ssl import microcontroller import socketpool import wifi import board import adafruit_minimqtt.adafruit_minimqtt as MQTT from adafruit_io.adafruit_io import IO_MQTT import digitalio from adafruit_debouncer import Debouncer
#setup buzzer1 buzzer1 = digitalio.DigitalInOut(board.D13) buzzer1.direction = digitalio.Direction.OUTPUT #setup buzzer2 buzzer2 = digitalio.DigitalInOut(board.D11) buzzer2.direction = digitalio.Direction.OUTPUT
Door Sensors
Setup Debouncer for both door switches pins D9 & D5. If once you install your alarm you realize that the doors are reversed, swap the pins for leftdoor and rightdoor.
#setup left door switch leftdoor = digitalio.DigitalInOut(board.D9) leftdoor.direction = digitalio.Direction.INPUT leftdoor.pull = digitalio.Pull.UP leftswitch = Debouncer(leftdoor) #setup right door switch rightdoor = digitalio.DigitalInOut(board.D5) rightdoor.direction = digitalio.Direction.INPUT rightdoor.pull = digitalio.Pull.UP rightswitch = Debouncer(rightdoor)
#setup motion sensor pir = digitalio.DigitalInOut(board.D6) pir.direction = digitalio.Direction.INPUT motion = Debouncer(pir)
Variables
- start 1 & 2: is the "start" time of the timer
- alarm 1 & 2: is based the state of the timer
- feed names: door1, door2, motion, alarm and resolved
#door timers set start times to now start1 = time.monotonic() start2 = time.monotonic() #door alarms set to False alarm1 = False alarm2 = False door1feed = "unit-6.door1" door2feed = "unit-6.door2" motionfeed = "unit-6.motion" alarmfeed = "unit-6.alarm" resolvedfeed = "unit-6.resolved"
Door Logic
This section determines the state of the door, everything is repeated for the right door. If the door was closed (fell) then publish 1 then 0 to the door feed. If the door was opened (rose) publish 1 to door feed and reset start time. If the door is open do nothing and if the door is closed reset the start time.
#update leftswitch
leftswitch.update()
#if door closed upload to IO
if leftswitch.fell:
print('left closed')
io.publish(door1feed, 1)
time.sleep(.25)
io.publish(door1feed, 0)
#if door opened upload to IO, set start1 to now
if leftswitch.rose:
print('left opened')
io.publish(door1feed, 1)
start1 = time.monotonic()
#if door remains open
if leftswitch.value:
print('still left open')
#if door remains closed, reset start1
else:
#door still closed reset timer
print('still left closed')
start1 = time.monotonic()
Alarm resolution
This section checks if a door just closed, then if both doors are closed, then if an alarm is true, if it is all true then resolved feed is toggled to send the alarm resolved notification
#if a door closes
if rightswitch.fell or leftswitch.fell:
#if both doors are closed
if not rightswitch.value and not leftswitch.value:
print('doors just closed')
#if an alarm is true then upload to IO alarm resolved
if alarm1 or alarm2:
#publish 0 to alarm feed
io.publish(alarmfeed, 0)
#buzzers off/Alarms to False
buzzer1.value = False
buzzer2.value = False
alarm1 = False
alarm2 = False
#toggle alarm resolved feed to send email notification
io.publish(resolvedfeed, 1)
time.sleep(5)
io.publish(resolvedfeed, 0)
Motion Sensor Logic
This section will only update the motion sensor if the alarms are not True, then it will update the motion sensor, if motion stopped publish 1 then 0 to motionfeed to make the graphs look nice. If motion was detected publish 1 to motionfeed and reset both start times.
#check motion sensor if there is no alarm
if(alarm1 == False and alarm2 == False):
#update pir sensor
motion.update()
#if motion stopped
if motion.fell:
print('motion stopped')
#publish 0 to motion feed
io.publish(motionfeed, 1)
time.sleep(.25)
io.publish(motionfeed, 0)
#if motion started
if motion.rose:
print('motion detected')
#publish 1 to motion feed
io.publish(motionfeed, 1)
#reset start times
start1 = time.monotonic()
start2 = time.monotonic()
#if continued motion
if motion.value:
print('still motion')
#if continued no motion
else:
print('no motion')
Prealarm & Alarm Logic
If either door has been open for more than 5 minutes start beeping. then if either door has been open for more than 10 minutes activate the second buzzer and publish 1 to the alarm feed.
#check difference between time now and start times if more than N seconds start beeping
if (((time.monotonic() - start1) >= 300) or ((time.monotonic() - start2) >= 300)):
#beeping
buzzer1.value = True
time.sleep(.5)
buzzer1.value = False
#check if difference between time now and start1 if more than X seconds turn on buzzer2, publish 1 to alarm feed
if (alarm1 == False and ((time.monotonic() - start1) >= 600)):
alarm1 = True
buzzer2.value = True
#publish 1 to alarm feed
io.publish(alarmfeed, 1)
#check if difference between time now and start2 if more than X seconds turn on buzzer2, publish 1 to alarm feed
if (alarm2 == False and ((time.monotonic() - start2) >= 600)):
alarm2 = True
buzzer2.value = True
#publish 1 to alarm feed
io.publish(alarmfeed, 1)
if(int(time.time()/300) > start):
print("PUBLISH EVERY FIVE MINUTES")
start = int(time.time()/300)
io.publish(door1feed, int(leftswitch.value))
io.publish(door2feed, int(rightswitch.value))
io.publish(motionfeed, int(motion.value))
Page last edited January 21, 2025
Text editor powered by tinymce.