The BITX40 is dead. Long Live the BITX40!

Come Join the QRP-Tech Group!

Time Lapse Camera with the Raspberry Pi Zero W: Part 2

In April of 2018, I posted the article “Time Lapse Camera with the Raspberry Pi Zero W: Part 1” and introduced the project. At the end of that article I said that I needed more progress to cal it complete. I said “The photos aren’t time stamped except in the filename, and I need to get the photos off the Raspberry Pi Zero W and into a format that anyone in the house could easily view, even away from home. Just like a Big Boy security camera. It will involve learning more Python, some linux programs like ffmpeg, and eventually a rain proof enclosure so it can be mounted outdoors.”

While I have to admit it’s not exactly as perfect as I’d hoped on the software side of things (mostly because I’m not very good at programming and stopped work when I had it ‘good enough’) the hardware side is complete. Let’s have a look, and be sure to go all the way to the end for Pictures and Video!

A Look at The Hardware

The main ingredient to the Raspberry Pi Zero W Time Lapse Camera is of course the Raspberry Pi Zero W which you can get over at Seeed Studio. I am using a 5v regulator with a 12v power supply rather than trying to use a 5V power supply. My junk box is full of old wall warts and so all I needed was a coaxial power plug. Those are easy to come by and could even be salvaged from something else.

Of course you also need the camera kit for the Raspberry Pi Zero, and you can find one to your liking here. A word of advice: Unless you’re going to illuminate the area with some infrared LED’s, be sure to get a camera with the IR filter in it. I got one without the IR filter, and then bought a switchable filter which you can see in the pictures. I even made a H-Bridge for it to turn it on and off based on measuring the luminescence of photos every 5 minutes. But, it did no good. I eventually unplugged it and left the IR filter intact.

The case that I have was built by a friend who has access to CNC routers and the like, but something else would work as well. A piece of black construction paper cuts back the glare and finishes off the build.

In the pictures you’ll also see an OTG cable with a wifi adapter. This is because the wifi on the Pi Zero W died on me. I was not pleased! But it was cheaper than buying a new Pi, and also has 5ghz capability, so it was an upgrade. It’s also a great way to add wireless to your Raspberry Pi Zero.

The Software

The software has been written by hand or borrowed from various online sources. It’s a mashup of Bash and Python. While the 32GB MicroSD card has enough room for a couple of days worth of photos, I have a Linux server that’s configured to get the latest photos every 15 minutes. The Pi Zero W deletes old photos daily, and that way it never runs out of space.The Linux server is far more powerful than the Pi and combines the previous days photos into a time lapse every day. It works quite nicely and has been stable for months.

Here’s the software below. It’s a bit of a mess!

crons for the rpi:
@reboot /usr/bin/python3 /home/pi/
#*/1 * * * * /bin/bash /home/pi/ #If you use a switching filter. Optional.
*/5 * * * * /bin/date >> ~/i_am_alive
@daily mv ~/today/* ~/yesterday;/usr/bin/find ~/yesterday -ctime +1 -exec rm -f {} \;

------------, optional

#uses to measure brightness of an image, and writes "dark" or "light" 
#to a file which is later read by the cam program itself.

bn=$(/usr/bin/python3 /home/pi/ `ls -1r /home/pi/images/*.jpg | head -1`)
if (( $( echo "$bn < $threshold" | bc -l) )); then
        echo "dark" > /home/pi/brightstatus
        echo "light" > /home/pi/brightstatus


BRIGHTNESS controlled IR filter
# pi@khcam:~ $ ./ images/05-13-18--05-07-58.jpg
# images/05-13-18--05-07-58.jpg   0.09469119831919676
# cron job runs every 5 minutes, tests the luminance. If it's < 0.08 then the IR filter is OFF.
# also optional
# Measures luminescence writes it to STDOUT.

import sys
from PIL import Image

def calculate_brightness(image):
    greyscale_image = image.convert('L')
    histogram = greyscale_image.histogram()
    pixels = sum(histogram)
    brightness = scale = len(histogram)

    for index in range(0, scale):
        ratio = histogram[index] / pixels
        brightness += ratio * (-scale + index)

    return 1 if brightness == 255 else brightness / scale

if __name__ == '__main__':
    for file in sys.argv[1:]:
        image =

import PIL
from PIL import Image, ImageFont, ImageDraw
font = ImageFont.truetype('/home/pi/DejaVuSans.ttf',25)

from time import sleep
import time
import picamera
with picamera.PiCamera() as camera:
        camera.resolution = (2048, 1536)
        for filename in camera.capture_continuous('/home/pi/today/{timestamp:%m-%d-%Y--%H-%M-%S}.jpg'):
#               Reads the filter status and writes it to the image
#               with open('filter.status') as f:
#                       filter_status =
#               f.closed
                im1 =
                draw = ImageDraw.Draw(im1)
                ts = time.ctime()
                draw.rectangle(((0, 5), (700, 30)), fill="black")
                draw.text((0, 5),'Time Lapse Cam '+str(ts),(255,255,0),font=font)
#               draw.text((500, 5),'IR Filter: '+str(filter_status),(255,255,0),font=font)
                draw = ImageDraw.Draw(im1)
      , quality=100)

# Turn off the IR filter
import RPi.GPIO as GPIO
import time
with open('filter.status', 'w') as f:
        write_data =f.write('off')
# Turn on the IR filter
import RPi.GPIO as GPIO
import time
with open('filter.status', 'w') as f:
        write_data =f.write('on')

if the video is at 10 frames per second, then it'll be easy to calculate by video time which frames to display.
use a video player that can jog, and then give links to proper images based on video time.

On the server:
date=$(date '+%m-%d-%Y')
echo "---------------------------------------" >> ~/cron.log
echo "Cron Start: "$(date) >> ~/cron.log;
pgrep rsync -c  > /dev/null || rsync -av -e ssh  pi@$date*.jpg ~/images/today/ > /dev/null 2>&1;
#pgrep rsync -c  > /dev/null || rsync -av -e ssh  pi@$date*.jpg ~/images/today/
echo "Cron End: "$(date) >> ~/cron.log

date=$(date -d "yesterday 13:00" '+%m-%d-%Y')
#echo $date
#mkdir ~/images/$date; cd ~/images/$date; rsync -vr -e ssh pi@$date* .
ffmpeg -threads 1 -framerate 18 -pattern_type glob -hwaccel vdpau -i "/home/khcam/images/today/$date*.jpg" -c:v libx264 ~/images/video/timelapse-$date.mp4
mkdir /home/khcam/images/$date
mv /home/khcam/images/today/$date*.jpg /home/khcam/images/$date

 crontab -e
10 0 * * * ~/
#*/15 * * * * echo "---------------------------------------" >> ~/cron.log; echo "Cron Start: "$(date) >> ~/cron.log; pgrep rsync -c  > /dev/null || rsync -av -e ssh  pi@ ~/ > /dev/null 2>&1; echo "Cron End: "$(date) >> ~/cron.log
*/15 * * * * ~/

Lastly, I have Apache configured on the server so that I can view the videos that are generated. Here are pictures of the build, and one of the resulting lapses.

1 comment

    • Robin on November 6, 2019 at 8:37 AM
    • Reply

    Excellent! Been wanting to find a fun and practical use for the old Pi3 and some JB cameras!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.