PlantCam (how it works)

It’s currently visible at /plantcam, (at least for a little while).

The camera is a raspberry pi “noir” camera v2. Sensor data is captured using a BME280 sensor.

Basil Plant
Most Recent Frame - Here's a live look at one of my plants, taken using a raspberry pi camera.

Capture

I’m using raspistill to capture the photos. It’s adding an annotation (with some metrics from a BME280 environment sensor) on top of the image. This runs every half-hour using a cron-job.

raspistill \
  -ae 70,0xff,0x808000 \
  -a 1024 \
  -a " PlantCam 'Basil' - $(date) \n $(temp-metrics.sh) " \
  -e jpg \
  -th none \
  -o "timelapse/plant-$(date +'%Y-%m-%d_%H_%M_%S').jpg"

Low Quality Capture

This variant with reduced quality is run every minute and is used for the live photo.

raspistill \
  -ae 20,0xff,0x808000 \
  -a 1024 \
  -a " PlantCam 'Basil' - $(date) \n $(temp-metrics.sh) " \
  -w 640 \
  -h 480  \
  -q 60 \
  -e jpg \
  -th none \
  -o latest.jpg

Compile Timelapse to Video

To compile the frames into an mp4 video, I’m combining some info from my previous post about using ffmpeg, with a trick to easily grab all the photos using a glob:

ffmpeg \
  -y \ # Always overwrite
  -f image2 \ # Force input type to image2
  -pattern_type glob \ # Use input pattern as a glob
  -i 'timelapse/*.jpg' \ # Glob all the files
  -vcodec libx264 \ # Use the h264 video codec
  -pix_fmt yuv420p \ # Apple Quicktime support
  -profile:v baseline \ # For compatibility
  -level 3 \
  -movflags faststart \ # <- move the moov atom
  timelapse/output.mp4

The resulting video is served on a hidden web server and then served to end users via CloudFront.

Cache Control

I’m setting Cache-Control: max-age=<duration> on my nginx server to ensure cloudfront doesn’t cache images & videos too long. The duration (seconds) is set to the interval of each script.

Camera Setup

The camera is secured to the lightbar using some velcro cable ties, and the bottom half of one of these camera mounts.

Camera and Sensor Mounting
Camera Setup

Change Log

  • 2020-06-16 - Initial Revision

Found a typo or technical problem? file an issue!