I had the need to embed a webcam image in a webpage. I did not want to stream video and did not want to view images directly on my Raspberry Pi. I wanted to take a picture every so often and upload the image onto a website. I also wanted the webpage to update with the latest image. One other requirement was for the webcam to (try to) survive power outages without file system corruption and with auto-start at boot/reboot. Yet another Raspberry Pi Zero project was born!
I will assume that you have setup a Raspberry Pi Zero W with an official camera module though other Raspberry Pi versions will certainly work. I am using the official Zero case with camera lid as it makes a nice small package and protects the camera. I recommend using the Raspbian Stretch Lite Operating System. Ensure that you configure WiFi and have internet connectivity where you plan to position the Pi/Camera. You will need terminal or ssh access to the Pi during the setup described here.
- Test camera and software configuration & prepare image for uploading
- Setup SSH keys for easy, hands-off image file upload
- Write shell script to take & upload image
- Create a start-up service to launch the script & recover from errors
- Create an environment where only RAM & not the SD Card is used
- Prepare the target server & webpage to receive & update image
Configure and test
I’m using a Raspberry Pi Zero W running Stretch Raspbian. You can use another Pi but I’m assuming “Stretch” for this project. Make sure that you have run “raspi-config” and enabled the camera module!
update, upgrade and check OS version:
pi@ZeroCam:~ $ sudo apt-get update pi@ZeroCam:~ $ sudo apt-get upgrade pi@ZeroCam:~ $ uname -a Linux ZeroCam 4.14.79+ #1159 Sun Nov 4 17:28:08 GMT 2018 armv6l GNU/Linux pi@ZeroCam:~ $ cat /etc/os-release PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)" NAME="Raspbian GNU/Linux" VERSION_ID="9" VERSION="9 (stretch)" ID=raspbian ID_LIKE=debian HOME_URL="http://www.raspbian.org/" SUPPORT_URL="http://www.raspbian.org/RaspbianForums" BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
install ImageMagick graphics library. This will be used to manipulate and imprint info on images.
pi@ZeroCam:~ $ sudo apt-get install imagemagick ... ... Setting up ghostscript (9.26~dfsg-0+deb9u2) ... Setting up libpango-1.0-0:armhf (1.40.5-1) ... Setting up imagemagick (8:184.108.40.206+dfsg-11+deb9u6) ... Setting up libpangoft2-1.0-0:armhf (1.40.5-1) ... Setting up libpangocairo-1.0-0:armhf (1.40.5-1) ... Setting up libmagickcore-6.q16-3-extra:armhf (8:220.127.116.11+dfsg-11+deb9u6) ... Processing triggers for libc-bin (2.24-11+deb9u3) ...
Test the camera. Take a still image with appropriate settings and view by using “scp” or “sftp” to download the “jpg” file to another computer. You can also view on the actual Pi Zero if you have a monitor connected 😉
raspistill -n -t 3000 -o test_01.jpg -e jpg -q 80 -w 1024 -h 768 -ex auto -awb auto
-n == no preview
-t 3000 == wait three seconds for camera to “settle”
-o test_01.jpg == output image to file named, “test_01.jpg”
-e jpg == save image as JPEG — NOTE this is H/W accelerated and fast
-q 80 == set JPEG quality to 80 (good with decent compression)
-w 1024 == set width of output image to 1024 pixels
-h 768 == set height of output image to 768 pixels
-ex auto == set exposure to automatic (usually a good thing)
-awb auto == set white balance to auto (usually OK though you might need to change for a light source such as fluorescents)
You can also use different options to flip an image if upside down or if you need to rotate the image. Check out the complete documentation for raspistill at raspberry.org.
Using the ImageMagick convert command, you can add text over your image. I add the date and time in the upper left corner of the image using white text. You can experiment with placement, size, color, and text 😉
pi@ZeroCam:~ $ imageDate=$(date) pi@ZeroCam:~ $ echo $imageDate pi@ZeroCam:~ $ convert -pointsize 18 -fill white -draw 'text 30,40 "'" $imageDate"' " ' image_01.jpg image_01.jpg
Using the convert command, I set text size to 18 points, set the fill/text color to white and position the text (the lower left corner) at position x=30, y=40 with the top-left corner of the original image being origin (0,0). I made the input and output file the same to “edit in-place.” Full documentation for convert can be found at the ImageMagic website.
Setup SSH key pair
OK, the Raspberry Pi Zero Cam is setup, we can take pictures and superimpose information on the image. Now we need to upload the image to our website server in a hands-off repeatable manner. I use the “scp” command for my Linux-based server — after setting-up SSH keys – so no password or intervention is needed. You could also use “ftp” is you are more used to that — and know how to automate. To start, make sure you can “ssh” into your website server.
pi@ZeroCam:~ $ ssh cd pi@ZeroCam:~ $ ssh firstname.lastname@example.org # You may need to accept the ECDSA key fingerprint - say "yes" if prompted # Enter password and should be located in the user home directory # Logout to return to your Pi's command line user@yourserver:~ $ exit pi@ZeroCam:~ $
Setup public key authentication: You must generate both a public and private key pair and install the public key on your website server. For deep details and info for various OSs, you can read the docs for ssh-keygen and ssh-copy-id at ssh.com. A much more simple “How-to” can be found on nixCraft.
# Generate a public/private RSA key pair # you can just hit enter/return at prompts for defaults pi@ZeroCam:~ $ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/pi/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/pi/.ssh/id_rsa. Your public key has been saved in /home/pi/.ssh/id_rsa.pub. The key fingerprint is: SHA256:Rc1n4AYa1a/2DjONkVMPzEtg4wnYyM/Fe3E/ZDxyzzy pi@ZeroCam The key's randomart image is: +---[RSA 2048]----+ | . +ooB+.. oO| | + .OoB= +*E| | o=.+.+*o..| | oB... ...| | S =. . .| | = .o | | *. . | | + . | | .. | +----[SHA256]-----+ # Use the command "ssh-copy-id" to install the public key # You will be prompted to enter your password (one last time!) pi@ZeroCam:~ $ ssh-copy-id email@example.com # You can now ssh/login to your remote website sever without password pi@ZeroCam:~ $ ssh firstname.lastname@example.org # Again, exit back to your Pi Zero command line user@yourserver:~ $ exit pi@ZeroCam:~ $
Create shell script to take and upload image
We should now be able to upload images to the website server. Check this out by taking a picture and using “scp” to upload.
# Take a picture pi@ZeroCam:~ $ raspistill -n -t 3000 -o test_01.jpg -e jpg -q 80 -w 1024 -h 768 -ex auto -awb auto # Upload to server in user's root directory pi@ZeroCam:~ $ scp test_01.jpg email@example.com:~ # Check to see that the file was transferred pi@ZeroCam2018:~ $ ssh firstname.lastname@example.org user@yourserver:~$ cd user@yourserver:~$ ls -l test_01.jpg -rw-r--r-- 1 user user 631483 Dec 31 15:01 test_01.jpg
If this worked and you can see the uploaded image file in your remote server home directory, we can now create a script to automatically take a picture every so often, embed the date/time in the image and upload it to the server. Another task on the server side will need to be created to actually display the image as part of your webpage.
Planning ahead, we are going to create a mount-point for a RAM-based file system so we do not repeatably read/write to the SD Card that would cause “wear” and offer a point of failure if the R-Pi loses power or experiences a power-off/power-on “spike.”
# make a dir for the RAM filesystem and make read & writable pi@ZeroCam:~ $ cd pi@ZeroCam:~ $ sudo mkdir /mnt/ram pi@ZeroCam:~ $ sudo chmod 777 /mnt/ram # create a 4MB RAM filesystem and mount pi@ZeroCam:~ $ sudo mount -t tmpfs -o rw,size=4M tmpfs /mnt/ram # confirm file system created and mounted with 'df' pi@ZeroCam:~ $ df -h
Now let’s configure “fstab” to create the RAM disk on boot. Make sure you have a backup of the existing, working file! If you “break” things with the new fstab file, you can restore from the backup file.
# backup current stab file pi@ZeroCam:~ $ sudo cp -pv /etc/fstab /etc/fstab.backup # 'cp' command should return something like: '/etc/fstab' -> '/etc/fstab.backup'
Edit /etc/fstab with your text editor (vi, nano) and add a line after other disk mount entries and before swap entry (or comment about swap). Add this line and save and exit editor.
tmpfs /mnt/ramdisk tmpfs rw,size=4M 0 0
Easiest way to load the new configuration and ensure it is correct is to “sudo reboot” and check the mounted filesystems after boot with “df -h” You should see an entry for “/mnt/ram”
Let’s create a bash script that will take the picture, put it into the “/mnt/ram” directory, add the date & time and, finally upload the image to the server. Make sure you put in the “raspistill” parameters that you developed while testing. Note the image is saved to the “/mnt/ram” directory. Use “vi” or your favorite text editor and create a file, “capture.sh” in the Pi home directory with this content:
#!/bin/bash # capture.sh - take picture, embed date, upload # Take a picture echo "Taking picture" raspistill -n -t 3000 -o /mnt/ram/webcam.jpg -e jpg -q 80 -w 1024 -h 768 -ex auto -awb auto imageDate=$(date) convert -pointsize 18 -fill white -draw 'text 30,40 "'" $imageDate"' " ' /mnt/ram/webcam.jpg /mnt/ram/webcam.jpg # assuming ssh keys exchanged - upload file to server scp /mnt/ram/webcam.jpg email@example.com:~
Make the script executable with “chmod” Test by running the script and looking at the image in the “/ram” dir – i.e. “/ram/webcam.jpg” AND by looking on the server for the file uploaded.
pi@ZeroCam:~ $ cd pi@ZeroCam:~ $ chmod 755 capture.sh pi@ZeroCam:~ $ ./capture.sh pi@ZeroCam:~ $ ls -l /mnt/ram/webcam.jpg
OK, we have all the pieces we need to automate the process of taking a picture and uploading. We want to do this on a recurring, periodic, basis. We need to create a service that will fire a timer that runs our shell script. We also want to create the RAM file system and start the timer on system boot.
Let’s create the “systemctl” (service) file and associated timer file. Use “sudo vi” or your favorite text editor to create a file, “/etc/systemd/system/mycam.service” – owned by root (i.e. using “sudo”) – and input this text:
# /etc/systemd/system/mycam.service [Unit] Description=mycam job [Service] User=pi Type=oneshot ExecStart=/bin/bash /home/pi/capture.sh
Create another file, “/etc/systemd/system/mycam.timer” – owned by root (i.e. using “sudo vi” or similar command) – and input the text below. Note that I am running the picture capture script every 120 seconds. You can change as desired.
# /etc/systemd/system/mycam.timer [Unit] Description=mycam [Timer] OnUnitActiveSec=120s OnBootSec=120s [Install] WantedBy=timers.target
Now you can reload systemd, start the timer and ensure it is running:
pi@ZeroCam:~ $ sudo systemctl daemon-reload pi@ZeroCam:~ $ sudo systemctl start mycam.timer # list all timers pi@ZeroCam:~ $ sudo systemctl list-timers --all
You should see something like this for the list-timers “mycam” entry:
NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2019-01-22.. 1min 21s left Tue 2019-01-22.. 38s ago mycam.timer mycam.service
If everything is working, enable the “mycam” timer service to start at boot:
pi@ZeroCam:~ $ sudo systemctl enable mycam.timer
Time to see if everything is functional. You should reboot the Pi, check the RAM file system config with “df -h” Check that the picture-taking script is running on the timer with “sudo systemctl list-timers –all” You can look at your server where you are uploading the picture and watch over a couple of minutes to see that the picture is being uploaded.
Assuming that your R-Pi image is being uploaded to the directory & file that you have setup, there are a couple of options for loading in a webpage. I directly upload to a file/directory that is referenced by my HTML. There are also WordPress Plugins that will monitor a directory and load an image to a WordPress page. You could also write a script on the server to move the uploaded file to the “correct” directory/file on a timer or cron job.