Raspberry Pi Zero W as a headless time-lapse camera

tl;dr: There are many ways to capture time-lapse videos. But this one is cheap, completely wireless, and mine. If you want to skip the post and go straight for the glory, grab a copy of my Time-lapse app for the Raspberry Pi.

Time-lapses transform subtle, slow processes into something beautiful, and often make us think about things in new ways. For example, have you ever thought about just how heavy a wet snow is? The trees in your yard might know a thing or two about that! Check out a time-lapse I recorded this morning some mighty oak tree branches, as they relaxed upward as if in relief from the wet snow falling off:

Quality time-lapse photography used to require expensive equipment special knowledge, so it remained the domain of the specialist. Fast forward to 2017, and we have a $10 computer and a $30 camera that can create a wireless time-lapse device that can produce footage in any modern resolution—even up to 4K! Sure there are many cameras with built in intervalometers and lenses you can use to make more creative images... but would you be willing to leave your expensive equipment in places or situations where you risk damaging them? $50 of equipment, maybe... $1000+, no way! (At least, not for fun and exploration.)

The Raspberry Pi has freed me to think in new directions for computing (for example, check out my Raspberry Pi Dramble project), and it's also allowing me to expand my photography horizons. And if you want to tinker as well, all you need is this little guy:

Raspberry Pi Zero with Camera Cable and microSD card
The Raspberry Pi Zero W.

For this year's Pi Day (3.14), I decided to grab a Raspberry Pi Zero W from Micro Center, write up a small app, and record time-lapses. And I've decided to open source all my work, and share this blog post so you, too, can enjoy watching life in fast-forward!

Requirements

