|
| 1 | +# SPDX-FileCopyrightText: 2023 Brent Rubell for Adafruit Industries |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | +# |
| 4 | +# An open-source IoT birdfeeder camera with Adafruit MEMENTO |
| 5 | + |
| 6 | +import os |
| 7 | +import ssl |
| 8 | +import binascii |
| 9 | +import board |
| 10 | +import digitalio |
| 11 | +import socketpool |
| 12 | +import wifi |
| 13 | +import adafruit_pycamera |
| 14 | +import adafruit_requests |
| 15 | +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError |
| 16 | + |
| 17 | +print("MEMENTO Birdfeeder Camera") |
| 18 | + |
| 19 | +### WiFi ### |
| 20 | +# Add settings.toml to your filesystem CIRCUITPY_WIFI_SSID and CIRCUITPY_WIFI_PASSWORD keys |
| 21 | +# with your WiFi credentials. DO NOT share that file or commit it into Git or other |
| 22 | +# source control. |
| 23 | + |
| 24 | +# Set your Adafruit IO Username, Key and Port in settings.toml |
| 25 | +# (visit io.adafruit.com if you need to create an account, |
| 26 | +# or if you need your Adafruit IO key.) |
| 27 | +aio_username = os.getenv("ADAFRUIT_AIO_USERNAME") |
| 28 | +aio_key = os.getenv("ADAFRUIT_AIO_KEY") |
| 29 | + |
| 30 | +#print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}") |
| 31 | +wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) |
| 32 | +#print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}!") |
| 33 | + |
| 34 | +pool = socketpool.SocketPool(wifi.radio) |
| 35 | +requests = adafruit_requests.Session(pool, ssl.create_default_context()) |
| 36 | + |
| 37 | +# Initialize an Adafruit IO HTTP API object |
| 38 | +io = IO_HTTP(aio_username, aio_key, requests) |
| 39 | + |
| 40 | +try: |
| 41 | + # Get the 'birdfeeder' feed from Adafruit IO |
| 42 | + feed_camera = io.get_feed("birdfeeder") |
| 43 | +except AdafruitIO_RequestError: |
| 44 | + # If no 'birdfeeder' feed exists, create one |
| 45 | + feed_camera = io.create_new_feed("birdfeeder") |
| 46 | + |
| 47 | +# initialize camera |
| 48 | +pycam = adafruit_pycamera.PyCamera() |
| 49 | +# turn off the display backlight |
| 50 | +pycam.display.brightness = 0.0 |
| 51 | +# set photo resolution |
| 52 | +pycam.resolution = 3 |
| 53 | +# set focus to estimated bird location |
| 54 | +pycam.autofocus_vcm_step = 145 |
| 55 | + |
| 56 | +# initialize PIR sensor |
| 57 | +pir = digitalio.DigitalInOut(board.A0) |
| 58 | +pir.direction = digitalio.Direction.INPUT |
| 59 | + |
| 60 | +def send_jpeg_to_io(): |
| 61 | + # before we send the image to IO, it needs to be encoded into base64 |
| 62 | + encoded_data = binascii.b2a_base64(jpeg).strip() |
| 63 | + # then, send the encoded_data to Adafruit IO camera feed |
| 64 | + print("Sending image to IO...") |
| 65 | + io.send_data(feed_camera["key"], encoded_data) |
| 66 | + print("Sent image to IO!") |
| 67 | + |
| 68 | +print("Waiting for movement...") |
| 69 | +old_pir_value = pir.value |
| 70 | +while True: |
| 71 | + pir_value = pir.value |
| 72 | + # if we detect movement, take a photo |
| 73 | + if pir_value: |
| 74 | + if not old_pir_value: |
| 75 | + print("Movement detected, taking picture!") |
| 76 | + # take a picture and save it into a jpeg bytes object |
| 77 | + jpeg = pycam.capture_into_jpeg() |
| 78 | + # if the camera successfully captured a jpeg, send it to IO |
| 79 | + if jpeg is not None: |
| 80 | + send_jpeg_to_io() |
| 81 | + else: |
| 82 | + print("ERROR: JPEG capture failed!") |
| 83 | + else: |
| 84 | + if old_pir_value: |
| 85 | + print("Movement ended") |
| 86 | + # update old_pir_value |
| 87 | + old_pir_value = pir_value |
0 commit comments