My Kingdom for a Webcam Server
Why was it so difficult to take and serve an image from the Raspberry Pi’s camera when I visit a web page?
I’ve found myself driving home unsure of whether I’ve closed the warehouse door at my church’s building. The building is 15 minutes away. That’s almost a 30-minute round trip just to check. It’s happened more than once. Yes, I’ve made the trip every time I doubted myself. For about a month, I make it a habit to ensure that the door is closed. I follow a routine. A month later, I forget to do it, and the frustration replays itself.
I want a camera, but I don’t need streaming, motion-detection, web controls, timestamps, etc. This isn’t a security feature I’m pursuing. I want an image that is updated regularly. I couldn’t get https://motion-project.github.io to work properly. After much suffering, it ran, but it couldn’t get video from the camera. I don’t need video anyway.
libcamera-still -o door.jpg
Have you ever run this command? It takes a whole 6 seconds to run. After struggling to get lighttpd, apache2, etc. to run a script to capture an image, I found out that it takes too long to get an image and can’t be reasonably called from a cgi or php script. What to do?
- Update and upgrade all the packages.
- Create a ramfs filesystem to avoid writing to the memory card every minute of every day.
- Tell systemd to mount that filesystem every time the pi starts.
- Install and configure lighttpd.
- Write the html file.
- Create a symlink to the file in ramfs.
- Set up a cron job to take and save a photo once per minute.
sudo mount -t ramfs -o size=128m ramfs /mnt/cramfs
Save this in ~/initcramfs.sh and chmod it. Ramfs is a bit volatile and can crash your system if we overrun its size, but 128m should be plenty for us, and ramfs ensures that no swap file is will ever be used unlike tempfs.
sudo chmod u+x ~/initcramfs.sh
Head over to /etc/systemd/system/ and touch initcramfs.service. Edit the file you just created.
[Unit] Description=Camera ramdisk init [Service] ExecStart=/home/your_user_name_here/initcramfs.sh [Install] WantedBy=multi-user.target
Take note that the file is extremely case-sensitive. Install != INSTALL. I don’t think systemctl start is necessary, but I did it anyway. It’s late. I’m not going to experiment.
sudo systemctl start initcramfs sudo systemctl enable initcramfs
Install lighttpd. Apache2, nginx, etc. should be fine as well, but configuring them will be different.
sudo apt install lighttpd
You don’t have to edit the file at /etc/lighttpd/lighttpd.conf, but I did change one line to serve on a different port:
server.port = 8080
Create and edit /var/www/html/index.html. You may wish to remove or rename index.lighttpd.html. Mine is very basic. It simply loads an image.
<html> <body style="background-color:black;"> <img src="door.jpg"/> </body> </html>
That image, door.jpg, isn’t an image. It’s a symlink (yes, I know, naughty) to a file (not yet) in ramfs.
sudo ln -s /var/www/html/door.jpg /mnt/cramfs/door.jpg
Reboot. You should now be able to cd into /mnt/cramfs, and you should be able to bring up a broken html page at http://127.0.0.1:8080 or http://ip-address-of-your-pi:8080 if you’re connecting remotely. Let’s get cron going.
crontab -e
It may ask you for your editor of choice. The line you want to add is:
* * * * * sudo libcamera-still -o /mnt/cramfs/door.jpg --width 800 --height 600
Save the file. This line tells cron to run the command once a minute. It will save a 800px x 600px image at /mnt/cramfs/door.jpg which is then symlinked and read whenever someone queries your server. After about a minute, an image.jpg file should appear in /mnt/cramfs/.