Things with circuits, sensors, and solder. Firmware is yours to download — bring your own secrets.h where a sketch asks for one.
A solar-terrarium timelapse: an AI-Thinker ESP32-CAM wakes every 15 minutes, snaps one frame, reads temp/humidity off a KY-015, and POSTs both to a Cloudflare Worker (R2 + D1). The site flips through them as day, week, month, or all-time reels. Camera runs a patched esp32-camera driver to tame an OV3660 clone mislabeled as an OV2640.
A wearable UV-exposure tracker for photosensitive people. ESP32-C3 + VEML6075 logs UVA/UVB/IR to microSD and syncs to a self-hosted Flask server. Companion to sardinetracker.
An ESP32-C3 four-button memory game — LED-backlit buttons, piezo tones, LiPo-powered. A Simon clone with a typo.
Attempt at reverse-engineering disposable vapes to reflash their MCUs (Puya PY32, Nations N32G031) and upcycle the e-waste into reprogrammable gadgets — including a vape-tamagotchi that's still in progress.
The board is a stock AI-Thinker ESP32-CAM. These pins are fixed by its traces, not a wiring choice — collected here so the map is in one place. The full reference (power, SCCB fix, KY-015) is in the downloadable notes.
| Signal | GPIO | Signal | GPIO |
|---|---|---|---|
| PWDN | 32 | D7 (Y9) | 35 |
| RESET | — | D6 (Y8) | 34 |
| XCLK | 0 | D5 (Y7) | 39 |
| SIOD (SDA) | 26 | D4 (Y6) | 36 |
| SIOC (SCL) | 27 | D3 (Y5) | 21 |
| VSYNC | 25 | D2 (Y4) | 19 |
| HREF | 23 | D1 (Y3) | 18 |
| PCLK | 22 | D0 (Y2) | 5 |
KY-015 (DHT11): DATA → GPIO 13, VCC → 3V3 (not 3.7 V), GND common. Stream at XCLK 10 MHz / VGA — 20 MHz gave garbage frames.
Why it "wouldn't work": the sensor was an OV3660 clone
mislabeled as an OV2640. It hard-refuses four cosmetic auto-white-balance
registers over SCCB, and the stock esp32-camera driver aborts the whole
init on the first refused write — so the camera never came up, in Arduino or ESP-IDF.
Nothing was wired wrong. Fix: two edits to sccb.c's SCCB_Write16()
— retry each write up to 8×, and return 0 on a NAK instead of aborting —
plus SCCB clocked at 20 kHz and the camera init'd first in app_main.