Archive for April 15, 2021

Watching Plants Grow


I’ve been into timelapse photograpy for a long time, often focused on driving the cost down by hacking existing digital cameras.

That follow up post (already written almost two years after the original work) promised project pictures that were never delivered, so here, I went spelunking in some nearly decade-old backups for them.



Recently, I came across the ESP32-Cam, which basically hits all of the cost/hardware requirements I was aiming for, nearly a decade ago.

It uses an Espressif ESP32, which I’m very familiar/comfortable with, and is about $6. The camera sensor is 2MP, which is small for images, but fine for video.

There are a lot of timelapse projects out there using this board, but none of them really have the features I’m looking for. Espressif even has an official “webcam” codebase that others have packaged up into cool projects. But here are my requirements:

  • A web interface to control capture / timing parameters
  • Serving up images over the web interface to check that those parameters are good
  • Saving images to SD card
  • Nonvolatile storage of the parameters, along with the current snapshot number, in order to gracefully recover from power failure
  • SD card images available to download over the web interface
  • Over-the-air updating
  • Reasonably well-laid out codebase that’s easy to hack on

Essentially, I want to be able to plug this into a hard-to-reach closet and not have to ever physically touch it again. Lots of projects have a subset of these features, but nothing has hit it all, that I have seen.

One of the other ideas I had to improve upon the typical timelapse design was to get the ESP32 periodically grabbing the real-world time from NTP servers. It’s set to run every half an hour, which is probably too often to make a difference that overcome the latency from hitting the NTP server. Regardless, it should keep average timelapse interval perfect over a long period of time.

So I hacked something together. Originally I used PlatformIO with the Arduino framework, because it’s quicker to get a project off the ground than the official Espressif ESP-IDF. The branch is still there, but I abandoned it after running into issues with partitioning, OTA updating, and the additional PSRAM hardware all working together. The main branch uses the ESP-IDF now.

It’s essentially a webserver that controls the camera. You can open up a page and modify settings, including timelapse intervals:


You can also get existing parameters:


And snap a one-off photo to check your setup without touching anything. That made this super easy to tweak with one hand, while holding a laptop with the other.

When a sequence is done, it’s easiest to grab the SD card and plug it into a computer to copy over the files. For longer sequences, maybe to check progress, it is totally possible to grab the images over the web interface. Just slow.

To stitch together all the images, initially I used this command:

ffmpeg -r 30 -start_number 1 -i %06d.jpg -vcodec mpeg4 -vf "transpose=1" -y movie.mp4

That results in a pixelated and honestly pretty crummy output. It’s fast, though.

This got much better results:

ffmpeg -start_number 1 -r:v "480/1" -i %06d.jpg -c:v libx264 -preset slow -vf "transpose=1" -y -f mp4 movie.mp4

ffmpeg is a little inscrutable, so the parameters that require tweaking warrant an explanation. Order matters, too – Some of the parameters affect the input, some affect the output, depending on position.

-r:v is the input frame rate, relative to the base (25 FPS) framerate. So a 250 frame video with -r:v 25/1 would be 10 seconds, while setting it to -r:v 50/1 would result in a 5 second video.

The -vf transpose option rotates the video. The ESP32-Cam’s camera is mounted sideways, but that’s a minor irritation.

To mount the device, tripods are the obvious choice. I created a quick, minimalist enclosure that allows access to the GPIO pins, SD card, reset button, and fits a 1/4″-20 nut that is compatible with most tripods. A Gorillapod, in my case.


For the video posted at the top of the page, here is the setup:


It was done with a picture taken every 40 seconds, 8 second exposure, low gain and brightness.

ffmpeg -start_number 1 -r:v "120/1" -i %06d.jpg -c:v libx264 -preset slow -vf "transpose=1" -y -t 00:00:08 -f mp4 movie.mp4


The -t option sets the end-time, there was a lot of “content plant” at the end that wasn’t moving much.

Future goals might include shoving the whole mess into a closet, along with timed control of the lights and a watering pump, and leaving it for a month.