How to work Pico B with CSI2 Camera module as UVC.
-
Hello LuckFox-forums members.
I am using Luckfox Pico Mini B and CSI camera module and trying to make it work as a UVC when connected to a PC via USB-C.
". /build.sh lunch", the only option is "applicaton(应用场景): IPC", and I can only build the kernel as an IP camera. And it seems that the SDK does not contain the necessary source code. Maybe Rockchip's SDK has a UVC config.
Is there any restriction that UVC cannot be realized in Luckfox pico? If not, is there any possibility to support it in future SDKs?
Thanks in advance.
To achieve UVC functionality with your Luckfox Pico Mini B and CSI camera module when connected to a PC via USB-C, you can refer to the tutorial at the following link:
Tutorial link:https://wiki.luckfox.com/Luckfox-Pico/L ... usb-camera
I will wait for the SDK to be updated in the future.
Thanks again.
https://www.kernel.org/doc/html//next/u ... t_uvc.html
-compiled buildroot with UVC gadget (you can also manually edit /etc/init.d/S50usbgadget.sh to enable UVC mode)
-cross-compiled uvc-gadget from https://github.com/wlhe/uvc-gadget
This version of uvc-gadget recognizes the UVC device and allows streaming of empty content or JPEG image. What does not work is streaming of V4L2 device to UVC device - for some reason ioctl shows 'bad file descriptor' error.
I think that this may work - v4l2-ctl uses same ioctl methods, but it works in v4l2-ctl and not in uvc-gadget.
I tried /etc/init.d/S50uvcdevice script and /oem/usr/bin/usb_config.sh, but camera device did not appear on host's device manager.
The configfs generated by the script is flawless.
But UDC binding is not interacting with host USB and host is not detecting anything.
I tried UVC gadget on other SoCs, but did not see this issue.
If anyone solved this issue, please share how.
I've seen that there were some advancements to make this work under official Luckfox repository. After numerous attempts to make this work, I gave up and put on an AI agent to figure out how to make a working configuration. Here is what it came up with:
What you need
- Luckfox Pico Pro Max (RV1106) with a compatible camera module (e.g. SC3336)
- USB-C cable connected to your PC
- The Luckfox Pico SDK (either via Docker or native install)
- SocToolKit for flashing the SD card
The short version
The default firmware does NOT configure UVC correctly for the Rockchip rk_mpi_uvc streaming app. There are several
critical issues you need to fix:
1. Wrong configfs naming — The default S50usbdevice names resolution directories as 480p, 720p etc. The rk_mpi_uvc app
expects SDK-style names: 640_480p, 1280_720p. Without this, rk_mpi_uvc can't parse the USB gadget configuration and
the pipeline doesn't start.
2. Missing pipeline config — rk_mpi_uvc looks for its pipeline configuration at /oem/usr/share/uvc_mpi_cfg.conf, NOT
at /userdata/. Without this file, it skips VPSS initialization and outputs zero-length buffers (you'll see "Unable to
queue buffer length is 0" in the log). The file exists in the SDK at output/uvc_mpi_cfg.conf but nobody copies it to
the right place.
3. Handler must start before UDC bind — The DWC3 USB controller on the RV1106 will crash with request was not queued
to ep1in if Windows opens the camera stream and no userspace UVC handler is running. You must start rk_mpi_uvc
(waiting for /dev/video21 to appear) BEFORE binding the UDC. The app will block waiting for the video device, and once
the UDC bind creates it, the handler is ready before Windows tries to stream.
4. Wrong USB parameters — The default script uses streaming_bulk=1, which doesn't work with rk_mpi_uvc. You need
streaming_maxpacket=2048 and uvc_num_request=2 (the SDK defaults).
5. Wrong device class — For a composite ACM+UVC device to enumerate correctly on Windows, you need bDeviceClass=239,
bDeviceSubClass=2, bDeviceProtocol=1. The default script doesn't set these.
Step-by-step instructions
1. Prepare the overlay files
Create these files in your project's overlay directory (or wherever your SDK build picks them up):
overlay/root/rk_mpi_uvc — Copy this binary from your SDK. It's usually at output/rk_mpi_uvc or built as part of the
media/rockit component.
overlay/root/rkuvc.ini — ISP configuration. A minimal working config:
Code: Select all
[video.source]
enable_aiq = 1
enable_vo = 0
enable_npu = 0
enable_uac = 0
[isp.0.adjustment]
contrast = 50
brightness = 50
saturation = 50
sharpness = 50
fps = 30
hue = 50
[isp.0.exposure]
exposure_mode = auto
gain_mode = auto
auto_exposure_enabled = 1
audo_gain_enabled = 1
[isp.0.white_blance]
white_blance_style = autoWhiteBalance
[isp.0.enhancement]
noise_reduce_mode = close
dehaze = close
[isp.0.video_adjustment]
scene_mode = indoor
power_line_frequency_mode = PAL(50HZ)overlay/root/uvc_mpi_cfg.conf — Pipeline configuration. This tells rk_mpi_uvc to use VPSS for format conversion. The
full file is in the SDK at output/uvc_mpi_cfg.conf. Key settings inside it:
- uvc_enable_vpss: "on" — Enables VPSS for scaling/format conversion (without this, raw NV12 goes to USB with zero
frames)
- geometric_output: "16:9" — Maintains aspect ratio matching the sensor
2. Replace the USB gadget init script
Replace /etc/init.d/S50usbdevice with this script. This is the single most critical file:
Code: Select all
#!/bin/sh
# Luckfox Pico Pro Max - UVC Camera gadget
# SDK naming + rk_mpi_uvc auto-start
. /etc/profile
USB_ATTRIBUTE=0x409
USB_GROUP=rockchip
USB_SKELETON=b.1
CONFIGFS_DIR=/sys/kernel/config
USB_CONFIGFS_DIR=${CONFIGFS_DIR}/usb_gadget/${USB_GROUP}
USB_STRINGS_DIR=${USB_CONFIGFS_DIR}/strings/${USB_ATTRIBUTE}
USB_FUNCTIONS_DIR=${USB_CONFIGFS_DIR}/functions
USB_CONFIGS_DIR=${USB_CONFIGFS_DIR}/configs/${USB_SKELETON}
UVC_GS=uvc.gs1
UVC_DIR=${USB_FUNCTIONS_DIR}/${UVC_GS}/
UVC_STREAMING_DIR=${UVC_DIR}/streaming/
UVC_CONTROL_DIR=${UVC_DIR}/control/
UVC_U_DIR=${UVC_STREAMING_DIR}/uncompressed/u/
UVC_M_DIR=${UVC_STREAMING_DIR}/mjpeg/m/
configfs_init()
{
mount -t configfs none ${CONFIGFS_DIR}
mkdir ${USB_CONFIGFS_DIR} -m 0770
echo 0x2207 > ${USB_CONFIGFS_DIR}/idVendor
echo 0x0310 > ${USB_CONFIGFS_DIR}/bcdDevice
echo 0x0200 > ${USB_CONFIGFS_DIR}/bcdUSB
echo 239 > ${USB_CONFIGFS_DIR}/bDeviceClass
echo 2 > ${USB_CONFIGFS_DIR}/bDeviceSubClass
echo 1 > ${USB_CONFIGFS_DIR}/bDeviceProtocol
mkdir ${USB_STRINGS_DIR} -m 0770
SERIAL=$(cat /proc/cpuinfo | grep Serial | awk '{print $3}')
test -z "$SERIAL" && SERIAL=0123456789ABCDEF
echo $SERIAL > ${USB_STRINGS_DIR}/serialnumber
echo "rockchip" > ${USB_STRINGS_DIR}/manufacturer
echo "Luckfox UVC" > ${USB_STRINGS_DIR}/product
mkdir ${USB_FUNCTIONS_DIR}/acm.g0
mkdir ${USB_FUNCTIONS_DIR}/${UVC_GS}
mkdir ${USB_CONFIGS_DIR} -m 0770
mkdir ${USB_CONFIGS_DIR}/strings/${USB_ATTRIBUTE} -m 0770
echo 500 > ${USB_CONFIGS_DIR}/MaxPower
}
# Each resolution dir must use SDK naming: ${W}_${H}p
# Frame interval 333333 = 30fps. Only advertise this one.
configure_uvc_resolution_yuyv()
{
W=$1; H=$2
DIR=${UVC_U_DIR}/${W}_${H}p/
mkdir ${DIR}
echo $W > ${DIR}/wWidth; echo $H > ${DIR}/wHeight
echo 333333 > ${DIR}/dwDefaultFrameInterval
echo $((W*H*20)) > ${DIR}/dwMinBitRate
echo $((W*H*20)) > ${DIR}/dwMaxBitRate
echo $((W*H*2)) > ${DIR}/dwMaxVideoFrameBufferSize
printf "333333\n" > ${DIR}/dwFrameInterval
}
configure_uvc_resolution_mjpeg()
{
W=$1; H=$2
DIR=${UVC_M_DIR}/${W}_${H}p/
mkdir ${DIR}
echo $W > ${DIR}/wWidth; echo $H > ${DIR}/wHeight
echo 333333 > ${DIR}/dwDefaultFrameInterval
echo $((W*H*20)) > ${DIR}/dwMinBitRate
echo $((W*H*20)) > ${DIR}/dwMaxBitRate
echo $((W*H*2)) > ${DIR}/dwMaxVideoFrameBufferSize
printf "333333\n" > ${DIR}/dwFrameInterval
}
program_kill()
{
P_PID=$(ps | grep $1 | grep -v grep | awk '{print $1}')
test -z ${P_PID} || kill -9 ${P_PID}
}
case "$1" in
start)
ifconfig lo up
test -d ${USB_CONFIGFS_DIR} || configfs_init
echo 0x0018 > ${USB_CONFIGFS_DIR}/idProduct
# SDK UVC parameters (NOT streaming_bulk=1)
echo 2048 > ${UVC_DIR}/streaming_maxpacket
echo 2 > ${UVC_DIR}/uvc_num_request
echo "UVC Camera" > ${UVC_DIR}/device_name
echo "UVC Camera" > ${UVC_DIR}/function_name
# Control endpoint
mkdir ${UVC_CONTROL_DIR}/header/h
ln -s ${UVC_CONTROL_DIR}/header/h ${UVC_CONTROL_DIR}/class/fs/h
ln -s ${UVC_CONTROL_DIR}/header/h ${UVC_CONTROL_DIR}/class/ss/h
# YUYV — use 16:9 resolutions only (sensor is 2304x1296)
mkdir ${UVC_U_DIR}
configure_uvc_resolution_yuyv 640 360
configure_uvc_resolution_yuyv 1280 720
# MJPEG
mkdir ${UVC_M_DIR}
configure_uvc_resolution_mjpeg 640 360
configure_uvc_resolution_mjpeg 1280 720
configure_uvc_resolution_mjpeg 1920 1080
# Streaming header + class links
mkdir ${UVC_STREAMING_DIR}/header/h
ln -s ${UVC_U_DIR} ${UVC_STREAMING_DIR}/header/h/u
ln -s ${UVC_M_DIR} ${UVC_STREAMING_DIR}/header/h/m
ln -s ${UVC_STREAMING_DIR}/header/h ${UVC_STREAMING_DIR}/class/fs/h
ln -s ${UVC_STREAMING_DIR}/header/h ${UVC_STREAMING_DIR}/class/hs/h
ln -s ${UVC_STREAMING_DIR}/header/h ${UVC_STREAMING_DIR}/class/ss/h
# Link ACM + UVC into config
ln -s ${USB_FUNCTIONS_DIR}/acm.g0 ${USB_CONFIGS_DIR}/f1
ln -s ${USB_FUNCTIONS_DIR}/${UVC_GS} ${USB_CONFIGS_DIR}/f2
echo "acm_uvc" > ${USB_CONFIGS_DIR}/strings/${USB_ATTRIBUTE}/configuration
program_kill rkipc
# Copy pipeline config to where rk_mpi_uvc expects it
mkdir -p /oem/usr/share
cp /root/uvc_mpi_cfg.conf /oem/usr/share/uvc_mpi_cfg.conf
# Start rk_mpi_uvc BEFORE binding UDC.
# It waits for /dev/video21 (created by UDC bind).
# Without this, DWC3 crashes with "request was not queued to ep1in".
(
while [ ! -e /dev/video21 ]; do sleep 0.1; done
sleep 0.3
LD_LIBRARY_PATH=/oem/usr/lib /root/rk_mpi_uvc \
-c /root/rkuvc.ini -a /etc/iqfiles -l 3 \
>> /tmp/rk_mpi_uvc.log 2>&1
) &
# Bind UDC — this creates /dev/video21 and triggers enumeration
sleep 2
UDC=$(ls /sys/class/udc/ | awk '{print $1}')
echo $UDC > ${USB_CONFIGFS_DIR}/UDC
;;
stop)
echo "none" > ${USB_CONFIGFS_DIR}/UDC 2>/dev/null
program_kill rk_mpi_uvc
;;
restart)
$0 stop; sleep 1; $0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"; exit 1
esac
exit 0
3. Build and flash
Build the firmware using the SDK (Docker or native) and flash to SD card with SocToolKit. Boot the board with USB-C
connected to your PC.
4. Verify
The board should appear as a "Luckfox UVC" camera device. Open it in VLC (dshow://), Windows Camera app, or any camera
app. You should see a 16:9 video stream at 30fps.
Debugging
- cat /tmp/rk_mpi_uvc.log — Check the streaming app log
- dmesg | grep -i uvc — Check kernel UVC messages
- configfs_find_uvc_function error! — This warning appears twice and is harmless
- "Unable to queue buffer length is 0" — The uvc_mpi_cfg.conf is missing from /oem/usr/share/
- Board crashes when opening camera — rk_mpi_uvc wasn't running before UDC bind
- Stretched image — You're using 4:3 resolutions (640x480) with a 16:9 sensor. Switch to 640x360 or 1280x720.
- Low FPS — Windows picks the slowest frame interval from dwFrameInterval. Only advertise 333333 (30fps).
- Board doesn't reboot with reboot command — Physical power cycle required on this board.
The result is my Luckfox successfully appearing as an USB camera in Device Manager ![]()