There are some programs you can download that are more automated, but (a) they usually require the use of a keyboard and monitor (which we don't want to use), and (b) they usually consume more power (meaning less battery life) and are less customizable than I desire.

Here are the things I wanted to have:

  • 100% portable—doesn't have to be plugged into mains electricity.
  • Stable, custom exposure and color control (so the video doesn't flicker).
  • Easy assembly of footage after the time-lapse is captured (to a gif or a video).
  • Reliable timing (so I can calculate how many frames/how much time-warp factor to record).

So for starters, I made sure I had the right hardware on hand. I'll run through my parts list, then explain the setup process below.

Parts needed

Raspberry Pi Zero W Time-Lapse portable AUKEY battery pack

Setting things up

I'll assume you're using a Mac for the setup process, but you can do the same thing on Windows if you use Ubuntu Bash or some other command line emulator that allows SSH access... and if you use Linux, you're golden—just pop open a Terminal window and follow along!

In this guide, we'll set up the Raspberry Pi headless—you don't need an extra monitor, keyboard, and mouse to plug into the Pi (nor do you need all the extra adapter cables to get it all hooked up!)

Put Raspbian on the microSD card

  1. Mount the microSD card (I use a microSD to SD adapter, then plug that into my USB SD card reader) on your Mac.
  2. Download Raspbian Lite (either as a .torrent or the direct .img download).
  3. Open Terminal, run diskutil list to see all connected volumes.
  4. Note the microSD card path (usually /dev/disk2 or /dev/disk3).
  5. Unmount the microSD card: diskutil unmountDisk /dev/disk2
  6. Write the image you downloaded to the card: sudo dd if=~/Downloads/2017-03-02-raspbian-jessie-lite.img of=/dev/rdisk2 bs=1m (should take a minute or less if you have a decent microSD card)

Initialize SSH and WiFi configuration.

  1. Open the boot volume on your Mac (should auto mount after the disk image is finished writing).
  2. Create an ssh file to tell the Pi to enable SSH when it boots up by default: touch /Volumes/boot/ssh
  3. Create a wpa_supplicant.conf file to tell the Pi to connect to your WiFi network on boot. Create a new file with that title in the boot volume, with the contents below:

    network={
        ssid="YOUR_SSID"
        psk="YOUR_PASSWORD"
        key_mgmt=WPA-PSK
    }
    
  4. Eject the microSD card, and stick it in the Raspberry Pi.

At this point, the Pi is fresh and new, and will boot up and connect to your WiFi network, allowing you to administer it via SSH.

Connect to the Pi

Assuming your WiFi network uses DHCP to assign IP addresses to devices (this is almost universally true), you need to figure out the IP address your Pi acquired when it booted. Use one of the following two options:

# Use nmap.
$ sudo nmap -sP 10.0.1.1/24

# Use Fing.
$ brew install fing
$ sudo fing 10.0.1.1/24

(Note: Check your computer's local network IP address—if it's something like 192.168.0.x, then you need to use 192.168.0.1/24 instead of 10.0.1.1/24.)

Either of these options will scan the network for a bit, then output a list of Host addresses and MAC/Hardware addresses. nmap additionally prints human-readable manufacturer labels, so it's even easier to identify devices labeled with (Raspberry Pi Foundation) on your network!

Once you have found the Pi's IP address, log into it: ssh pi@[IP-ADDRESS-HERE] (the default password is raspberry). Since this is the first time the Pi is being used, it needs to be configured:

  1. sudo raspi-config
  2. Set a new password (first option in the list).
  3. Set a hostname (e.g. pi-zero-timelapse).
  4. Go to 'Interfacing Options', then 'Camera', then choose 'Yes' to enable the camera interface.

Raspberry Pi Zero raspi-config Terminal SSH configure hostname
Aren't the graphics amazing?

Create your own Time Lapse script

Now that the Pi is set up, and you're connected to it, you need to install a few libraries so you can call them in your Python time-lapse script:

sudo apt-get install -y python-picamera python-yaml imagemagick

Then create a script named timelapse.py (nano timelapse.py to open it in the nano text editor), with the contents:

from picamera import PiCamera

camera = PiCamera()
camera.capture('image.jpg')

This is basically how the guide in Raspberry Pi's official documentation works (Time-lapse animations with a Raspberry Pi). But at this point, we want to go a bit deeper, and have a more flexible way to control the timelapse—the length, the exposure settings, color temperature, etc.

To that end, I've built a little Python app (really, it's a glorified script... but nowadays everyone calls anything resembling software an 'App', so I might as well, too) that you can download from GitHub to have greater control over your time-lapses!

Going deeper

With the pi-timelapse app, you can build timelapses like the one at the top of this post pretty easily. Some of the features I've built so far include:

  • Easy configuration via a config.yml file
  • Resolution control
  • Intervalometer control (number of images and interval between images)
  • Ability to generate an animated gif or an mp4 video after capture is complete (experimental)
  • Manual exposure control (optional): ISO, shutter speed, and white balance

Here's how to set things up:

  1. Change directories into the pi user's home directory: cd ~ (the tilde means 'home', which is /home/pi in this case).
  2. Download the project: git clone https://github.com/geerlingguy/pi-timelapse.git
  3. Change directories into the pi-timelapse directory: cd pi-timelapse
  4. Create your configuration file: cp example.config.yml config.yml (this copies the example file to config.yml).
  5. Configure the time-lapse: nano config.yml (in the nano editor, Ctrl-O saves ('writes out') the file, and Ctrl-X exits).
  6. Start a time-lapse: python timelapse.py

After the number of frames you configured have been captured, there will be a folder named series-[date-and-time] in the directory with the pi-timelapse project. That directory contains all the images that were captured, numbered in a sequence like image00001.jpg, image00002.jpg, etc. And if you configured a gif or video to be created, then you'll see a .gif and/or .m4v video in the pi-timelapse project directory too.

You can use ls to display the contents of the directory, and cd [folder] to go into a folder, or cd .. to go back one directory.

At this point, if you want to view these things, you'll either need to use scp to copy a file from the Pi to your computer, or use an FTP client that works with SFTP (I use Transmit, but Cyberduck is a great, free alternative).

More Examples

Here's a video I shot at 1 frame every 15 seconds of cirrus clouds in the sky in front of my house:

And a shot of an earlier snow melting (as it was falling!) in my backyard:

And here's a gif I captured of myself, showing one of the risks of working at an adjustable-height standing desk:

Standing Desk Problems - Animated Gif

I haven't had time to build any more elaborate time-lapses, mostly because they often take hours (or days!), depending on what I'm trying to capture, and I've still been honing the software in the short term.

Summary

Grab the Raspberry Pi Time-Lapse App from GitHub, and start making some time-lapses of your own!

My next steps are:

  • Build a weatherproof enclosure that I can lock down to a post or otherwise secure outdoors, so I can record more of the nature around the house.
  • Optimize the software so I can deliver videos directly to places like Dropbox or a network share (right now I have to SCP the files to my computer).
  • Test other Pi models (Pi 3, Pi 2, etc.) to see how they fare in terms of power consumption vs. efficiency for shorter time-lapses.

And if you're interested in getting into the nitty-gritty, check out the contents of the timelapse.py script, and read the official picamera module documentation for a ton of useful background information.

Comments

Awesome! I'll have to try that soon! Thanks for the post

Ive made a few timelapse with my pi. I didn't have the time to mess with a hardware switch so I spoofed my pi to set the time to midnight anytime it booted without wifi and I coded it to take pictures for 8 hours and then had a cronjob to turn the pi off after 8 hours. (Bit of a write-up for reference: https://github.com/HerbFargus/Raspberry-Pi-Scripts/wiki/Timelapse#schedu...)

Anyways looks like a cool script, I'll have to check it out

Very nice project, I like it!
I suggest you to use python >= 3.5 and take advantage of async. I'm developing a similar project, but I want to use telegram to take photos, timelapse, videos or set options! All your photos or similar will be on your telegram cloud.

Check this, it is a good start to know async in Python (and why not telegram library 😁): https://gist.github.com/heejune/afd77aeec49e836fa549fc025962ddd8

Couple things - first, works great on a pi zero-w with the original camera. Nice. Second, your code doesn't like interval=1 because you sleep interval-2 in the code. I didn't know you coded up the wayback machine :-)

