Overview
Doorbell Camera
Build a simple IoT doorbell camera using Adafruit's MEMENTO. Press a mini LED arcade button to snap a photo and display it on an Adafruit IO Dashboard. The electronics are housed in a 3D printed snap fit enclosure.
Adafruit MEMENTO Camera
Adafruit's MEMENTO Camera Board is a development board with everything you need to create programmable camera and vision projects. The OV5640 camera module onboard the MEMENTO is a 5MP camera sensor auto-focus motor and a JPEG encoder built-in for reducing the image size. The ESP32-S3 module on the MEMENTO runs CircuitPython and sends images over WiFi to Adafruit.io.
Code with CircuitPython
This doorbell is programmed with CircuitPython which makes it easy to modify the project's code to fit your needs.
An IoT Doorbell with Privacy in Mind
This DIY doorbell won't share its data with 3rd parties as it adheres to Adafruit's IoT Bill of Rights. Data sent from the doorbell to Adafruit IO will be stored on a private Adafruit IO feed, the photos captured by the doorbell are only viewable by you.
The following part is optional and not required for the completion of this project. However, if you would like to set up SMS alerts, as described later in the guide, you will need an upgraded, paid, Adafruit IO subscription (Adafruit IO Plus). For information on what is included with an Adafruit IO Plus subscription, visit io.adafruit.com/plus
Hardware
The following hardware is required for the assembly.
- 6x M3 x 6mm long machine screws
- 3/8 to 1/4-20 Tripod screw insert (optional)
Page last edited March 08, 2024
Text editor powered by tinymce.
Get Started with Adafruit IO
Adafruit IO is integrated with your adafruit.com account so you don't need to create yet another online account! You need an Adafruit account to use Adafruit IO because we want to make sure the data you upload is available to only you (unless you decide to publish your data).
I have an Adafruit.com Account already
If you already have an Adafruit account, then you already have access to Adafruit IO. It doesn't matter how you signed up, your account will make all three available.
To access Adafruit IO, simply visit https://io.adafruit.com to start streaming, logging, and interacting with your data.
Create an Adafruit Account (for Adafruit IO)
An Adafruit account makes Adafruit content and services available to you in one place. Your account provides access to the Adafruit shop, the Adafruit Learning System, and Adafruit IO. This means only one account, one username, and one password are necessary to engage with the content and services that Adafruit offers.
If you do not have an Adafruit account, signing up for a new Adafruit account only takes a couple of steps.
Begin by visiting https://accounts.adafruit.com.
Click the Sign Up button under the "Need An Adafruit Account?" title, below the Sign In section.
This will take you to the Sign Up page.
Fill in the requested information, and click the Create Account button.
This takes you to your Adafruit Account home page. From here, you can access all the features of your account.
You can also access the Adafruit content and services right from this page. Along the top of the page, you'll see a series of links beginning with "Shop". To access any of these, simply click the link.
For example, to begin working with Adafruit IO, click the IO link to the right of the rest of the links. This is the same for the other links as well.
That's all there is to creating a new Adafruit account, and navigating to Adafruit IO.
Page last edited March 08, 2024
Text editor powered by tinymce.
Create Adafruit IO Feed
Feeds are the core of the Adafruit IO system. A feed holds data and meta-data that you have sent to Adafruit IO.
- Data is the information you want Adafruit IO to store. For example, image data from the MEMENTO's camera.
- Meta-data includes settings for the data such as privacy settings and data retention options.
Before using the doorbell camera, a new Adafruit IO Feed must be created to store the camera's image. Then, the feed's history must be disabled to allow larger data points to be sent to Adafruit IO, such as a large amount of data to represent an image.
To create a feed, navigate to your Adafruit IO Feeds page. Then, click New Feed.
Name the feed camera and click Create.
The feeds list now lists a camera feed. Click this feed.
Set History to OFF and click Save. This will change the feed's data storage from 1KB to 100KB, large enough to store image data.
Page last edited March 08, 2024
Text editor powered by tinymce.
Wiring
Make the following connections between the MEMENTO camera board and the metal pushbutton:
- MEMENTO A0 Signal to pushbutton NO
- MEMENTO A0 GND to pushbutton COM
- MEMENTO A1 Signal to pushbutton LED+
- MEMENTO A1 GND to pushbutton LED-
Power
The suggested battery for this project has a capacity of 420mAh for a total of about 1.55 Wh and plugs directly into the MEMENTO's 2-Pin JST-PH port.
However, the MEMENTO does run out of battery power quickly. We suggest plugging the MEMENTO into USB-C and using a wall power supply.
Page last edited March 08, 2024
Text editor powered by tinymce.
3D Printing
3D Printed Parts
STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material using PLA filament. Original design source files may be downloaded using the links below.
Build Volume
The parts require a 3D printer with a minimum build volume.
- 80mm (X) x 30mm (Y) x 66mm (Z)
CAD Assembly
The Memento PCB is secured to the PCB mount using machine screws. The PCB mount is secured to the camera case using machine screws. The mini LED arcade button is panel mounted to a sleeve and the button casing. The button case snap fits onto the camera case. The top and bottom covers snap fits on to the camera case.
Design Source Files
The project assembly was designed in Fusion 360. This can be downloaded in different formats like STEP, STL and more. Electronic components like Adafruit's boards, displays, connectors and more can be downloaded from the Adafruit CAD parts GitHub Repo.
Page last edited March 08, 2024
Text editor powered by tinymce.
Assembly
JST Cables
Get the 3-pin JST cables ready by cutting off the red wires from the sets.
Using wire stripper, remove a bit of insulation from each wire.
Tin the tips of the wires with a bit of solder to keep the strands of wire from fraying.
Solder Cables
Solder one of the 3-pin JST cable to the LED pins on the mini LED arcade button.
Solder the remaining JST cable to the switch pins.
Wired Button
Double check the wires have been properly soldered to the pins on the mini LED arcade button.
Memento PCB Mount
Get the Memento, PCB mount and hardware screws ready.
Place the PCB mount over the Memento PCB with the mounting holes lined up.
Secure Memento
Use four M3 x 6mm long machine screws to secure the PCB mount to the standoffs on the Memento camera board.
Install Button
Insert the 3D printed sleeve over the mini arcade button.
Slide the two JST cables through the front hole of the 3D printed button case.
Slip the included mini arcade button hex nut over the two JST cables.
Secure Button
Insert the mini arcade button with sleeve into the hole in the button case.
Secure the mini arcade button using the included hex nut.
Install Battery
Plug in the 420mAh lipo battery into the Memento's battery port.
Use a piece of a double sided tape or mounting tack to secure the battery to the Memento PCB.
Test Button
Locate the JST cable connected to the mini arcade button's LED and connect it to the A1 port on the Memento.
Plug in the JST cable from the buttons switch to the A0 port on the Memento.
Use the on-board slide switch to power on the Memento and test the circuit.
Connect Button to Memento
Disconnect the JST cables from them Memento.
Thread the two cables from the mini arcade button through the camera case.
Reconnect the JST cables to the Memento.
Double check the orientation of the board and case are correct.
Camera Case
Push the PCB mount into the camera case so that the Memento's USB port lines up with the cutout.
Snap Fit Cases
Adjust the excess wire from the JST cables so they're fitted inside the case.
Join the button case to the camera case by snap fitting them together.
Secure PCB mount to Case
Use two M3 x 6mm long machine screws to secure the PCB mount to the camera case.
Install Tripod Screw (Optional)
Get the 3/8 to 1/4-20 threaded tripod screw and bottom cover.
Insert and fasten the tripod screw into the thread on the bottom cover using a wide flat head tool.
Install Top Cover
Orient the top cover with the camera case so the built-in switch actuator is lined up with the Memento's built-in slide switch.
Press the top cover onto the camera case.
Page last edited March 08, 2024
Text editor powered by tinymce.
Install CircuitPython
CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.
CircuitPython Quickstart
Follow this step-by-step to quickly get CircuitPython running on your board.
import storage storage.erase_filesystem()
Your board will reboot after running this.
Click the link above to download the latest CircuitPython UF2 file.
Save it wherever is convenient for you.
Plug your board into your computer, using a known-good data-sync cable, directly, or via an adapter if needed.
Double-click the reset button (highlighted in red above), and you will see the RGB status LED(s) turn green (highlighted in green above). If you see red, try another port, or if you're using an adapter or hub, try without the hub, or different adapter or hub.
For this board, tap reset and wait for the LED to turn purple, and as soon as it turns purple, tap reset again. The second tap needs to happen while the LED is still purple.
If you do not see the LED turning purple, you will need to reinstall the UF2 bootloader. See the Factory Reset page in this guide for details.
If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!
A lot of people end up using charge-only USB cables and it is very frustrating! Make sure you have a USB cable you know is good for data sync.
You will see a new disk drive appear called CAMERABOOT. Drag the adafruit-circuitpython-adafruit_esp32s3_camera-etc.uf2 file to CAMERABOOT.
Page last edited March 08, 2024
Text editor powered by tinymce.
Create Your settings.toml File
CircuitPython works with WiFi-capable boards to enable you to make projects that have network connectivity. This means working with various passwords and API keys. As of CircuitPython 8, there is support for a settings.toml file. This is a file that is stored on your CIRCUITPY drive, that contains all of your secret network information, such as your SSID, SSID password and any API keys for IoT services. It is designed to separate your sensitive information from your code.py file so you are able to share your code without sharing your credentials.
CircuitPython previously used a secrets.py file for this purpose. The settings.toml file is quite similar.
CircuitPython settings.toml File
This section will provide a couple of examples of what your settings.toml file should look like, specifically for CircuitPython WiFi projects in general.
The most minimal settings.toml file must contain your WiFi SSID and password, as that is the minimum required to connect to WiFi. Copy this example, paste it into your settings.toml, and update:
your_wifi_ssidyour_wifi_password
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password"
Many CircuitPython network-connected projects on the Adafruit Learn System involve using Adafruit IO. For these projects, you must also include your Adafruit IO username and key. Copy the following example, paste it into your settings.toml file, and update:
your_wifi_ssidyour_wifi_passwordyour_aio_usernameyour_aio_key
CIRCUITPY_WIFI_SSID = "your_wifi_ssid" CIRCUITPY_WIFI_PASSWORD = "your_wifi_password" ADAFRUIT_AIO_USERNAME = "your_aio_username" ADAFRUIT_AIO_KEY = "your_aio_key"
Some projects use different variable names for the entries in the settings.toml file. For example, a project might use ADAFRUIT_AIO_ID in the place of ADAFRUIT_AIO_USERNAME. If you run into connectivity issues, one of the first things to check is that the names in the settings.toml file match the names in the code.
Here is an example settings.toml file.
# Comments are supported CIRCUITPY_WIFI_SSID = "guest wifi" CIRCUITPY_WIFI_PASSWORD = "guessable" CIRCUITPY_WEB_API_PORT = 80 CIRCUITPY_WEB_API_PASSWORD = "passw0rd" test_variable = "this is a test" thumbs_up = "\U0001f44d"
In a settings.toml file, it's important to keep these factors in mind:
- Strings are wrapped in double quotes; ex:
"your-string-here" - Integers are not quoted and may be written in decimal with optional sign (
+1,-1,1000) or hexadecimal (0xabcd).- Floats (decimal numbers), octal (
0o567) and binary (0b11011) are not supported.
- Floats (decimal numbers), octal (
- Use
\uescapes for weird characters,\xand\oooescapes are not available in .toml files- Example:
\U0001f44dfor 👍 (thumbs up emoji) and\u20acfor € (EUR sign)
- Example:
- Unicode emoji, and non-ASCII characters, stand for themselves as long as you're careful to save in "UTF-8 without BOM" format
When your settings.toml file is ready, you can save it in your text editor with the .toml extension.
In your code.py file, you'll need to import the os library to access the settings.toml file. Your settings are accessed with the os.getenv() function. You'll pass your settings entry to the function to import it into the code.py file.
import os
print(os.getenv("test_variable"))
In the upcoming CircuitPython WiFi examples, you'll see how the settings.toml file is used for connecting to your SSID and accessing your API keys.
Page last edited March 08, 2024
Text editor powered by tinymce.
Code
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 ###
# 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!")
Page last edited March 08, 2024
Text editor powered by tinymce.
Enable Email Notifications
In Adafruit IO, Reactive Actions are a way to integrate basic logic into your IoT projects, without programming. At a basic level, they check if a feed's value is somehow comparable to a value or even another feed. If so, they can be configured to send an email notification, post a webhook, or even publish a message to another feed
Let's set up a Reactive Action to send an email when a new photo has been taken by the doorbell's camera.
On Adafruit.io, navigate to the actions page. From the actions page, click the "+ New Action" button.
On the next page, select the Reactive Action.
Configure the action to check if the camera feed has any new data (such as a new image posted to the feed).
At the bottom of the form, you can set the subject and body of the email.
The Limit Every option decides how often the action will run. If you want to receive an email every time the doorbell is pressed, set this option to run every Ten Seconds.
As an example, here's what my doorbell will email me when it is pressed:
After configuring the action, click Submit. The new action is listed on the Actions page with the status set to Active.
Since the camera feed hasn't received a value yet, this trigger has not been executed. We'll demonstrate this trigger on this guide's Usage page by pressing the doorbell button.
Page last edited March 08, 2024
Text editor powered by tinymce.
Usage
Plug the MEMENTO into power. When the code is ready, the doorbell's LED will turn on.
When someone pushes the button, the MEMENTO's buzzer will emit a chime and the LED will turn off.
Behind the scenes, the MEMENTO's camera autofocuses and takes a picture. The image is encoded to reduce its size and is sent to Adafruit IO.
To see the image, navigate to your Adafruit IO Feeds page and click on the camera feed.
The image captured by the camera should appear on the feed as a data-point and an image preview will be visible.
You should receive an email containing the photo captured by the MEMENTO doorbell, and a message saying there's a new visitor at your door.
Page last edited March 08, 2024
Text editor powered by tinymce.