## Step 1: Fresh OS Installation
1. **Download Raspberry Pi OS Lite** from the official website
Doesn't need to be lite. This will work with the Desktop images too.
Imager does the download for you so this is not necessary.
2. **Flash to microSD** using Raspberry Pi Imager
3. **Enable SSH** by creating empty file named `ssh` in boot partition
4. **Configure WiFi** by creating `wpa_supplicant.conf` in boot partition:
```
country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid="YourWiFiName"
psk="YourWiFiPassword"
}
```
That's not going to work on Bookworm as it uses Network Manager not wpa_supplicant and that mechanism is no longer supported. Set it via Imager instead.
And not everyone needs a country of US. Despite what its President thinks there are 194 other countries most of which have different rules and regulations.
5. **Boot the Pi** and connect via SSH: `ssh pi@raspberrypi.local`
Not going to work. There will be no configured user account. The Pi will be running the first boot wizzard on the local console asking you to configure one.
## Step 2: Configure USB Gadget Mode
### Edit Boot Configuration
```bash
# Edit config.txt
sudo nano /boot/config.txt
# Add these lines:
start_x=1
dtoverlay=imx708
# Change camera_auto_detect to 0:
camera_auto_detect=0
Nope. Not needed.
# Add at the end:
dtoverlay=dwc2,dr_mode=peripheral
```
,dr_mode=peripheral is not required on any zero series Pi. It's the hardware default. [edit]TO clarify, unless the ID pin in the uUSB data connector is pulled low both the driver and hardware default to peripheral mode.[/edit]
### Edit Command Line Parameters
```bash
# Edit cmdline.txt
sudo nano /boot/cmdline.txt
# Add modules-load=dwc2 to the end of the single line
# Example result:
# console=serial0,115200 console=tty1 root=PARTUUID=xxx rootfstype=ext4 fsck.repair=yes rootwait cfg80211.ieee80211_regdom=US modules-load=dwc2
```
Not required. Enabling the overlay automatically gets the dwc2 module loaded.
### Enable Required Modules
```bash
# Add USB gadget modules
echo 'dwc2' | sudo tee -a /etc/modules
echo 'libcomposite' | sudo tee -a /etc/modules
```
### Reboot and Verify
```bash
sudo reboot
# After reboot, verify gadget mode:
lsusb # Should be empty (no USB devices)
lsmod | grep -E "(dwc2|libcomposite)" # Should show modules loaded
```
## Step 3: Install Dependencies
```bash
# Update system
sudo apt update
sudo apt upgrade -y
# Install build tools and dependencies
sudo apt install -y git cmake build-essential pkg-config
sudo apt install -y libcamera-dev libcamera-apps libevent-dev
sudo apt install -y libjpeg-dev libjpeg62-turbo-dev
sudo apt install -y meson ninja-build
sudo apt install -y v4l-utils
```
## Step 4: Download and Build UVC Gadget
```bash
# Clone the official UVC gadget repository
cd /home/pi
git clone https://gitlab.freedesktop.org/camera/uvc-gadget.git
cd uvc-gadget
# Checkout the stable commit
git checkout 04c18aa
# Build with meson
meson build
ninja -C build
# Install the binary and library
sudo cp build/src/uvc-gadget /usr/bin/
sudo chmod +x /usr/bin/uvc-gadget
sudo ninja -C build install
sudo ldconfig
# Verify installation
/usr/bin/uvc-gadget -h
# Should show -c option for libcamera support
```
## Step 5: Create USB Gadget Configuration Script
Create the configuration script:
```bash
nano /home/pi/.rpi-uvc-gadget.sh
```
Why are you hiding it? Why not put it where is belongs: $HOME/bin or /usr/local/bin?
Add this content:
```bash
#!/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 ../../../
# This configures the USB endpoint to allow 3x 1024 byte packets per
# microframe, which gives us the maximum speed for USB 2.0. Other
# valid values are 1024 and 2048, but these will result in a lower
# supportable framerate.
echo 2048 > functions/$FUNCTION/streaming_maxpacket
ln -s functions/$FUNCTION configs/c.1
}
# This loads the module responsible for allowing USB Gadgets to be
# configured through configfs, without which we can't connect to the
# UVC gadget kernel driver
echo "Loading composite module"
modprobe libcomposite
# This section configures the gadget through configfs. We need to
# create a bunch of files and directories that describe the USB
# device we want to pretend to be.
if [ ! -d $GADGET/g1 ]; then
echo "Detecting platform:"
echo " board : $BOARD"
echo " udc : $UDC"
echo "Creating the USB gadget"
echo "Creating gadget directory g1"
mkdir -p $GADGET/g1
cd $GADGET/g1
if [ $? -ne 0 ]; then
echo "Error creating usb gadget in configfs"
exit 1;
else
echo "OK"
fi
echo "Setting Vendor and Product ID's"
echo $VID > idVendor
echo $PID > idProduct
echo "OK"
echo "Setting English strings"
mkdir -p strings/0x409
echo $SERIAL > strings/0x409/serialnumber
echo $MANUF > strings/0x409/manufacturer
echo $PRODUCT > strings/0x409/product
echo "OK"
echo "Creating Config"
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "Creating functions..."
create_uvc configs/c.1 uvc.0
echo "OK"
echo "Binding USB Device Controller"
echo $UDC > UDC
echo "OK"
fi
# Run uvc-gadget. The -c flag sets libcamera as a source, arg 0 selects
# the first available camera on the system. All cameras will be listed,
# you can re-run with -c n to select camera n or -c ID to select via
# the camera ID.
uvc-gadget -c 0 uvc.0
```
Make it executable:
```bash
chmod +x /home/pi/.rpi-uvc-gadget.sh
```
## Step 6: Test Manual Operation
```bash
# Run the configuration script
sudo /home/pi/.rpi-uvc-gadget.sh
```
You should see output showing:
- Platform detection
- USB gadget creation
- Camera detection with libcamera
- "Using camera imx708"
- Control requests from the host
## Step 7: Test with Host Computer
1. **Connect the Pi** to your computer using micro USB cable (data port, not power)
2. **Check Device Manager** (Windows) or System Information (Mac) - should show "UVC Gadget"
3. **Test with applications**:
- Windows Camera app
- OBS Studio
- VLC Media Player
- Web browser camera tests
## Step 8: Set Up Auto-Start
### Method 1: Using rc.local (Recommended)
```bash
sudo nano /etc/rc.local
```
Add before `exit 0`:
```bash
/home/pi/.rpi-uvc-gadget.sh &
```
Not recommended. /etc/rc.local is deprecated and no longer exists on a default RPiOS installation though it will be run if you create it and it has an appropriate shebang and the correct permissions.
### Method 2: Using systemd service
Create the systemd service:
```bash
sudo nano /etc/systemd/system/uvc-webcam.service
```
Add this content:
```ini
[Unit]
Description=UVC Webcam Setup
After=basic.target
Before=multi-user.target
[Service]
Type=simple
ExecStartPre=/bin/bash -c 'pkill -f libcamera || true'
ExecStartPre=/bin/bash -c 'pkill -f camera || true'
ExecStart=/home/pi/.rpi-uvc-gadget.sh
Restart=no
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
```
Enable the service:
```bash
# Reload systemd and enable service
sudo systemctl daemon-reload
sudo systemctl enable uvc-webcam.service
```
## Step 9: Final Testing
1. **Reboot the Pi**: `sudo reboot`
2. **Wait 30-45 seconds** for full boot and service startup
3. **Connect to computer** - should automatically work as plug-and-play webcam
This implies the Pi will have its own PSU as well as power from the USB host. Not a good idea as the higher voltage one will feed power to the lower voltage one. All 5V pins on a zero series Pi are directly connected.
4. **Test in multiple applications** to verify functionality
## Troubleshooting
### Camera Not Detected
```bash
# Check if camera is connected
libcamera-hello --list-cameras
# Should show: 0 : imx708 [4608x2592]
# Verify video devices exist
ls -la /dev/video*
```
### Service Not Starting
```bash
# Check if script is running
ps aux | grep uvc-gadget
# Check for errors
sudo journalctl -xe | grep -i uvc
# Run manually to see errors
sudo /home/pi/.rpi-uvc-gadget.sh
```
### UVC Gadget Binary Issues
```bash
# Check if binary has libcamera support
/usr/bin/uvc-gadget -h
# Should show -c option
# Check library dependencies
ldd /usr/bin/uvc-gadget
# Should show libuvcgadget.so.0
```
### Video Format Issues
```bash
# Check camera capabilities
v4l2-ctl --device=/dev/video0 --list-formats-ext
```
### Windows Compatibility
- Try different applications (OBS, VLC, browser tests)
- Check Device Manager for proper driver installation
- Ensure using data-capable USB cable
## Important Configuration Notes
The `/boot/config.txt` must include:
- `start_x=1` - Enables camera support
- `camera_auto_detect=0` - Disables auto-detection
- `dtoverlay=imx708` - Loads IMX708 driver for Pi Camera 3
- `dtoverlay=dwc2,dr_mode=peripheral` - Enables USB gadget mode
The script supports both:
- **Uncompressed formats** (YUYV) - Better compatibility
- **MJPEG formats** - Lower bandwidth usage
## Result
You now have a **plug-and-play USB webcam** that:
-Starts automatically on boot
-Works with Windows, Mac, and Linux
-Supports multiple resolutions (480p, 720p, 1080p)
-Requires no software installation on host computers
-Features auto-focus with Pi Camera 3
-Uses libcamera for optimal performance
Simply power on the Pi, wait ~30 seconds, then connect via USB to any computer for instant webcam functionality!
A couple of general points:
- Markdown is not supported. You need to replace it with bbcode and/or appropriate tags.
- It's not complete. What about other cameras? No mention of other methods to start at boot (hint: [shameless self promotion]Running A Program At Start Up A Beginner's Guide[/shameless self promotion]
- The functionality is not limited to a zero2w. A, A+, 3A+, zero, zeroW[H], 4B, 400, 5, 500 and all CM series can also act as USB devices with minor variations (A series need to be forced into peripheral mode, 4B and later via the USB C port, CM series varies by carrier board).
- Should work with any supported camera module if you stop loading a specific overlay and allow the firmware to do its job.
Statistics: Posted by thagrol — Fri Jul 04, 2025 2:05 pm