It would be nice to be able to specify an output directory for the finished product(s) and whether to copy them there or not. FWIW, I usually just stash this stuff in a nginx docroot subdirectory and serve'em up via the web. Everybody loves a timelapse !

This is great for fiddling with the camera resolution, but just running 'motion' will get the job done nicely and do timelapse very efficiently on even the oldest Pi. I've been running it doing timelapse movies for a few years now on an old model-B with a USB webcam. Gonna port all this stuff over to the zero-w and my new ZeroView mounting thing from pihut (way cool - would be great for a birdbox cam)

Please excuse the basic nature of this question - I would appreciate any help that you can offer. I have only cloned repositories on github a few times, and I am clearly missing a step here that is holding me back.

When I input sudo apt-get install git clone https://github.com/geerlingguy/pi-timelapse.git , I receive the following:

Reading package lists...Done
Building dependency tree
Reading state information...Done
E: Unable to locate package clone
E: Unable to locate package https
E: Couldn't find any package by regex 'https://github.com/geerlingguy'

What am I doing wrong here - any help on the step(s) I've missed would be greatly appreciated.

@Heath - for the git clone part, you actually need to do:

git clone https://github.com/geerlingguy/pi-timelapse.git

The apt-get commands are for installing software earlier.

Got it - thank you Jeff. That makes sense.

