Turn your Raspberry Pi into a plug-and-play USB webcam using uvc-gadget.
Based on:
- Raspberry Pi Zero 2 W, Pi 4, or Pi 5 with USB OTG support
- Raspberry Pi Camera Module
- MicroSD card (16 GB recommended)
- USB data cable connected to your host computer
- Wi-Fi network access
- Another computer to use the webcam
Install Raspberry Pi OS using Raspberry Pi Imager.
Select:
Raspberry Pi OS (Other)
→ Raspberry Pi OS (Legacy) Lite
The Legacy Lite image is recommended for compatibility with this setup.
This installation runs fully headless (without monitor, mouse, or keyboard).
Before flashing, open the OS Customisation menu and configure the following:
| Setting | Value |
|---|---|
| Hostname | pi-webcam |
| Username | Your preferred username |
| Password | Your preferred password |
| Wi-Fi SSID | Your network name |
| Wi-Fi Password | Your network password |
| SSH | Enabled |
| Authentication | Password authentication |
Enable:
- Wi-Fi
- SSH
- Password authentication
You will use these credentials later to connect over SSH.
- Insert the MicroSD card into the Pi
- Power it on
- Wait approximately 1–2 minutes for first boot
Connect from your computer:
ssh <username>@pi-webcam.localExample:
ssh pi@pi-webcam.localIf .local does not work, locate the Pi IP address from your router or network scanner.
Update packages and reboot:
sudo apt update
sudo apt full-upgrade -y
sudo rebootReconnect via SSH after reboot completes.
sudo umount /boot
sudo sed -i "s:/boot:/boot/firmware:" /etc/fstab
sudo mkdir /boot/firmware
sudo mount /boot/firmwareAppend the OTG overlay to config.txt:
echo "dtoverlay=dwc2,dr_mode=otg" | sudo tee -a /boot/firmware/config.txtThis enables USB gadget mode.
sudo apt install -y git meson libcamera-dev libjpeg-devClone the repository:
git clone https://gitlab.freedesktop.org/camera/uvc-gadget.git
cd uvc-gadgetCompile and install:
make uvc-gadget
cd build
sudo meson install
sudo ldconfigCreate the startup script:
sudo nano ~/.rpi-uvc-gadget.shPaste the following:
#!/bin/bash
# Variables we need to make things easier later on.
CONFIGFS="/sys/kernel/config"
GADGET="$CONFIGFS/usb_gadget"
VID="0x0525"
PID="0xa4a2"
SERIAL="0123456789"
MANUF=$(hostname)
PRODUCT="UVC Gadget"
BOARD=$(strings /proc/device-tree/model)
UDC=`ls /sys/class/udc` # will identify the 'first' UDC
# Later on, this function is used to tell the usb subsystem that we want
# to support a particular format, framesize and frameintervals
create_frame() {
# Example usage:
# create_frame <function name> <width> <height> <format> <name> <intervals>
FUNCTION=$1
WIDTH=$2
HEIGHT=$3
FORMAT=$4
NAME=$5
wdir=functions/$FUNCTION/streaming/$FORMAT/$NAME/${HEIGHT}p
mkdir -p $wdir
echo $WIDTH > $wdir/wWidth
echo $HEIGHT > $wdir/wHeight
echo $(( $WIDTH * $HEIGHT * 2 )) > $wdir/dwMaxVideoFrameBufferSize
cat <<EOF > $wdir/dwFrameInterval
$6
EOF
}
# This function sets up the UVC gadget function in configfs and binds us
# to the UVC gadget driver.
create_uvc() {
CONFIG=$1
FUNCTION=$2
echo "Creating UVC gadget functionality : $FUNCTION"
mkdir functions/$FUNCTION
create_frame $FUNCTION 640 480 uncompressed u "333333
416667
500000
666666
1000000
1333333
2000000
"
create_frame $FUNCTION 1280 720 uncompressed u "1000000
1333333
2000000
"
create_frame $FUNCTION 1920 1080 uncompressed u "2000000"
create_frame $FUNCTION 640 480 mjpeg m "333333
416667
500000
666666
1000000
1333333
2000000
"
create_frame $FUNCTION 1280 720 mjpeg m "333333
416667
500000
666666
1000000
1333333
2000000
"
create_frame $FUNCTION 1920 1080 mjpeg m "333333
416667
500000
666666
1000000
1333333
2000000
"
mkdir functions/$FUNCTION/streaming/header/h
cd functions/$FUNCTION/streaming/header/h
ln -s ../../uncompressed/u
ln -s ../../mjpeg/m
cd ../../class/fs
ln -s ../../header/h
cd ../../class/hs
ln -s ../../header/h
cd ../../class/ss
ln -s ../../header/h
cd ../../../control
mkdir header/h
ln -s header/h class/fs
ln -s header/h class/ss
cd ../../../
# Maximum USB 2.0 bandwidth
echo 2048 > functions/$FUNCTION/streaming_maxpacket
ln -s functions/$FUNCTION configs/c.1
}
# Load composite module
echo "Loading composite module"
modprobe libcomposite
# Configure USB gadget
if [ ! -d $GADGET/g1 ]; then
echo "Detecting platform:"
echo "board : $BOARD"
echo "udc : $UDC"
echo "Creating USB gadget"
mkdir -p $GADGET/g1
cd $GADGET/g1 || exit 1
echo $VID > idVendor
echo $PID > idProduct
mkdir -p strings/0x409
echo $SERIAL > strings/0x409/serialnumber
echo $MANUF > strings/0x409/manufacturer
echo $PRODUCT > strings/0x409/product
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
create_uvc configs/c.1 uvc.0
echo $UDC > UDC
fi
# -c 0 selects the first available camera
uvc-gadget -c 0 uvc.0Save and exit:
CTRL + X
Y
ENTER
sudo chmod +x ~/.rpi-uvc-gadget.shEdit rc.local:
sudo nano /etc/rc.localAdd this line before exit 0:
/home/<username>/.rpi-uvc-gadget.sh &Example complete file:
#!/bin/sh -e
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
/home/<username>/.rpi-uvc-gadget.sh &
exit 0Save and exit:
CTRL + X
Y
ENTER
sudo shutdown -h nowUse the USB OTG/data port, not the power-only port.
Then:
- Connect the Raspberry Pi to your computer
- Power on the Pi
- Wait approximately 30–60 seconds for boot
Your computer should automatically detect:
UVC Gadget
as a standard USB webcam.
List video devices:
v4l2-ctl --list-devicesTest the webcam:
ffplay /dev/video0Open one of the following:
- Camera app
- OBS Studio
- Zoom
- Microsoft Teams
Select:
UVC Gadget
Open:
- QuickTime Player
- OBS Studio
- Zoom
Choose:
UVC Gadget
Verify the camera works:
libcamera-helloList available cameras:
libcamera-hello --list-camerasVerify the OTG overlay:
cat /boot/firmware/config.txtEnsure this line exists:
dtoverlay=dwc2,dr_mode=otg
Run the script manually:
~/.rpi-uvc-gadget.shCheck logs:
dmesg | tail -50ls /sys/class/udcYou should see a USB controller listed.
| Feature | Note |
|---|---|
| Compression | MJPEG generally performs better than uncompressed video |
| 1080p Support | Depends on USB bandwidth and Raspberry Pi model |
| USB 2.0 Bandwidth | Can limit frame rates at high resolutions |
| Multiple Cameras | Select camera using uvc-gadget -c <camera_number> uvc.0 |
Example:
uvc-gadget -c 1 uvc.0This guide is based on official Raspberry Pi documentation and community contributions.