How to work Pico B with CSI2 Camera module as UVC.

  • Hello,

    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
  • Thank you for the Link. However, that is the way to connect a USB camera to a LuckFox Pico, which is the opposite of what I want to achieve.
    I will wait for the SDK to be updated in the future.
    Thanks again.
  • Thank you for your feedback. If your goal is realized in a future SDK update, we will notify you as soon as it becomes available.
  • I have the same function requirement, Does the SDK have update?
  • I believe this should work with the SDK as-is. You´d need to configure the kernel to add UVC USB gadget, then configure your usb gadget module
    https://www.kernel.org/doc/html//next/u ... t_uvc.html
  • Not sure about Pico B, but I'm pretty close to making it work on Pico Pro. Here's what I did:

    -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.
  • On my RV1106 pico pro board,
    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 :)

    Last edited by Hamstakillah on 2026-06-07 17:22, edited 1 time in total.