Headless Streaming Video with the Raspberry Pi Zero W and Raspberry Pi Camera

Hardware and Introduction

I’ve done lots of things with USB cameras on a Raspberry Pi. My 3d Printer has one permanently connected to it running Octoprint (shameless plug). However, I finally needed a really tiny yet cheap camera so I thought of the Raspberry Pi Camera module. The V2 module does 8MP with good quality and so it fits my needs.

My rather ugly box

I printed this rather ugly box to put it into because it’s an industrial application where it gets installed on a system and a rectangular box is the optimal shape. The box snaps together (even though there are screw holes just in case). The final box will be a different color and have an aluminum faceplate.

Well, this article is about software so…

Software Intro

I spent quite some time trying to find software that would be fast and efficient.

After going through the usual Raspberry Pi documentation and StackOverflow and Google I finally went back to my preferred solution. I use mjpg-streamer because I’ve gone through the code personally and it’s remarkably efficient. Broadcom now has a V4L2 driver available for the Pi Camera but this version of mjpg-streamer has a direct camera driver.

The version of mjpg-streamer I use is the same one that Octoprint uses, so it comes from a specific github repository. And.. it has to be built.

Update: at this time I’ve started trying out uv4l (see the writeup below) and feel it may be better than using mjpg-streamer. It’s simpler to set up, doesn’t require a user’s code repository, and has documentation.

Setting up the Raspberry Pi

I use Raspbian Lite because the only thing we’re doing with this Pi is using it as a camera. Setting up Raspbian Lite is awkward with only a Pi Zero W because the wireless has to be configured. This setup will work with any version of Raspbian Jessie or later.

If you don’t have a Linux system around, boot the Raspberry Pi with a monitor and keyboard.

If you already have a Raspberry Pi or a working Linux system you can just create the ssh file and the wpa_supplicant files as shown below and add them to the sdcard. This will let the PI Zero W connect to your wireless and enable SSH.

Setup using Keyboard and Monitor

Connect a keyboard and monitor to your Raspberry PI. Boot and then follow the next two sections.

Setup Wireless

To add the wireless setting to the wpa_supplicant.conf file:

# setup the wireless connection
# give everyone r/w access to the conf file so you can append to it
sudo chmod 666 /etc/wpa_supplicant/wpa_supplicant.conf
# now append the wireless info to the conf file
sudo wpa_passphrase myPSID myPassword >>/etc/wpa_supplicant/wpa_supplicant.conf
# wpa_passphrase creates the password, psid text
# that gets added to the wpa_supplicant.conf file 
# it looks like this
# network={
#   ssid="myPSID"
   #psk="myPassword"
#   psk=eacbde1673e3f079fc8bbb375daa322a8b3b0
# }
# now you can reboot and it will connect to your wireless

You can set the wpa_supplicant.conf access back to 600 if you want to revert to original (otherwise a hacker could theoretically access your wifi).

Enable ssh

To enable ssh add an empty ssh file to the boot partition. Then ssh will come up enabled. You can also do this via raspi-config.

Remove the keyboard and monitor and reboot. From here on use ssh. I use Putty as a client but other clients work fine.

Setup using a second Raspberry PI

Start by placing the newly created SD card into a USB reader and put that reader into a Linux computer (such as a PI).

Mount the drive partitions. There are two that’ll show as SDA1 and SDA2 (or B or …). You can search /dev/SD*.

sudo mkdir /mnt
sudo mkdir /mnt/sd1
sudo mkdir /mnt/sd2
sudo mount /dev/sda1 /mnt/sd1
sudo mount /dev/sda2 /mnt/sd2

This will mount the two partitions. The Boot partition (sd1) takes the blank ssh file in its root. The Data partition (sd2) contains /etc.

Use the Setup Wireless commands above but add a /mnt/sd2 prefix to the path for the .conf file. After creating the wpa conf file, sync and unmount the two drives then remove the SD card.

If your source Raspberry PI is wireless to the same network you can just copy /etc/wpa_supplicant/wpa_supplicant.conf from the root to /mnt/sd2.

After Booting with Wireless and SSH enabled

Enable the camera

Enable the camera. This is done in raspi-config. There’s no reason to show the GUI and it changes anyway. Find the enable camera option and do that.

sudo raspi-config

Let the system reboot once the camera is enabled. To test that the camera is working:

vcgencmd get_camera

This should note that one camera was detected and connected.

Install mjpg-streamer

Note: as of April 2018 there is an easier solution. See a few paragraphs down for using uv4l.

I use a version of mjpg-streamer specifically for the Raspberry Pi. The ‘real’ version may now work but I know this works.


