In my previous article, I described how to set up a Raspberry Pi High Quality camera as an IP camera, and use IP Camera Adapter to plug this into your favourite video conference software. Now, go one better, with just a singe USB cable and zero networking!
Using a Raspberry Pi Zero with its on-the-go USB port in ‘peripheral’ mode, we can make the Pi look like a USB webcam. And what’s even better is that Windows will recognise it as a Camera and automatically install the drivers for you.
Here’s a video of the setup, with demo of it booting.
So the first thing to do is install RaspiOS on an SD Card, and enable the camera. There’s plenty of howto’s out there on how to do that. 🙂 This is usually done while the Pi is connected to a monitor so you can run raspi-config to enable the camera, ssh, configure WiFi, etc. Later on on the article we’ll power down the pi and plug it into a USB port on a Windows PC.
All the scripts needed for this are in a repo on GitHub. It’s a forked version of uvc-gadget, with additional systemd and configuration scripts.
So let’s checkout the software. As the pi user, checkout the repo. You may need to install git.
sudo apt-get update sudo apt-get install git cd /home/pi git clone https://github.com/climberhunt/uvc-gadget.git
That will create a uvc-gadget directory, so lets configure the system now. We need to create a systemd service. I’ve supplied a file for this, which looks as follows. It just runs the piwebcam script, which creates the multifunction device and runs the uvc-gadget application.
[Unit] Description=Start pi webcam service [Service] ExecStart=/home/pi/piwebcam StandardOutput=syslog StandardError=syslog SyslogIdentifier=piwebcam User=pi Group=pi WorkingDirectory=/home/pi/ [Install] WantedBy=basic.target
To install this, just copy the file into the correct location, and enable it:
cd /home/pi/uvc-gadget sudo cp piwebcam.service /etc/systemd/system/ sudo systemctl enable piwebcam
Next, edit the kernel command line parameters at /boot/cmdline.txt, adding the following at the end of the “console” line. This enables the on-the-go mode of the USB interface, and allows us to create a multi-fuction gadget on top.
Also edit the boot config, adding an extra line at the end containing:
Now to build the uvc-gadget app, just run ‘make’ in the uvc-gadget directory:
cd /home/pi/uvc-gadget make
All going well, you should now have the uvc-gadget binary ready to be executed at the next boot.
Next to set up the new serial device /dev/ttyGS0 to present a login prompt at boot. For that, we run the following command to cause systemd to run a getty (all one command, not separate lines)
sudo ln -s /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@ttyGS0.service
One of the scripts that’s called as part of the piwebcam service startup us multi-gadget, which was published on the Raspberry Pi forums in a post by g.letourneur, and I added the extra serial gadget functionality from a few other posts. Here’s the script for reference. The main differences are the tweaks for 1080p video, and the addition of a few lines to create a serial device endpoint (acm.usb0).
!/bin/bash mkdir /sys/kernel/config/usb_gadget/pi4 echo 0x1d6b > /sys/kernel/config/usb_gadget/pi4/idVendor echo 0x0104 > /sys/kernel/config/usb_gadget/pi4/idProduct echo 0x0100 > /sys/kernel/config/usb_gadget/pi4/bcdDevice echo 0x0200 > /sys/kernel/config/usb_gadget/pi4/bcdUSB echo 0xEF > /sys/kernel/config/usb_gadget/pi4/bDeviceClass echo 0x02 > /sys/kernel/config/usb_gadget/pi4/bDeviceSubClass echo 0x01 > /sys/kernel/config/usb_gadget/pi4/bDeviceProtocol mkdir /sys/kernel/config/usb_gadget/pi4/strings/0x409 echo 100000000d2386db > /sys/kernel/config/usb_gadget/pi4/strings/0x409/serialnumber echo "Samsung" > /sys/kernel/config/usb_gadget/pi4/strings/0x409/manufacturer echo "PI4 USB Device" > /sys/kernel/config/usb_gadget/pi4/strings/0x409/product mkdir /sys/kernel/config/usb_gadget/pi4/configs/c.2 mkdir /sys/kernel/config/usb_gadget/pi4/configs/c.2/strings/0x409 echo 500 > /sys/kernel/config/usb_gadget/pi4/configs/c.2/MaxPower echo "UVC" > /sys/kernel/config/usb_gadget/pi4/configs/c.2/strings/0x409/configuration mkdir /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0 mkdir /sys/kernel/config/usb_gadget/pi4/functions/acm.usb0 mkdir -p /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/control/header/h ln -s /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/control/header/h /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/control/class/fs mkdir -p /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p mkdir -p /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/dwFrameInterval 5000000 EOF cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/wWidth 1920 EOF cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/wHeight 1080 EOF cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/dwMinBitRate 10000000 EOF cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/dwMaxBitRate 100000000 EOF cat <<EOF > /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/mjpeg/m/1080p/dwMaxVideoFrameBufferSize 7372800 EOF mkdir /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/header/h cd /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0/streaming/header/h ln -s ../../mjpeg/m cd ../../class/fs ln -s ../../header/h cd ../../class/hs ln -s ../../header/h cd ../../../../.. ln -s /sys/kernel/config/usb_gadget/pi4/functions/uvc.usb0 /sys/kernel/config/usb_gadget/pi4/configs/c.2/uvc.usb0 ln -s /sys/kernel/config/usb_gadget/pi4/functions/acm.usb0 /sys/kernel/config/usb_gadget/pi4/configs/c.2/acm.usb0 udevadm settle -t 5 || : ls /sys/class/udc > /sys/kernel/config/usb_gadget/pi4/UDC
That thread was and excellent source of information, and discussed some solutions using g_webcam and others using configfs. I went with configfs, as it seems to be the preferred options these days. Other references are listed below at the end of the article.
Now, power down your Pi Zero, and plug the USB cable into the USB port marked “USB” (not the one marked “PWR”), and the other end into your PCs USB port and see if the serial gadget comes up in Windows Device Manager. We’ll be running the Pi headless from now on with access via USB-serial or WiFi.
Here we can see that both the UVC camera and the serial device are now present in Device Manager on Windows. You should be able to connect a serial terminal to the Pi, and log in at the prompt. This saves you having to have the webcam connected to WiFi in order to login to tweak things. Use 115200 baud to connect.
Next, open up your camera app, and you should now see the output of the camera. You should be able to use the camera an any of your favourite video capture or video conferencing apps.
With a Raspberry Pi zero W, and using a class 10 SD card (~20MB/sec), the piwebcam boots in about 35 seconds. There’s lots of articles with tips and tricks on reducing this time, I didn’t manage to get configuration below 25 seconds, but didn’t spend too long at it. It should be possible to do better.
With the Raspberry Pi Zero at about £5 /€5.50, the Camera at £50/€55, and the standard wide(ish) angle lens at £25/€27.50, that’s a VERY decent interchangeable lens setup for about £80/€85.
Next to see if it’s possible to get 4k out of it! 😉
https://www.raspberrypi.org/forums/viewtopic.php?t=148361 (most useful thread)
https://github.com/wlhe/uvc-gadget (forked from this version of uvc-gadget)
https://training.ti.com/sites/default/files/docs/USB-M6-USB-in-Device-Mode.pdf (really good presentation on Kernel UDC interface and configfs)
http://irq5.io/2016/12/22/raspberry-pi-zero-as-multiple-usb-gadgets/ (setting up multiple gadgets on one USB interface)
https://gist.github.com/justinschuldt/36469e2a89d95ef158a8c4df091e9cb4 (Good clean HOWTO on a very similar setup, was told about this link after I’d spend a few days trawling through the other references) 🙂