A Tiny Sealed Raspberry Pi Webcam
CNC Spindle Cameras Part I
This article describes the hardware and software for a Raspberry Pi Zero W-based headless streaming video/photo camera. The camera is 1½" x 3" x ¾" (1½" with lens), sealed to the elements, and comes in two flavors — one using any USB camera and one using the 8MP Raspberry Pi Camera V2 (total cost is $40 retail).
The camera hosts a web page for viewing the video or photos. It also has a camera control page. Last, it runs a VNC server so you can log in and maintain it.
I’ve placed STL files and scripts into github.
I currently use 4 streaming cameras in hostile places. Two watch a wood router that creates a lot of dust sometimes. Two watch a metal milling machine that throws metal shavings and dust into the air. Not only are the locations physically hostile but they are also electrically hostile — with a lot of motor and other noise generated when they run.
So, I have spent years waiting for a great near-waterproof solution. It has to be small (two of the units are down-pointing on spindles), it has to produce robust wifi, it has to be reliable, and it has to be maintainable. Open source is a plus. I’m not a fan of cloud video cameras because they are security-issues-in-waiting — so local broadcast and IP filtering.
Years ago I wrote an article about how to stream video with the Raspberry Pi and in some ways this is an update to today (March 2022).
The camera uses either a standard USB camera — in which case the USB port is available and there is no lens…
…or the camera uses a standard Raspberry Pi V2 8MP camera. The photo module on this board is excellent and would always be my preference except for the two spindle cameras — which must be USB due to RF interference.
This camera uses the recently released Raspberry Pi Zero W2 (not the W1) so you get a good frame rate and the Pi V2 camera is really good. It makes a lot of sense as a streaming webcam. The Zero gets pretty hot so a sealed box requires a metal heat sink to stream video without overheating.
This case is half aluminum (for heat sink) and half plastic (for convenience and for WiFi). In order for the WiFi to work well a chunk is bitten out of the alumimum — otherwise the signal is blocked. The chunk is covered by the plastic so it looks like a black plastic strap. Making a sealed box takes both a 3d printer and a milling machine.
If you don’t need the box sealed, it’s very easy to just replace the aluminum bottom with a plastic bottom with many holes (and put holes in the top too). Then it just requires a 3d printer.
I tried 4 different options.
Raspivid — raspberry pi camera has a now-deprecated app named raspivid that streams video but it won’t stream jpeg and the 5 second delay streaming H264 is totally unusable. There’s no gui and no browser stream.
Libcamera — the new raspberry pi-supported libcamera video streamer is just like Raspivid in that it won’t stream jpeg and the 5 second delay streaming H264 is totally unusable. There’s no gui and no browser stream.
motion — the linux motion app does stream video and does stream audio (which is really nice) but the performance when I tried it was miserable. It’s really designed far more for motion detection. I’m not sure about a gui.
mjpg-streamer — this hasn’t been updated in years, but mjpg-streamer remains the best solution for many reasons. It’s the only one with a web-based interface and jpeg streaming and for performance it’s when you see it is when it happened (on a good wifi connection). The winner.
Software Installation / Walkthrough
The installation is super simple for Linux.
- Start by installing Raspberry Pi OS (32 bit). I used the Raspberry Pi imager app to make an SD card, which works well. Click the subtle gear icon bottom right and …
- Enable SSH
- Set up the WiFi SSID and password
- Set the hostname to a unique name. I use ZeroCam1, ZeroCam2, …
- Set the Locale to your country/timezone.
- Change the user name and password. Leaving the user as “pi” is asking for trouble.
- Insert the SD card built by imager into the Pi Zero, wait for it to boot then SSH into it.
- sudo raspi-config and
- in system set boot into desktop mode
- in display options set the VNC resolution. I use 1080P (1920x1080).
- in interface enable VNC if you use it. I do.
- in interface select your camera choice if you use the Raspberry Pi camera module.
- in performance options change the GPU memory to 96MB if using a Pi camera
- I usually reboot and switch to VNC at this point.
- Next install mjpg-streamer-experimental. This old fork of the really old mjpg-streamer adds support for native raspberry pi (now legacy) functionality. The github documentation is adequate.
git clone https://github.com/jacksonliam/mjpg-streamer.git
sudo apt-get install libjpeg-dev cmake gcc g++
sudo make install
- Finally, to test the program restart the console and use a script like this for a usb (uvc) camera. This script also works for the Raspberry Pi cameras if you’ve installed the legacy support, which has a uvc driver, although it’s maybe not optimal efficient.
mjpg_streamer -o "output_http.so -w /usr/local/share/mjpg-streamer/www" -i "input_uvc.so -d /dev/video0"
The binary is installed into /usr/local/bin. The libraries are in /usr/local/lib/mjpg-streamer. The web files are in /usr/local/share/mjpg-streamer/www.
- Point a browser to Cameraip:8080.
Starting at power-on
If you want the camera to come on when the power is turned on the easiest way is by defining a startup service that runs a shell script in your home folder. The script will run as root and it can start up the mjpeg_server binary.
Create a tinycam.service file in the system folder.
sudo nano /lib/systemd/system/tinycam.service
sudo chmod 644 /lib/systemd/system/tinycam.service
The file contents are:
Description=TinyCam Startup Script
Then, to have it run after the network starts up — refresh the services to read your new service file and then enable it
sudo systemctl daemon-reload
sudo systemctl enable tinycam.service
Finally, the contents of the shell script are something like this:
# disable power management for wifi
iwconfig wlan0 power off
# run video server
mjpg_streamer -o "output_http.so -w /usr/local/share/mjpg-streamer/www -p 80" -i "input_uvc.so -r 1280x720 -d /dev/video0"
I lazily use port 80 so it’s just a web site in the browser and I use 1280x720 to save a little bandwidth.
Selecting the video device
Whether to use video0 or video1 or 2… can be confusing partly because if the video glitches it will reconnect on a higher number since the current video port is in use. So, the best way to connect is to look at a synonym in the /dev/v4l/by-id folder. One will obviously be the video stream and that makes a great address, so the mjpg_streamer call becomes
mjpg_streamer -o "output_http.so -w /usr/local/share/mjpg-streamer/www -p 80" -i "input_uvc.so -r 1280x720 -d /dev/v4l/by-id/my-video-stream-id"
The Zero doesn’t have the world’s best WiFi reception. The on-board antenna is great for the available real-estate but… Anyway, the way to maximize your range is to point the plastic side of the camera at the router antenna. To visualize it, if the Zero is flat on a table the max output is up into the ceiling.
Making the Case
There are two possible bottoms for the camera. The bottom for the external USB camera version has the power port and the USB port accessible/open. The bottom for the internal Pi Camera has the USB port sealed.
Here’s the model of the bottom for the internal version:
Here’s a photo:
The bottom has 4 M2.5 tapped holes for the Pi Zero mounting. It also has 4 M3 holes to attach the box top. Finally, there are two spare M4 holes in the back for mounting the camera to whatever.
The WiFi antenna on the Pi Zero W is that triangular-looking object just below the CPU.
Notice there is a small platform on the aluminum bottom. This is about 1/2mm away from the bottom of the Pi Zero W. I use a small piece of silicone as a heat transfer mechanism (and insulator) instead of using more efficient thermal grease. This piece gets placed on the platform below the board. The temperature of the Zero W is comfortably within spec with the silicone. My quick unscientific tests showed near 75°C in a sort-of-sealed plastic enclosure streaming with raspivid vs about 48°C in the aluminum/silicone streaming with mjpg-streamer.
There are also two tops. One flat (USB) and one with a nozzle for the lens. This requires an uncommon tap to create the thread for an M12x0.5 lens. Yes, you can tap plastic for the lens holder. It seems cheap but there is so much thread to connect for a lens that it works very well.
Here is the internal camera model. It requires a camera module without components on the top sides (such as the Pi V2 board) although it could be modified to accommodate other modules.