Running through a series of troubleshooting my confusion (much of it self-inflicted by having 8 different windows open to diagnose what I haven't done to Raspbian) - I realized I hadn't run the install for "git" yet (sudo apt-get install git). For me, that seemed to open this up.

I'm moving now. Will keep you posted. Thank you for the quick reply.

I believe you also need sudo apt-get install libav-tools to do video, correct?

Yes; that feature is currently being worked on further in a branch though. Good catch!

Jeff - do you have any quick login on why I am getting "ImportError: No module name picamera" when I run python timelapse.py?

I've gone back through this a few times and I feel like I'm missing something very small - apologize for the simple questions, but appreciate any insight you have or troubleshooting you would suggest.

You'll need to make sure you run sudo apt-get install -y python-picamera python-yaml imagemagick — that makes sure the picamera module is installed.

Hey Jeff thank you for the script, been experimmenting with it and having fun.

My scripting skillz are somewhat lacking and I have two question I hope you can answer.
How do it change the resolution of the pictures made, tried different formats but either I get an error or the default resolution.
What will the resolution of the mp4 video if i choose to use the option?

Thx Markus

Please see the Resolution section of the README for recommended resolution settings—if you use those settings, they're the most optimal for the camera.

With the video option, the resolution you set will be the resolution of the final video. If you want to capture one resolution then scale the video at a different resolution, you'd have to manually run the command to scale the video using avconf later.

Hi Jeff,

thank you for the swift reply.
I read the readme, but I still don't know what to put in where exactly.
I tried this:

# Image resolution Set to empty object (`{ }`) to use default resolution.
resolution: {}
  #width: 1920
  #height: 1080

or theese:

resolution: {1080p}
resolution: {1080,1920}
resolution: {1080*1920}

But all result in an error.

@Markus - Oh, sorry about that! The way it's structured, you should write it like:

resolution:
  width: 1920
  height: 1080

Thank you! This is perfect.

I went a step further and wrote a unit file and added it to systemd startup. Now it starts capturing images as soon as I turn it on. I use a short bash script to start it in the root of a running web server on the pi zero. So I can browse to it and see all the captured images.

That's awesome! I actually needed that earlier today, since I was going to a remote location to do a project with no Internet/network, and didn't have time to set up the Pi manually via direct WiFi...

Do you think you'd be able to share the unit file either as a pull request or some other way, and I could include it with the project? It would add the main missing feature ("set it and forget it") that's currently missing from this setup.

It's pretty simple so I'll just paste here:

Two files: A script to change to your working directory, and the unit file.

After creating both of these:

systemctl daemon-reload
systemctl enable timelapse
systemctl start timelapse # or reboot

Shell script to start: /usr/local/bin/timelapse-start.sh

#!/bin/bash
WEBROOT="<Your Web root dir here>"  # Or where ever you want the pictures to end up.
cd $WEBROOT
/usr/local/bin/timelapse.py &

Unit File: /etc/systemd/system/timelapse.service

[Unit]
Description=Take pictures for timelaps photos
After=syslog.target

[Service]
Type=forking
ExecStart=/usr/local/bin/timelapse-start.sh

[Install]
WantedBy=multi-user.target

Thanks! I've referenced your comment in the GitHub repo here: https://github.com/geerlingguy/pi-timelapse/issues/8

Hi Jeff,
Can't get scp to work. I get this error: ssh: connect to host 192.168.0.102 port 22: Connection refused
lost connection
From this: scp image.jpg donde@192.168.0.102:/home/donde/Public
image.jpg is in /home/pi of the Pi Zero
donde@192.168.0.102 is referring to the Mint box I'm on now.
I notice my older Netgear router has Device Names sometimes mixed together on same line. Don't think this matters.
So, why doesn't this single image get moved from the Pi Zero to the Mint box?
Thanks, Don

Are you running scp image.jpg donde@192.168.0.102:/home/donde/Public from the Raspberry Pi itself, or from your Mint box?

Jeff,
Right now not sure! I see up arrow commands from both the Mint box and the Pi Zero.
Most look like from the Pi. What I see is the file name moved but 0 bytes in the file.

More info
First of all, can either machine be the Remote or the Local? If the file to be moved resides in a machine, this is the Local? And the other machine is the Remote? I think this can be confusing in looking at the examples in Google.
As the file move starts, I see at the far right ETA. Then after a number of seconds, I see STALLED and then the move hangs. Do a Ctrl C.
I look where it's supposed to be and see correct file name, but no bytes.
From which machine should I send the move command? Should I use the host name or the IP address? I have been looking at SCP in Google. It seems this command has lots of problems with others too. As I remember, a move did complete, but to the same machine! Please excuse my babbling.

Finally got it working!

scp pi@192.168.0.105:/home/pi/now.txt /home/donde

Now can receive a file from Pi Zero to Mint box. Command is done from Mint box only.

Got rid of "permission denied" errors that stopped me from the get go.

Thanks

Glad you could get it working!

Any more you are adding to this time lapse project? I did see about you improving focus of pi camera. Have to read it. I have the same little case for camera and Pi Zero. I can't believe why they didn't put a slit to remove the micro SD card!
Want to read about your other projects. You are quite diverse!
Take care, Don

Follow along on the GitHub project. I'll be working on it more for this year's Solar eclipse!

Going to Idaho Falls, actually Rexburg, ID to see eclipse with Astronomy Club. 200 members are going!

I'm lucky to be in the path of totality, so I'm working on my setup and practicing the full progression—working on a blog post and YouTube video explaining the things I'll do, with some links to equipment I'm renting and/or buying to help make it fun, safe, and most importantly, memorable!

FYI
A new problem. For scp tests I was using small files around 1 K. Well, anything much over this, scp would stall and I would never receive any bytes. Got the file name OK. Nosing around Stack Overflows using phrase scp stalls over 1 KB, I found this as a fix.
sudo ip link set eth0 mtu 1024. Now I can receive the 250 K jpg example file with no problem. For days, all I would see was STALL along with other problems. How frustrating this science is!

OK now I'm finally ready to so timelapse.
only one image is stored image00000.jpg
But getting this error:

Traceback (most recent call last):
File "timelapse.py", line 48, in
sleep(config['interval'] - 2)
IOError: [Errno 22] Invalid argument

Getting more lazy, I guess. Changed interval to 2. OK now. Hope I don't have to bother you any more!

so what is the syntex of that line then? I am getting the same error, but have not figured out how to correct it.

sleep(config['interval'] - 2)

Jeff,
great project and great stuff on your blogg.
I came across this site while searching for Zeri W power consumption.

I'm looking for a real time birds view cam with low latency for persons with limited sight/impaired who can view the cam stream on an android phone/tablet. They love to see the birds and squirrels in front of their eyes instead of using a glass.

But currently the cams are powered by a power line and I t want to replace it with a solar powered power bank .
Therefore I need a glimpse ofbzero w's power consumption while streaming camera ... and you have the cam , the zero w and the foundation cam.
Maybe you have an idea what streaming means in power consumption, what size of panel and powerbank I need for 14 hours live transmission
Thx ...

Jeff, thanks for making your time-lapse code public, it sure saved me a lot of time! Your write-up is very clear too.
One thing that is confusing me - it seems that the call to create the movie is happening as soon as the series of photographs STARTS, not after it ENDS. I'm not experienced in python, so I don't know enough to say how this is possible.
I've watched the directory where the photos are slowly being created, and as soon as it starts, there's already a time-lapse.mp4 file there. Its file size is very small. I've stripped out the avconv call and done it manually later on the directory full of photos, and it clearly processes them all and creates a proper mp4 file.
What do you think?

@tinker - Do you maybe have an older copy of the code? In the current version, the avconv command is at the very end (see https://github.com/geerlingguy/pi-timelapse/blob/master/timelapse.py#L99-L102).