Build your own camera monitor v2.0 (worksheet)
Companion to this video
ALREADY BUILT V1? Skip the build steps. Upgrade in two commands, then jump to the V2 FEATURES sections below:
cd ~/rtsp-monitor && wget -O monitor.py https://dad-the-engineer.com/s/monitor.py sudo systemctl restart rtsp-monitor
Your v1 cameras.conf works unchanged.
Note: the grid now keeps each camera's aspect ratio (fill and crop) instead of stretching. Add |fit=stretch to a camera's line to get the old behavior back
Every command below can be copied and pasted. Blanks are for values you'll need again later.
Parts list:
Raspberry Pi 4 (1gb) - $35 at Micro Center. Available on Amazon, here
Hosyond 7” DSI Touchscreen - $38 on Amazon, here (unless using HDMI, instead)
Momentary switch (optional)- $11 (for 5) on Amazon, here
16GB microSD card
USB-C power adapter - the one I use is on Amazon, here
Wall-mounted case - files located here, by TacoTruckMcTacoTruck
STEP 1: Flash Raspberry Pi OS Lite (64-bit)
• Download the Raspberry Pi Imager: https://www.raspberrypi.com/software/
Settings in the imager:
• Device: Raspberry Pi 4
• OS: Raspberry Pi OS (other) > Raspberry Pi OS Lite (64-bit)
• Storage: your microSD card
Hostname (suggested: pi-cam-mon-0): _______________________________
Username (video uses: netserv): _______________________________
Password: _______________________________
• WiFi: enter your network name and password (or skip if using Ethernet)
• Services: Enable SSH, password authentication
• Write the card, eject, insert into the Pi, power on. Wait for the login prompt on the touchscreen.
Pi IP address (from router client list, or ping the hostname): _______________________________
STEP 2: Assemble the hardware
• Ribbon cable: display board to the Pi port labeled DISPLAY. Lift clip, insert ribbon, close clip. This one cable carries video, touch, and power (Hosyond and similar DSI panels).
• Screw the Pi onto the standoffs on the back of the display, then attach the stand or mount. With the Hosyond, you’ll screw the brass standoffs into the threaded holes on the back of the display that correspond to the RPi mount.
• Note for the Official Raspberry Pi touchscreen only: also connect the two jumper wires, display 5V to GPIO pin 2 (5V) and display GND to GPIO pin 6 (ground). Pin 2 is the second pin from the corner on the OUTER row; pin 6 is two below it on the same row.
• Wire up a momentary switch (to GPIO 17 and ground, for example)
• Power it on
STEP 3: Connect and install software
Open a terminal on your computer (Windows: press Windows+R, type cmd, press Enter), then, using YOUR username and IP:
Hello, World!
(ex: ssh netserv@192.168.0.72)
Run these one at a time:
sudo apt update sudo apt full-upgrade -y sudo apt install -y mpv python3-evdev
STEP 4: Camera RTSP URLs
Use the SUBSTREAM (low resolution) URL for every camera. Common patterns:
Reolink: rtsp://USER:PASS@CAMERA-IP:554/h264Preview_01_sub Amcrest/Dahua: rtsp://USER:PASS@CAMERA-IP:554/cam/realmonitor?channel=1&subtype=1 Hikvision: rtsp://USER:PASS@CAMERA-IP:554/Streaming/Channels/102 Frigate/go2rtc: rtsp://FRIGATE-IP:8554/CAMERA_NAME_sub Blue Iris rtsp://USER:PASS@BLUEIRIS-IP:81/CAMERA-NAME
Tips: create a view-only camera account with an alphanumeric password. If a password has special characters, percent-encode them (@ becomes %40, : becomes %3A, / becomes %2F).
Test each URL on the touchscreen (press q in the SSH window to quit):
mpv --fs --vo=gpu --gpu-context=drm --rtsp-transport=tcp "rtsp://YOUR-URL-HERE"
Camera 1 name and URL: _______________________________
Camera 2 name and URL: _______________________________
Camera 3 name and URL: _______________________________
Camera 4 name and URL: _______________________________
STEP 5: Install the monitor script
mkdir -p ~/rtsp-monitor && cd ~/rtsp-monitor wget https://dad-the-engineer.com/s/monitor.py wget https://dad-the-engineer.com/s/cameras.conf wget https://dad-the-engineer.com/s/rtsp-monitor.service
Edit the camera list with:
nano cameras.conf
One camera per line: Name|URL from Step 4.
Then set GRID (1 = grid view on) and CYCLE_SECONDS (0 = manual, or seconds between automatic rotation)
Save with Control-X, then Y, then Enter
STEP 6: Permissions and housekeeping (one time)
Group access to video, GPU, and touch input (use YOUR username):
sudo usermod -aG video,render,input netserv
Backlight permission rule:
echo 'SUBSYSTEM=="backlight",RUN+="/bin/chmod 666 /sys/class/backlight/%k/brightness /sys/class/backlight/%k/bl_power"' | sudo tee /etc/udev/rules.d/99-backlight.rules
Stop the console from blanking, and hide the cursor:
sudo nano /boot/firmware/cmdline.txt
Add a SPACE at the end of the existing single line, then add (still on the same line):
consoleblank=0 vt.global_cursor_default=0
Save with Control-X, then Y, then Enter.
STEP 7: Autostart as a service
If your username is not netserv (from Step 1), first edit rtsp-monitor.service and change the User, Group, and the two /home/netserv paths. Then:
sudo cp ~/rtsp-monitor/rtsp-monitor.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable rtsp-monitor sudo reboot
About 15 seconds after reboot, the first camera should appear.
Daily use
• Tap: next view (cameras, then grid, then around again)
• Press and hold 1 second: screen off (Pi stays on)
• Tap while dark: wake to the last view
• Brightness (0 to 255, where 0 is off, and 255 is max brightness):
echo 128 | sudo tee /sys/class/backlight/*/brightness
v2 FEATURES
Everything below is optional and OFF by default. All settings go in cameras.conf, NOT in monitor.py.
After any change: sudo systemctl restart rtsp-monitor
FEATURE 1: Custom grid layouts
LAYOUT works in rows, top to bottom. Cameras fill the slots in the order they're listed in the file.
LAYOUT=1,2 one full-width panel on top, two below
LAYOUT=1,3 one wide panel over three small ones
LAYOUT=2,2 classic quad (same as leaving it blank)
LAYOUT=3 a single row of three
Per-camera options go at the end of the camera's line, after another pipe character:
Panoramic|rtsp://viewer:PASSWORD@192.168.0.21:554/stream|fit=fill,hwdec=no
• fit=fill (default): fill the panel, keep proportions, crop the overflow, like the Windows Fill wallpaper option
• fit=letterbox: show the whole image, black bars on the leftover sides
• fit=stretch: stretch to the exact panel shape (the v1 behavior)
• hwdec=no: software decode. Use for any camera that shows smeared or blank video in single view, and for any stream wider than 1920 pixels
My layout plan (rows): _______________________________
Slot 1 (top left): _______________________________
Slot 2: _______________________________
Slot 3: _______________________________
Slot 4: _______________________________
FEATURE 2: Physical button
Any momentary pushbutton. One leg to a GPIO pin, the other to ground. No resistor; female jumper wires mean no soldering.
• Suggested wiring: GPIO 17, which is PHYSICAL pin 11, with a ground right next to it at physical pin 9.
• BUTTON_PIN uses BCM numbering: the GPIO number, not the physical pin number. GPIO 17 = physical pin 11.
sudo apt install -y python3-gpiozero
Then in cameras.conf:
BUTTON_PIN=17
• Short press = next view. Hold 1 second = screen off. Press while dark = wake.
• Works alongside the touchscreen, or completely without one.
GPIO number I used: _______________________________
FEATURE 3: Camera audio
In cameras.conf:
AUDIO=1 VOLUME=85
• Plays the audio of the camera you're looking at, in single camera view only. The grid is always silent, on purpose.
• The camera must have a microphone, and it must be enabled in the camera's own settings.
• Keep one camera muted while others play by adding audio=no to its options:
Noisy Street Cam|rtsp://...|audio=no
• On a TV, sound comes out of the TV over HDMI. On the touchscreen build, use the Pi's 3.5mm jack or a USB speaker
FEATURE 4: TV output with CEC remote control
Plug the Pi into the TV with HDMI, and in cameras.conf set the real output size:
SCREEN=1920x1080
FEEDS ON A BIG SCREEN: the substream advice in Step 4 was for the little 800x480 touchscreen. On a 1080p TV, substreams look terrible blown up. Use HIGHER resolution RTSP feeds in cameras.conf, around 1080p per camera. Two cautions:
• Any stream wider than 1920 pixels needs hwdec=no on its line (Pi 4 hardware decoder limit).
• A full grid of main streams can choke a Pi 4. Resolution is per camera line, so mix: high resolution for cameras you fullscreen, substreams for grid filler.
Then enable remote control:
sudo apt install -y v4l-utils
And in cameras.conf:
CEC=1
On the TV: enable CEC (it hides under brand names: Anynet+ on Samsung, Bravia Sync on Sony, Simplink on LG, sometimes just HDMI-CEC), and switch the TV to the Pi's HDMI input.
• Right / Down / OK = next camera
• Left / Up = previous camera
• Number buttons = jump straight to that camera (its position in cameras.conf)
• 0 = grid view
Troubleshooting
• Read the monitor's log first; it prints warnings naming anything that's missing:
journalctl -u rtsp-monitor -n 50 --no-pager
• No video in Step 4: test the same URL in VLC on your computer (Media > Open Network Stream). If VLC fails too, the URL or credentials are wrong, or RTSP is disabled in the camera's settings.
• Black screen after reboot: read the log above. Nine times out of ten it's a typo in a camera URL.
• Smeared or blank video on one camera: add hwdec=no to that camera's line in cameras.conf.
• Choppy or laggy video: you're probably on the main stream on the small screen. Switch to the substream URL.
• Touch does nothing: confirm your user is in the input group (log out and back in after Step 6), and that the ribbon cable is fully seated.
• Button does nothing: BUTTON_PIN goes in cameras.conf, NOT in monitor.py. Also check python3-gpiozero is installed, and that you used the GPIO (BCM) number, not the physical pin number.
• No audio: camera mic disabled or absent, the camera line has audio=no, or you're on the grid, which is always silent.
• TV remote does nothing: CEC not enabled on the TV, TV not on the Pi's input, or v4l-utils not installed. Arrows and OK work on nearly every brand; number buttons vary.
• Grid looks cropped compared to v1: that's the new fill default keeping proportions. Use fit=letterbox to see the full frame, or fit=stretch for the old look.
• Screen never sleeps on hold: check the udev rule file from Step 6 for typos, then reboot. On some third-party panels the script dims the backlight to zero instead of switching it off; the screen still goes fully dark either way.
Quick reference: every setting in cameras.conf
CYCLE_SECONDS=0 patrol mode: 0 = manual, or seconds between rotation
GRID=1 include the grid view in the rotation
LAYOUT= blank = classic 2x2. Rows, e.g. 1,2
SCREEN=800x480 or 1920x1080 for a TV
BUTTON_PIN=0 0 = off. GPIO (BCM) number of your button
AUDIO=0 1 = play current camera's audio
VOLUME=85 0 to 100
CEC=0 1 = TV remote control over HDMI
Per-camera options: fit=fill | letterbox | stretch, hwdec=no, audio=no
Maintenance
• Every month or two:
sudo apt update && sudo apt full-upgrade -y
• After editing cameras.conf, restart the monitor:
sudo systemctl restart rtsp-monitor