# get the prerequisites sudo apt-get install libjpeg8-dev imagemagick libv4l-dev uvcdynctrl sudo apt-get install git cmake # get the latest code git clone https://github.com/jacksonliam/mjpg-streamer.git cd mjpg-streamer/mjpg-streamer-experimental make USE_LIBV4L2=true clean all # note this will spew a few errors. ignore them # you need to be in the video group sudo usermod -aG video myUserName

[Optional] Install the Broadcom V4L2 driver and run

sudo modprobe bcm2835-v4l2
./mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 1640x1232" -o "output_http.so -w ./www"

Here I’m using half-resolution. Full resolution seemed to run out of memory. But… this is real-time and good enough resolution for my application. If I need higher resolution I can duck the frame rate or capture stills. Note that the Broadcom driver needs to be part of the OS and this only installs it for the session. To install it as part of startup, add the module name to /etc/modules.

Use the custom raspberry pi camera driver

The nice thing about this build is that there’s a driver for the raspberry pi. This does not require a v4l2 driver. You can simply run it like this:

./mjpg_streamer -i "input_raspicam.so -x 1920 -y 1080" -o 'output_http.so -w ./www"

Or, you can do 2MP at the right aspect ratio:

./mjpg_streamer -i "input_raspicam.so -x 1640 -y 1232" -o "output_http.so -w ./www"

Trying to stream video at the full 8MP caused a kernel panic. I was able to stream at 1fps on my Zero W, but the readout said it was capturing at 1.5fps so there wasn’t much margin. I used this line:

./mjpg_streamer -i "input_raspicam.so -x 3280 -y 2464 -fps 1" -o "output_http.so -w ./www"

Open a web browser and browse to http://myraspberrypi:8080 and you should see the mjpg-streamer user interface and be able to stream.

My actual mjpg-streamer running

Install UV4L and WebRTC

Recently (April 2018) the folks at linux-projects have written a raspberry pi driver that streams all by itself. I haven’t extensively tested it but I have tried it out and it seems to work. It’s painless install (nothing to compile) and supports h264 as well as a myriad of other formats. There is even documentation (gasp).

Installation instructions are here. Or, if you’re running Raspbian Stretch: start by adding the linux-projects repo (requires a key and a line added to /etc/apt/sources.list).

curl http://www.linux-projects.org/listing/uv4l_repo/lpkey.asc | sudo apt-key add -echo 'deb http://www.linux-projects.org/listing/uv4l_repo/raspbian/stretch stretch main' | sudo tee -a /etc/apt/sources.list

Next, update the source holdings and install the uv4l stuff:

sudo apt-get update 
sudo apt-get install uv4l uv4l-raspicam uv4l-raspicam-extras

If you want simple access and a GUI add the WebRTC shell.

sudo apt-get install uv4l-webrtc

Then reboot the PI and browse to the uv4l server at http://raspberryip:8080 from your desktop. The video stream is at http://raspberryip:8080/stream

Postscript — October, 2017

I worked on this project for a while and found out (or speculate with a lot of evidence) the following.

Kernel Panics

There are two reasons for kernel panics (complete failures). First, if you do full resolution video you must allocate more memory to the GPU. This is done in the /boot/config.txt file as a setting for gpu_mem.

gpu_mem=256

is what I ended up using. It’s pretty tight on CPU RAM but worked ok for me. At the usual gpu_mem setting of 128 I wasn’t able to get full-resolution images but lower resolutions worked fine.

The second reason is more insidious. I found that my camera would only run for a while and then at full resolution it would overheat. The combination of constantly running the Wi-Fi and the camera port cause chips to heat up and in the sealed camera enclosure it overheats and you get a Kernel Panic and complete fail. You can run at lower resolution and cut the frame rate — or take the top off. What I did was watch CPU usage.

A much better solution is to use h264. I was forced to use MJPG for streaming in my application and I assume (but didn’t check) that mjpg-streamer also uses it. If you can receive H.264 it’s much lower resource usage on the Wi-Fi and probably on the GPU as well.

Helpful Commands

Show the current chip temperature (vcgencmd is in /opt/vc/bin):

vcgencmd measure_temp

Show CPU usage:

This requires systat for a simple rolling cpu usage view.

sudo apt-get install sysstat

sar -u 2 # every two seconds show usage

Update April 2018 — Add 3d Models

Since there was a question about this, I’m including the 3d models for the camera holder front. These are 3dm (Rhinoceras 3d) and STL (more generic 3d model).

The Camera Front

The models can be downloaded from here: https://www.thingiverse.com/thing:2849957

Home Wireless

Home automation in the wireless IOT era

Mark Zachmann

Written by

Entrepreneur, software architect, electrical engineer. Ex-academic.

Home Wireless

Home automation in the wireless IOT era