Once you've finished setting up your MEMENTO camera with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2023 Brent Rubell for Adafruit Industries # SPDX-License-Identifier: MIT # # An open-source IoT birdfeeder camera with Adafruit MEMENTO import os import ssl import binascii import board import digitalio import socketpool import wifi import adafruit_pycamera import adafruit_requests from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError print("MEMENTO Birdfeeder Camera") ### WiFi ### # Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys # with your WiFi credentials. DO NOT share that file or commit it into Git or other # source control. # Set your Adafruit IO Username, Key and Port in settings.toml # (visit io.adafruit.com if you need to create an account, # or if you need your Adafruit IO key.) aio_username = os.getenv("ADAFRUIT_AIO_USERNAME") aio_key = os.getenv("ADAFRUIT_AIO_KEY") #print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}") wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) #print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}!") pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) try: # Get the 'birdfeeder' feed from Adafruit IO feed_camera = io.get_feed("birdfeeder") except AdafruitIO_RequestError: # If no 'birdfeeder' feed exists, create one feed_camera = io.create_new_feed("birdfeeder") # initialize camera pycam = adafruit_pycamera.PyCamera() # turn off the display backlight pycam.display.brightness = 0.0 # set photo resolution pycam.resolution = 3 # set focus to estimated bird location pycam.autofocus_vcm_step = 145 # initialize PIR sensor pir = digitalio.DigitalInOut(board.A0) pir.direction = digitalio.Direction.INPUT def send_jpeg_to_io(): # before we send the image to IO, it needs to be encoded into base64 encoded_data = binascii.b2a_base64(jpeg).strip() # then, send the encoded_data to Adafruit IO camera feed print("Sending image to IO...") io.send_data(feed_camera["key"], encoded_data) print("Sent image to IO!") print("Waiting for movement...") old_pir_value = pir.value while True: pir_value = pir.value # if we detect movement, take a photo if pir_value: if not old_pir_value: print("Movement detected, taking picture!") # take a picture and save it into a jpeg bytes object jpeg = pycam.capture_into_jpeg() # if the camera successfully captured a jpeg, send it to IO if jpeg is not None: send_jpeg_to_io() else: print("ERROR: JPEG capture failed!") else: if old_pir_value: print("Movement ended") # update old_pir_value old_pir_value = pir_value
After downloading the Project Bundle, plug your MEMENTO into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the MEMENTO's CIRCUITPY drive.
- lib folder
- code.py
Your MEMENTO CIRCUITPY drive should look like this after copying the lib folder and the code.py file.
The following code snippet connects the MEMENTO camera to a WiFi network.
### WiFi ### # Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys # with your WiFi credentials. DO NOT share that file or commit it into Git or other # source control. # Set your Adafruit IO Username, Key and Port in settings.toml # (visit io.adafruit.com if you need to create an account, # or if you need your Adafruit IO key.) aio_username = os.getenv("ADAFRUIT_AIO_USERNAME") aio_key = os.getenv("ADAFRUIT_AIO_KEY") print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}") wifi.radio.connect( os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD") ) print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}!")
The io
object is created to interface with the Adafruit IO HTTP API.
pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # Initialize an Adafruit IO HTTP API object io = IO_HTTP(os.getenv("ADAFRUIT_AIO_USERNAME"), os.getenv("ADAFRUIT_AIO_KEY"), requests)
We'll attempt to get the birdfeeder feed from Adafruit IO. If it does not exist on the account, the Adafruit IO CircuitPython library will automatically create one.
# Manage Adafruit IO Feed try: # Get the 'birdfeeder' feed from Adafruit IO feed_camera = io.get_feed("birdfeeder") except AdafruitIO_RequestError: # If no 'birdfeeder' feed exists, create one feed_camera = io.create_new_feed("birdfeeder")
The camera is initialized and its display backlight is disabled.
# initialize camera pycam = adafruit_pycamera.PyCamera() # turn off the display backlight pycam.display.brightness = 0.0
The PIR sensor is initialized as a digital input on the MEMENTO's A0 port.
# initialize PIR sensor pir = digitalio.DigitalInOut(board.A0) pir.direction = digitalio.Direction.INPUT
The loop reads the value of the PIR sensor. If the value of the PIR sensor has changed, movement is detected and the MEMENTO takes a photo. The photo is saved into a JPEG (bytes) object.
print("Waiting for movement...") old_pir_value = pir.value while True: pir_value = pir.value # if we detect movement, take a photo if pir_value: if not old_pir_value: print("Movement detected, taking picture!") # force camera autofocus pycam.autofocus() # take a picture and save it into a jpeg bytes object jpeg = pycam.capture_into_jpeg()
If the camera successfully captures a photo, the jpeg
object will be full of bytes. The code checks if the jpeg capture was successful and then calls a function to send the jpeg data to Adafruit IO.
If the image is not captured, an error will be printed to the REPL.
# if the camera successfully captured a jpeg, send it to IO if jpeg is not None: send_jpeg_to_io() else: print("ERROR: JPEG capture failed!")
The following function, send_jpeg_to_io()
, is called by the snippet above. In this function, the jpeg data gets encoded from bytes into a base 64 object to reduce its size. Then, the encoded data is sent to the Adafruit IO feed we specified earlier.
def send_jpeg_to_io(): """ Sends a JPEG image to Adafruit IO. """ # before we send the image to IO, it needs to be encoded into base64 encoded_data = binascii.b2a_base64(jpeg).strip() # then, send the encoded_data to Adafruit IO camera feed print("Sending image to IO...") io.send_data(feed_camera["key"], encoded_data) print("Sent image to IO!")
Finally, if the new value detected by the PIR sensor was the previous value, we'll print to the REPL that movement has stopped being detected. The old_pir_value
is updated each loop iteration.
else: if old_pir_value: print("Movement ended") # update old_pir_value old_pir_value = pir_value
Text editor powered by tinymce.