Back to all work
Personal build · 2026

ESP32-S3 Keychain Camera

Seeed XIAO ESP32-S3 Sense · Custom 3D-printed enclosure · Firmware & web UI

A pocket-sized WiFi-transfer keychain camera built on a Seeed XIAO ESP32-S3 Sense (OV3660 sensor), 16 GB microSD, and a 200 mAh Li-Po in a custom 3D-printed enclosure. One-button operation: tap to snap, later dump photos to a phone over WiFi — no app, no cable.

Firmware complete Enclosure printed & assembled
Exploded view of the camera — XIAO ESP32-S3 Sense with the OV3660 module and tactile button, the printed keychain body, and the translucent press-fit lid.
What it crosses
ESP32 FirmwareCamera DriverDeep Sleep / Low PowerSD File I/OWiFi Access PointHTTP + Web UISolidWorks Enclosure

The engineering interest lives at the seams: an OV3660 camera driver that has to come up cleanly on every wake, deep-sleep bookkeeping tight enough that a 200 mAh cell lasts weeks of casual use, a WiFi access-point + HTTP server that serves photos straight from the SD card to a phone browser without any app, and an enclosure printed to hold the whole thing at keychain scale.

Design

Built around one button

A single-button UX for a device that doesn't have room for anything else. Press duration disambiguates every mode.

ActionWhat it does
Single tapWakes the camera, captures a photo, saves as YYYY-MM-DD_HH-MM-SS.jpg, returns to deep sleep. Filenames sort chronologically.
Hold 3 secondsEnters WiFi transfer mode — broadcasts an access point. Connect from your phone, then open a browser to 192.168.4.1 for the photo gallery.
Tap a photo (WiFi mode)Opens a lightbox — long-press the image to save it to the phone's camera roll.
Single tap (WiFi mode)Exits WiFi mode, returns to normal sleep/capture.
Idle 2 minutes (WiFi mode)Auto-exits back to sleep to save battery.
Hardware

Bill of materials

MCUSeeed XIAO ESP32-S3 Sense
Dual-core Xtensa LX7 @ 240 MHz, 8 MB PSRAM, WiFi/BLE, onboard OV3660 camera header
SensorOmniVision OV3660
3 MP CMOS camera, JPEG output
StoragemicroSD, 16 GB
JPEGs written via SD_MMC driver
Power200 mAh Li-Po
Deep-sleep between shots keeps this alive for weeks
InputTactile button
Wired to a wake-capable GPIO
BodyCustom 3D print (SolidWorks)
Two-piece press-fit enclosure with camera aperture and button dome
What I built

End to end

Firmware
  • Wake-from-deep-sleep bootpath: bring up camera → capture → JPEG-encode → write to SD → sleep. Every step measured for latency to keep the "tap to shot" window tight.
  • State machine for the three modes (sleep, capture, WiFi transfer) driven by button-press duration.
  • Filesystem hygiene: timestamped filenames, safe unmount on sleep entry.
Web UI
  • ESP32 acts as a WiFi AP + HTTP server, streaming JPEGs off the SD card to the phone's browser.
  • Themed gallery grid with a lightbox view — long-press to save to the camera roll, no app install required.
  • Auto-exit timer so the AP doesn't burn battery if the user forgets to close it.
Power
  • Deep sleep on a 200 mAh cell — the design point that makes a keychain camera possible.
  • The button is wired to a wake-capable GPIO, so the MCU is fully off between shots.
Enclosure
  • Two-piece press-fit body in SolidWorks; camera aperture, button dome, SD access, and charge-port cutout in the right places.
  • Printed and iterated on a home FDM; final fit tuned by shaving 0.15 mm off the lid press fit.
Why build this

Small builds, real skills

Deep-sleep bookkeeping, camera bring-up, filesystem I/O, WiFi networking, HTTP + a web UI, and a printed enclosure that has to actually fit the parts. Every one of those maps to a piece of embedded work you'd do on a shipping product — just at a scale that fits on a keychain.

Stack

Tools

Arduino-ESP32PlatformIOESP32 CameraWebServerSD_MMCDeep SleepSolidWorksGit