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#sched…)

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?

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

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 [email protected]:/home/donde/Public
image.jpg is in /home/pi of the Pi Zero
[email protected] 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

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 [email protected]:/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

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

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

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?

Thank you for making this project public. I've got it working for taking pictures, but like the comment above when enabling video compiling it drops into trying to compile the video file after only taking the first picture. I'm not a python expert by any means, but it looks to me that it's caused by the threading.

If I am I'm understanding the source code correctly, the capture_image() routine launches iterative threads which wait some amount of time and then call the capture_image() routine, over and over. My thought is that the original thread would drop through after spawning the next thread, take a picture, then drop out of the capture_image() routine and try to create either the gif or video depending upon what is enabled.

I ran some tests and set the interval at 30 seconds and would see the first image file appear, then the video file, and then ~30 seconds later the next image file. I don't know enough about controlling threads in python to set up a way for the threads to end prior to launching the video compiling.

Either way, thank you again for posting this. I'm able to create a nice series of images of my 3D printer running with a Pi Zero and camera. Currently I'm just using a program on my laptop to stitch the images together.

Is there a way to combine this with other software on the pi that would allow viewing the camera live from another computer with a browser? It'd be helpful to be able to aim/focus/adjust the picture vs transferring stills. I know nothing about how all this is stitched together but know enough to recognize potential problems with conflicting tools trying to use the same hardware.

Any suggestions on how I might combine this with a live stream option?

Jeff, this is great stuff!
I'm having a problem, however, and it doesn't seem to be addressed in the comments or on your page.
When I execute "Sudo python timelapse.py"
I get a printout that it's capturing pictures and creating a gif. It just sits there. If I leave terminal open, it will continue to take pictures. If I close it, the session ends and the pi no longer takes pictures.
I've gone through the settings a thousand times and nothing seems to be off, not sure where I've gone wrong.

Thank you for sharing your work!
I adapted what you did for my purpose. I chose to use crontab to take a picture every X minutes until my powerbank dies, so I my script is very basic.
Cheers :)

This is really cool! Would this setup be able to record rate of recovery?

Hello Jeff,

is there a way to move the image files directly to a mounted drive without writing them to the memory card?
Unfortunately I have no idea about programming and would be grateful for a suitable code snippet.

Hey Jeff,

What a wonderful project!

When I go through the instructions, I get an error on line 8

Traceback (most recent call last):
  File "timelapse.py", line 8, in <module>
    import yaml
ImportError: No module named yaml

This is after attempting to install yaml, but it seems to have trouble on this. Any thoughts?

Thank you so much for all your generous help!

-Sam

Its amazing what you can do with PIs ,you can of course make a portable tablet...running linux it should be possible to run
wordprocessing whilst out and about which will allow easy pasting into blogs etc which is a pain on android,android tablets
are ok however ideally linux and windows tablets are far more usefull in certain areas however they are extremely expensive,
the pi can be converted into a linux/windows tablet and so this makes ideal for paste on the go work!!!!

Hi, is there any chance to add the date and hour every time the timelapse take a shot?

I pretty much like it (as also the tips hereby in the comments) ... but am will need to understand what advantages there are compared to a bash script combining raspistill (and possibly crontab if raspistill parameter does not suffice) and renaming the img with a timestamp (and joining them to a video with mencoder afterwards).

I mean, as of now I am downloading an image from a wilderness webcam (one every minute as they are shot) and renaming them as suggested. I could also have it create a folder for every day instead of moving them manually. Is this approach unreasonable?

Not at all! A bash script could easily do the same stuff, though you may need to work a bit harder to parameterize the configuration if you want to set exposure, color temperature, etc. manually in a separate config file.