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 # # An open-source IoT doorbell with the Adafruit MEMENTO camera and Adafruit IO # # SPDX-License-Identifier: Unlicense import os import time import ssl import binascii import digitalio import adafruit_pycamera import board import wifi import socketpool import adafruit_requests from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError print("CircuitPython Doorbell 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(os.getenv("ADAFRUIT_AIO_USERNAME"), os.getenv("ADAFRUIT_AIO_KEY"), requests) # Adafruit IO feed configuration try: # Get the 'camera' feed from Adafruit IO feed_camera = io.get_feed("camera") except AdafruitIO_RequestError: # If no 'camera' feed exists, create one feed_camera = io.create_new_feed("camera") # Initialize memento camera pycam = adafruit_pycamera.PyCamera() # Turn off TFT backlight pycam.display.brightness = 0.0 # Deinitialize the MEMENTO's NeoPixels # Why? The pixels use board.A1 and we want to use it to control the doorbell LED pycam.pixels.deinit() # Set up the button pin_button = digitalio.DigitalInOut(board.A0) pin_button.direction = digitalio.Direction.INPUT pin_button.pull = digitalio.Pull.UP # Set up the button's LED led = digitalio.DigitalInOut(board.A1) led.direction = digitalio.Direction.OUTPUT led.value = True print("Doorbell ready to be pressed!") def capture_send_image(): """Captures an image and send it to Adafruit IO.""" # Force autofocus and capture a JPEG image pycam.autofocus() jpeg = pycam.capture_into_jpeg() print("Captured image!") if jpeg is not None: # Encode JPEG data into base64 for sending to Adafruit IO print("Encoding image...") encoded_data = binascii.b2a_base64(jpeg).strip() # Send encoded_data to Adafruit IO camera feed print("Sending image to Adafruit IO...") io.send_data(feed_camera["key"], encoded_data) print("Sent image to IO!") else: print("ERROR: JPEG frame capture failed!") while True: # Wait until the doorbell is pressed if not pin_button.value: print("Doorbell pressed!") # Turn the doorbell LED off to signal that it has been pressed led.value = False # Play a doorbell tone using the speaker pycam.tone(95, 0.5) pycam.tone(70, 0.5) capture_send_image() print("DONE, waiting for next press..") # Turn the LED on to signal that the doorbell is ready to be pressed again led.value = True time.sleep(0.01)
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.
WiFi Connect
This section of code connects to the WiFi SSID defined by the secrets.toml file.
### 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')}!")
Initialize Adafruit IO
This section of code creates a new Adafruit IO client object, which uses the Adafruit IO HTTP API to communicate with the Internet. Using the Adafruit IO client object, it attempts to get the Adafruit IO "camera" feed. If it fails to obtain this feed, it will create a new feed.
# Create new socket and SSL context 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) try: # Get the 'camera' feed from Adafruit IO feed_camera = io.get_feed("camera") except AdafruitIO_RequestError: # If no 'camera' feed exists, create one feed_camera = io.create_new_feed("camera")
Initialize Camera
The camera is initialized and the TFT's backlight is turned off (since we're putting it in an enclosure - we don't need to waste power). The camera's default pixel strand is deinitialized as the project uses the A1 pin which later will be used to control the button's LED.
# Initialize memento camera pycam = adafruit_pycamera.PyCamera() # Turn off TFT backlight pycam.display.brightness = 0.0 # Deinitialize the MEMENTO's NeoPixels # Why? The pixels use board.A1 and we want to use it to control the doorbell LED pycam.pixels.deinit()
Set up Push-button
The push button is configured as an input with a pull-up on the pin A0
. Then, the push button's LED is configured as an output on the pin A1
. This lets us read from the push-button and control the LED separately.
# Set up the button pin_button = digitalio.DigitalInOut(board.A0) pin_button.direction = digitalio.Direction.INPUT pin_button.pull = digitalio.Pull.UP # Set up the button's LED led = digitalio.DigitalInOut(board.A1) led.direction = digitalio.Direction.OUTPUT led.value = True
Main Loop
The code's main, while True
, loop waits for the doorbell button to be pressed (if not pin_button.
value
). If the button is pressed, the LED is turned off to signal that something is happening. The MEMENTO's speaker also plays a descending tone, like a doorbell. The capture_send_image()
method is then called.
If a button press is not detected, the code waits 1 millisecond before running again. This delay is added for "debouncing" the button.
while True: # Wait until the doorbell is pressed if not pin_button.value: print("Doorbell pressed!") # Turn the doorbell LED off to signal that it has been pressed led.value = False # Play a doorbell tone using the speaker pycam.tone(95, 0.5) pycam.tone(70, 0.5) capture_send_image() time.sleep(0.01)
Capturing an Image
Within the capture_and_send_image()
method, the camera is forced to autofocus on the person pressing the button. Then an image is captured in JPEG format and its bytes are saved into an object, jpeg
.
def capture_send_image(): """Captures an image and send it to Adafruit IO.""" # Force autofocus and capture a JPEG image pycam.autofocus() jpeg = pycam.capture_into_jpeg() print("Captured image!")
Send the Image to Adafruit IO
While the image's binary data could be sent to Adafruit IO, we strongly recommend encoding images sent to Adafruit IO using base64 so you can store/retrieve it with REST and JSON. So, the image is encoded into base64 (b2a_base64(jpeg)
)and sent (io.send_data()
) to the camera feed on your Adafruit IO account.
This code section also contains a check for whether the image capture succeeded or failed (jpeg data would be None
).
if jpeg is not None: # Encode JPEG data into base64 for sending to Adafruit IO print("Encoding image...") encoded_data = binascii.b2a_base64(jpeg).strip() # Send encoded_data to Adafruit IO camera feed print("Sending image to Adafruit IO...") io.send_data(feed_camera["key"], encoded_data) print("Sent image to IO!") else: print("ERROR: JPEG frame capture failed!")
Text editor powered by tinymce.