This repository documents how to use a Raspberry Pi CSI camera (v1.3 / OV5647) with MotionEye on Raspberry Pi OS Bullseye (32-bit) by bridging the camera output to a v4l2loopback dummy device.
MotionEye often fails to show CSI camera video because it expects a V4L2 camera device, while Raspberry Pi CSI cameras are handled differently by the kernel and camera stack. This setup works around that limitation cleanly and reliably.
- MotionEye does not display CSI camera video
- Camera appears as
vc.ril.camera /dev/video0exists but MotionEye cannot use it directly- Selecting MMAL or V4L2 shows no video
- MotionEye requires a standard V4L2-compatible device
We solve this by:
- Creating a dummy V4L2 camera using
v4l2loopback - Using FFmpeg to forward the real CSI camera (
/dev/video0) into the dummy device (/dev/video1) - Running everything automatically at system boot using systemd services
- Configuring MotionEye to use
/dev/video1
- Raspberry Pi (Zero / Zero 2 / 3 / 4)
- CSI Camera (OV5647 / v1.3)
- Raspberry Pi OS Bullseye (32-bit)
- MotionEye installed
- Required packages:
sudo apt install ffmpeg v4l2loopback-dkms v4l-utils| Device | Description |
|---|---|
/dev/video0 |
Real CSI camera |
DummyCam |
Dummy camera created by v4l2loopback |
Creates the dummy V4L2 camera at boot.
[Unit]
Description=Load v4l2loopback dummy camera
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/sbin/modprobe v4l2loopback video_nr=1 card_label=DummyCam exclusive_caps=1
RemainAfterExit=yes
[Install]
WantedBy=multi-user.targetWhat this service does:
- Loads the
v4l2loopbackkernel module - Creates
/dev/video1 - Labels it as
DummyCam - Runs once at boot
Bridges the real CSI camera to the dummy camera.
[Unit]
Description=FFmpeg bridge from /dev/video0 to /dev/video1
After=v4l2loopback.service
Requires=v4l2loopback.service
[Service]
ExecStart=/usr/bin/ffmpeg -f v4l2 -i /dev/video0 -r 30 -f v4l2 /dev/video1
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetWhat this service does:
- Reads video from
/dev/video0 - Converts it into a standard V4L2 stream
- Forwards it to
/dev/video1 - Forces 30 FPS
- Automatically restarts on failure
The python file does the work of both the service files in one go, it converts the csi input to v4l2, while the python file in running only.
Libraries Required:
sudo apt install -y python3-dev python3-setuptools python3-wheel build-essential libjpeg-dev zlib1g-dev libpng-dev libfreetype6-dev libopenjp2-7-dev libtiff-dev libwebp-dev
One thing you need to remember, is to create a Empty Video Device before running the file.
sudo modprobe v4l2loopback devices=1 video_nr=40 card_label="DummyCam" exclusive_caps=1
Then run the python file
sudo python3 csi_to_v4l2.py
Note: For Pi Zero the dummmy video device created is /dev/video1, which is not possible for Pi-4 & 5, hence we used /dev/video40 for Pi-4 & 5 (Python File)
What this Python File does:
- Reads video from
/dev/video0 - Converts it into a standard V4L2 stream
- Forwards it to
/dev/video40 - Forces 30 FPS
- Automatically restarts on failure
sudo systemctl daemon-reload
sudo systemctl enable v4l2loopback.service
sudo systemctl enable ffmpeg-bridge.service
sudo systemctl start v4l2loopback.service
sudo systemctl start ffmpeg-bridge.serviceAfter reboot, both services will start automatically.
-
Open MotionEye Web UI
-
Add Local V4L2 Camera
-
Set:
- Camera Device:
/dev/video1 - Camera Name: DummyCam
- Camera Device:
-
Save
🎉 MotionEye will now show the CSI camera feed correctly.
List video devices:
ls /dev/video*List camera formats:
v4l2-ctl --list-devicesView FFmpeg logs:
journalctl -u ffmpeg-bridge.service -fCheck service status:
systemctl status v4l2loopback.service
systemctl status ffmpeg-bridge.service- CSI camera works reliably with MotionEye
- Stable 30 FPS
- No
device busyerrors - Fully automatic on boot
- Works on low-power boards like Raspberry Pi Zero