Skip to content

Instantly share code, notes, and snippets.

@ABelliqueux
Created March 14, 2025 15:29
Show Gist options
  • Save ABelliqueux/2ae69037349ee64fe1a924682d0142ac to your computer and use it in GitHub Desktop.
Save ABelliqueux/2ae69037349ee64fe1a924682d0142ac to your computer and use it in GitHub Desktop.
Plug-and-play Raspberry Pi USB webcam with serial

Plug-and-play Raspberry Pi USB webcam with serial

This is a refinement of the tutorial available here :

https://www.raspberrypi.com/tutorials/plug-and-play-raspberry-pi-usb-webcam/

It adds a serial device with autologin, that you can then connect to while the camera is running and use v4l-ctl to change the picture settings.

Follow the instructions on the link above, but use this script instead for .rpi-uvc-gadget.sh:

#!/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

create_serial() {
  mkdir -p $GADGET/g1/functions/acm.usb0
  ln -s $GADGET/g1/functions/acm.usb0 $GADGET/g1/configs/c.1/acm.usb0
}

# 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
	# Add serial port
	create_serial
	
	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

Source: https://github.com/showmewebcam/showmewebcam/blob/master/package/piwebcam/multi-gadget.sh#L43

Enabling serial console login

You will have to run the following command to enable serial console :

sudo ln -s /lib/systemd/system/[email protected]  /etc/systemd/system/getty.target.wants/[email protected]

Source: http://www.davidhunt.ie/raspberry-pi-zero-with-pi-camera-as-usb-webcam/

Serial console autologin

Edit /lib/systemd/system/[email protected] and look for the ExecStartline in [service], replace with (change $USER to your actual username):

ExecStart=-/sbin/agetty -o '-p  -f -- \\u' --noclear --autologin $USER %I $TERM

Source: https://forums.opensuse.org/t/autologin-in-text-mode/153127#post_17

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment