Secrets File Setup for Adafruit IO

If you don't have a file in your CIRCUITPY drive yet, create one and add the information about your wifi connection.

Then, add the following code to your file, replacing _your_adafruit_io_username with your Adafruit IO username.

Then, replace _your_big_huge_super_long_aio_key_ with your Adafruit IO Active Key.

secrets = {
    'ssid' : '_your_wifi_ssid_',
    'password' : '_your_wifi_password_',
    'aio_username' : '_your_adafruit_io_username_',
    'aio_key' : '_your_big_huge_super_long_aio_key_',

Make sure you save this file before proceeding as in the root directory of your board CIRCUITPY drive.

Upload the code

Click "Download Project Bundle" below and unzip it on your Espressif Kaluga's CIRCUITPY drive. It will automatically start the code and upload a 640x480 JPEG to Adafruit IO every 3 seconds or so. If you run into trouble, open up the REPL to look for clues. Carefully double-check your WIFI and Adafruit IO login information, and that you properly created & configured the Adafruit IO Feed and Dashboard.

# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Jeff Epler for Adafruit Industries
# SPDX-License-Identifier: Unlicense

The Kaluga development kit comes in two versions (v1.2 and v1.3); this demo is
tested on v1.3.

The audio board must be mounted between the Kaluga and the LCD, it provides the
I2C pull-ups(!)

This example requires that your WIFI and Adafruit IO credentials be configured
in CIRCUITPY/, and that you have created a feed called "image" with
history disabled.

The maximum image size is 100kB after base64 encoding, or about 65kB before
base64 encoding.  In practice, "SVGA" (800x600) images are typically around
40kB even though the "capture_buffer_size" (theoretical maximum size) is
(width*height/5) bytes or 96kB.

import binascii
import ssl
import time
from secrets import secrets  # pylint: disable=no-name-in-module

import board
import busio
import wifi
import socketpool
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
import adafruit_ov2640

feed_name = "image"

print("Connecting to WIFI")["ssid"], secrets["password"])
pool = socketpool.SocketPool(

print("Connecting to Adafruit IO")
mqtt_client = MQTT.MQTT(
io = IO_MQTT(mqtt_client)

bus = busio.I2C(scl=board.CAMERA_SIOC, sda=board.CAMERA_SIOD)
cam = adafruit_ov2640.OV2640(

cam.flip_x = False
cam.flip_y = False
cam.test_pattern = False

cam.size = adafruit_ov2640.OV2640_SIZE_SVGA
cam.colorspace = adafruit_ov2640.OV2640_COLOR_JPEG
jpeg_buffer = bytearray(cam.capture_buffer_size)
while True:
    jpeg = cam.capture(jpeg_buffer)
    print(f"Captured {len(jpeg)} bytes of jpeg data")

    # b2a_base64() appends a trailing newline, which IO does not like
    encoded_data = binascii.b2a_base64(jpeg).strip()
    print(f"Expanded to {len(encoded_data)} for IO upload")

    io.publish("image", encoded_data)

    print("Waiting 3s")

Watch the camera feed

Just open up the Dashboard on Adafruit IO. The image in your browser will update shortly after each new image is uploaded from the ESP32-S2.

Take it Further

Here are some improvements and changes you might want to make to the code:

  • WiFi camera: Add a button to take a photo, and an LCD to act as a viewfinder.
  • Add a button to the Adafruit IO Dashboard that signals the camera to upload a fresh photo
  • Video Doorbell: Add a button to take a photo, and send you a notification on your phone (requires an IFTTT account)
  • Use a PC or Raspberry Pi to download & analyze the images

This guide was first published on Aug 04, 2021. It was last updated on Aug 04, 2021.

This page (CircuitPython Code) was last updated on May 13, 2022.

Text editor powered by tinymce.