Raspberry Pi Zero with Pi Camera as USB Webcam

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 USB port in on-the-go 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 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:

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.

modules-load=dwc2,libcomposite

Also edit the boot config, adding an extra line at the end containing:

dtoverlay=dwc2

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! 😉

References:
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) 🙂

23 thoughts on “Raspberry Pi Zero with Pi Camera as USB Webcam”

  1. Dave,
    Cheers. Great follow-on experiment/article. If I could ever get a RPi HQ camera I could join the fun; they seem to be in very limited supply. Probably someone is writing about how good they are and getting people excited!
    Ric

  2. Thank you.

    I’m happy to report it also works with Ubuntu desktop and a Zero + older (cheap) v1 Camera.

    Just plug it in, and show the stream with `mpv /dev/video0`

    1. Another addition: on Ubuntu you need to stop ModemManager to get serial working.

      systemctl stop ModemManager.service

  3. Congrats for getting this working and thanks for the detailed post! I’ve been following the forum thread you linked to on raspberrypi.org for a few weeks. Unfortunately, I haven’t been able to get it to work with my Mac.

    Have you tried running the uvc-gadget binary with a static Jpeg file instead of streaming from the camera? I’ve been trying this but can’t get it to show anything:
    sudo ./uvc-gadget -f1 -s2 -r1 -d -i ../ColorBars.jpg -u /dev/video0

    Where ColorBars.jpg is a 1920×1080 Jpeg image of some standard color bars.

    I also noticed that in the multi-gadget.sh file you configure dwFrameInterval only for 2 FPS (5000000) but in your YouTube video you’re showing 30 FPS. How’s that working? Does Windows ignore the USB gadget FrameInterval?

    Thanks for your insights.

  4. David, a great thanks for cracking the riddle of getting this working. You don’t happen to own a Chromebook you could test this on do you? Been dying for a decent webcam solution for my Chromebox and this just might fit the bill …. if Chrome OS recognizes the USB device. Would hate to waste $100 on the camera hardware just to find out it is incompatible. Many thanks in advance either way, cheers!

    1. Hi, I usually log in with the serial console and power down that way. It would be a nice project to add a button on a GPIO to automatically shutdown.

  5. Hi, thanks for this detailed tutorial and I got multiple cameras set up as USB cam, won’t scratch my head moving my laptop around for video conference at work now 🙂

    because I have multiple RPi zero W and different types of camera modules I tested everything: V1, V2, 3rd party IR/non-IR/IR-cut cameras(with OV5647 sensor same as V1) & HQ – ALL work nicely. Of course as original uvc-gadget instructions indicated sometimes you need to manually adjust ‘brightness’ to get to the right exposure.

    I found out that certain apps can’t differ from multiple cameras hooked up at the same time (Discord & Zoom definite only sees the first one but multiple camera entries are present). I don’t know how to meaningfully alter the script to make them appear differently, basic change of names in multi-gadget.sh would result in weird driver recognition behavior in Windows 10.

    While this enables RPi zero to be used as USB webcam without worrying about security over wifi, I wonder if there’s an easy way to manually adjust the camera controls (ISO, WB, etc) on the fly, say, execute a few lines in the serial console.

    Looking forward to your 4K cam implementation!

    1. When I’m logged in over serial, I can issue the following commands to adjust various things…
      /usr/bin/v4l2-ctl -c auto_exposure=0
      /usr/bin/v4l2-ctl -c auto_exposure_bias=8
      /usr/bin/v4l2-ctl -c contrast=20
      /usr/bin/v4l2-ctl -c video_bitrate=25000000
      I think that bitrate is the max possible with a pi zero, so no hope of 4K, I’m afraid! 🙂

      1. Marvelous! I find the results of tuning it before activating camera in an app a bit different than changing it on the fly (when activated), but it’s really minor.

        I am very happy with the result. Thanks again.

  6. Hi there!

    Thank you so much for getting this working, I love my new 1080p DIY webcam! Any idea why I have to turn my Bluetooth off and on again to get it working after the webcam connects?

  7. Really great post which helped no end! Had a few difficulties but got everything working as it should using a standard camera module v2 and pi zero v1.3.

    That was until the windows camera app suddenly stopped presenting the image…seems to have developed difficulties with the serial connection

  8. Thanks a lot for this really cool tutorial. I got it working in only 10 minutes!

    However, I have quite a big latency: about 0.5s. Do you have the same results?
    How do you think I could reduce this latency?

    1. Hi, I don’t see the latency you’re seeing. Check the video, about 2 minutes in, I do a latency test. Maybe 200mS?

  9. Hi!

    Cool project! Two questions:

    1. Is there anything specific about the Zero that is required for this to work, or could it also work with a regular RPi4?
    2. Is there anything specific about the RPi camera that is required for this to work, or could it work with any video device that linux recognizes (v4l2, /dev/video0), assuming the video format is usable and the /dev/video0 hardware node is already configured properly?

    I want to do a spin-off project and need more CPU power for video postprocessing. The plan is to make an external IP-Camera look like a dumb USB webcam to windows by streaming it to a loopback v4l2-device on the Pi and then passing whatever comes in on this virtual device on to the USB connector, which Windows identifies as a USB webcam.

    The rationale is to offload h.264 decoding to the Pi, because my work laptop isn’t powerful enough to do that AND run Skype, and the IP-camera manufacturer’s bridge drivers are just ugly.

    1. 1. The Pi Zero uses it’s USB-OTG port, which I don’t think are available on the other modes, except the Pi4. The Pi4 can use it’s power USB-C port as USB-OTG, but you’re then powering the Pi4 off your laptop’s USB port, which may be too much power draw for the Pi4 to operate reliably, especially if you start using WiFi on it. Not really recommended. And the whole Idea was to just have a single cable between the Pi/Camera and the latop.
      2. I don’t think you can do that. i.e. use the Pi to do hardware encode the stream from an external USB webcam, and then send that on through v4l2. But I could be wrong. It’s very straightforward with a picamera, I’d suggest getting your hands on one! 🙂

      1. Hi! Thanks for the quick reply!

        1. Right, I hadn’t thought about the OTG-port, since what we are doing is technically not really OTG at all. But you’re right, since we’re abusing the OTG driver for it, that might be a problem.
        On the other hand the RPi4 has one and there are tons of ways to power a RPi nowadays, and I’m not really concerned about a clean, wireless look. Power is something to keep in mind, but I believe it can be solved.

        2. I think you misunderstood my plan! I don’t want to use a USB webcam WITH the RasPi, I want the RasPi itself to look like a UVC-USB-Camera when I plug it into my computer (just like what you did above), but instead of taking the image from its “internal” camera, I want the RasPi to use some RTP stream over the internet as source, but tell Windows “Hey, I’m a cheap USB webcam, here’s your MJPEG video, none of your business where the pixels are actually coming from!”

        So, my assumptions were:
        – the RPi’s High Quality Camera creates a /dev/videoX device node on the RPi and provides a video stream to it through its driver in raspbian.
        – uvc-gadget attaches to that video device node, picks up the stream that is provided, and forwards it to the PC attached to the USB port, making the computer think it’s talking to a class-compliant USB imaging device.

        If everything so far is correct, then uvc-gadget shouldn’t care what the camera physically looks like, as long as there is a device node /dev/videoX in linux, and there can be a video stream read from that device node.

        I can both fabricate the /dev/videoX device node and make a video stream come out of it (in a format of my choosing) using v4l2-loopback and ffmpeg, so my hope is that uvc-gadget can be persuaded to pick up and forward that video stream to the PC instead.

        Any thoughts?
        Might work or waste of time?

        1. I did get the bit about wanting to use the Pi to emulate a webcam like I’ve done, but replacing the video source with something external. Thought it was a USB device on the PI rather than RTSP source. Did you see the previous blog where I used uv4l to present as an MJPEG camera? Laggy and low frame rate due to it being MJPEG rathen than h264. Interesting idea though.

          1. Thanks for pointing out the link, I missed that one. Even though the discussion is old and partially outdated there is a ton of useful information there.

            Seems to me like it should be doable in principle and most of my assumptions hold, but usb-gadget seems to be particularly picky about video format provided.
            That is something that can probably be solved by doing inline conversions with ffmpeg’s “complex filters”. It’s a pain to set up if you don’t know exactly what you’re aiming for and we’ll have to see about lag of course, but the RPi4 is capable of transcoding 1080p h264 in hardware (and ffmpeg can utilize that if configured correctly), so it might not be so bad after all. We’ll see…

            Ordering the hardware now, I will report back if I can get it to work. Doing this in my (non-existant) free time though, so it might be a while :o)

            Thanks again for providing all these resources!

  10. Just been trying this and sadly failing atm. Install process worked step by step and threw no errors at any point. Am using latest clean RaspiOS image. PC is Windows 10, plug in the USB cable and I get the standard Windows ‘USB Device not recognised’ popup. If I look in device manager, there is no camera, no serial device and a USB Device with ‘Unknown USB Device (Device Descriptor Request Failed) message. Not a lot to go on :/ Any ideas greatly appreciated.

    Thx.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.