The worksheet is a companion to this video.
Every command below can be copied and pasted. Blanks are for values you'll need again later.
Pick your path:
Starting fresh? Go to Parts list and work through STEP 1 to STEP 7, then pick any FEATURES you want.
Already built v1 or v2? Do the UPGRADE section right below, then jump to the v3 FEATURES.
UPGRADE FROM V1 OR V2
Upgrading takes three commands. SSH into the monitor (STEP 3 shows how), then:
sudo apt install -y v4l-utils cd ~/rtsp-monitor && wget -O monitor.py https://dad-the-engineer.com/s/monitor.py sudo systemctl restart rtsp-monitor
(v4l-utils is the HDMI-CEC tool for TV remote control. It's small and does nothing until you set CEC=1, so everyone gets it.)
Your existing cameras.conf works unchanged. Every v3 setting is off by default and behaves like v2 until you turn it on.
Want the new commented config to copy settings from, without overwriting yours?
wget -O ~/rtsp-monitor/cameras.conf.v3-example https://dad-the-engineer.com/s/cameras.conf
What you might notice after upgrading:
ROTATE now rotates the grid too. In v2 it only turned single camera view. If you had ROTATE set, your grid now matches your cameras. That's the bug fix.
Coming from v1: the grid now keeps each camera's proportions (fill and crop) instead of stretching. Add |fit=stretch to a camera's line to get the old look back. All the v2 FEATURES below are new to you too.
Parts list
Raspberry Pi 4 (1gb) - $35 at Micro Center. Available on Amazon, here. A Raspberry Pi 5 works too.
Hosyond 7" DSI Touchscreen - $38 on Amazon, here (or the official Touch Display 2, or a TV over 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 (on a Pi 5, the official 27W supply is the safe bet)
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 (or Raspberry Pi 5)
OS: Raspberry Pi OS (other) > Raspberry Pi OS Lite (64-bit)
Storage: your microSD card
Use Lite, not the regular desktop version. The desktop grabs the screen and the monitor can't draw on it. (Already flashed the desktop version? See Troubleshooting.)
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 screen.
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.
Official Raspberry Pi touchscreen (original 7") 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.
Official Touch Display 2: assemble per its manual. It's a portrait panel, so you'll set ROTATE in FEATURE 5 to watch it in landscape.
Building the TV version instead? Skip the display: it's just the Pi and an HDMI cable. See FEATURE 4.
Optional: wire up a momentary switch (to GPIO 17 and ground, for example). See FEATURE 2.
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:
ssh USERNAME@PI-IP-ADDRESS(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 v4l-utils
(v4l-utils is for TV remote control over HDMI-CEC. It sits idle unless you turn on CEC in FEATURE 4, so install it now and you'll never have to come back for it.)
STEP 4: Camera RTSP URLs
For the 7 inch touchscreen, use the SUBSTREAM (low resolution) URL for every camera. For a TV build, see FEATURE 4. 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-NAMETips: 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 screen (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: ________________________________
Camera 5 name and URL: ________________________________
Camera 6 name and URL: ________________________________
Pick names you can type twice. In v3, grids and the rotation order refer to cameras by name.
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. Delete or replace the example cameras.
Then set GRID (1 = grid view on) and CYCLE_SECONDS (0 = manual, or seconds between automatic rotation). Everything else in the file is optional and covered in the FEATURES sections; the defaults give you the classic setup.
Save with Control-X, then Y, then Enter.
STEP 6: Permissions and housekeeping (one time)
Group access to video, GPU, touch input, and the console (use YOUR username):
sudo usermod -aG video,render,input,tty 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 view should appear.
STOP HERE if the classic setup is all you want. Everything below is optional.
Daily use
Tap: next view (cameras, then grids, then around again). FEATURE 7 can change this.
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/*/brightnessv2 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. (Want more than one grid, or a different order? See FEATURE 5.)
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 threePer-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=nofit=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
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-gpiozeroThen in cameras.conf:
BUTTON_PIN=17Short press = same as a tap. 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=85Plays the audio of the camera you're looking at, in single camera view only. Grids are 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=noOn a TV, sound comes out of the TV over HDMI. On the touchscreen build, use the Pi's 3.5mm jack (Pi 4) 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=1920x1080FEEDS 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, and a 4K or 5MP main stream is too much for the Pi to decode. Aim for about 1080p per camera. Cautions:
Any stream wider than 1920 pixels needs hwdec=no on its line (Pi 4 hardware decoder limit).
A full grid of high resolution streams can choke a Pi. Resolution is per camera line, so mix: higher resolution for cameras you fullscreen, substreams for grid filler.
If your camera has no 1080p stream and you run Frigate, go2rtc can make one from the main stream. Add a new stream in your Frigate config (CAMERA is your existing go2rtc stream name), then use rtsp://FRIGATE-IP:8554/CAMERA_tv in cameras.conf:
CAMERA_tv:
- ffmpeg:CAMERA#video=h264#height=1080#hardwareThen enable remote control. The CEC tool (v4l-utils) went in with STEP 3, so it's just one line in cameras.conf:
CEC=1On 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 view
Left / Up = previous view
Number buttons = jump straight to a camera (its position in the rotation)
0 = your HOME view (FEATURE 7), or the first grid if HOME is blank
v3 FEATURES
Same rules: optional, OFF by default, all in cameras.conf, restart after any change.
FEATURE 5: Rotation that includes the grid
For portrait-native panels mounted in landscape, like the official Touch Display 2. In cameras.conf:
SCREEN=1280x720
ROTATE=90SCREEN is the LANDSCAPE size you watch, not the panel's portrait size. 1280x720 is for the 7 inch Touch Display 2; for other panels, use your panel's resolution with the bigger number first.
90 usually. If everything comes up upside down, use 270.
v3 rotates single camera view AND the grids. (In v2, the grid ignored ROTATE.)
The video=...rotate= trick in cmdline.txt only rotates the text console. The monitor needs ROTATE.
ROTATE that worked for me: ________________________________
FEATURE 6: Named grids, display order, and grid-only cameras
Build as many grids as you want. One line each, starting with the word GRID:
GRID Main|Driveway, Front Door, Back Yard, Pool|layout=2,2
GRID Yard|Back Yard, Pool, Side Gate|layout=1,2The order on the GRID line IS the display order. The first camera takes the first slot, top left, then across and down.
Camera names must match the names on your camera lines (capitals don't matter).
layout= works like LAYOUT in FEATURE 1. Leave it off and the monitor picks one.
rotate= gives one grid its own rotation (0, 90, 180, 270). Example: three cameras stacked tall to fill a portrait panel:
GRID Tall|Front Door, Driveway, Pool|layout=1,1,1,rotate=0Once any GRID line is active, GRID= and LAYOUT= are ignored. They only build the automatic grid.
Grid-only cameras. Add single=no to a camera's line. It never shows full screen on its own, but you can still put it in a grid:
Side Gate|rtsp://viewer:PASSWORD@192.168.0.25:554/h264Preview_01_sub|single=noGrid 1 name and cameras, in order: ________________________________
Grid 2 name and cameras, in order: ________________________________
Grid-only cameras: ________________________________
FEATURE 7: Grid home screen, tap to zoom, and time out
This is the "grid all day" setup. In cameras.conf:
HOME=grid
GRID_TAP=camera
CAMERA_TAP=back
HOME_SECONDS=60HOME: the view shown at boot. A camera or grid name. Using the automatic grid instead of GRID lines? Its name is just grid.
GRID_TAP=camera: tap a panel on a grid to open that camera full screen. (next = the old behavior, go to the next view.)
CAMERA_TAP=back: tap a single camera to go back to the grid you came from. (next = the old behavior.)
HOME_SECONDS: after this many seconds without a tap, go back to HOME. 0 = never.
Leave CYCLE_SECONDS=0 if you don't want it flipping through feeds on its own.
Rotation order. VIEWS lists the cameras and grids a tap steps through, in order. Anything not listed is skipped:
VIEWS=Main, Driveway, Front Door, YardLeave VIEWS blank to get every camera in file order, then the grids.
Tapping opens the wrong camera? Some rotated panels report touch in a different orientation. Set TOUCH_ROTATE=90, restart, and test. Still wrong? Try 180, then 270.
My HOME view: ________________________________
My VIEWS order: ________________________________
TOUCH_ROTATE that worked (if needed): ________________________________
Troubleshooting
Read the monitor's log first; it prints warnings naming anything that's missing or misspelled:
journalctl -u rtsp-monitor -n 50 --no-pagerNo 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.
Video works in VLC but not on the Pi: try replacing --vo=gpu --gpu-context=drm with --vo=drm in the test command, and tell me in the comments.
Pi boots to a desktop: you flashed the regular version instead of Lite. Switch it to console boot, then reboot:
sudo systemctl set-default multi-user.target
sudo rebootBlack 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.
Grid is sideways but cameras are fine: you're on v2. Upgrade (top of this page).
A camera is missing from a grid, or a view is missing from the rotation: the log names it. Usually the name on the GRID or VIEWS line doesn't match the camera line.
LAYOUT stopped working: you added a GRID line. Put layout= on the GRID line instead.
Tapping a grid panel opens the wrong camera: see TOUCH_ROTATE in FEATURE 7.
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 a 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 missing (if you skipped it, run sudo apt install -y v4l-utils). Arrows and OK work on nearly every brand; number buttons vary.
Grid looks cropped compared to v1: that's the 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: everything in cameras.conf
Line formats:
Name|rtsp://url|options camera line
GRID Name|Cam A, Cam B, Cam C|options grid line (v3)
KEY=value setting
# anything commentCamera options: fit=fill | letterbox | stretch, hwdec=no, audio=no, single=no (v3)
Grid options (v3): layout=1,2 (rows), rotate=0 | 90 | 180 | 270
Settings:
CYCLE_SECONDS=0 patrol mode: 0 = manual, or seconds between rotation
GRID=1 include the automatic grid (ignored once GRID lines exist)
LAYOUT= automatic grid rows. Blank = classic 2x2
SCREEN=800x480 1920x1080 for a TV, 1280x720 for Touch Display 2
ROTATE=0 90 or 270 for portrait panels. Rotates grids too (v3)
VIEWS= v3: names in tap order. Blank = all cameras, then grids
HOME= v3: view shown at boot. Blank = first view
HOME_SECONDS=0 v3: seconds idle before going back to HOME. 0 = never
GRID_TAP=next v3: next, or camera (open the tapped panel)
CAMERA_TAP=next v3: next, or back (return to the grid)
TOUCH_ROTATE=0 v3: only if taps open the wrong camera
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 HDMIMaintenance
Every month or two:
sudo apt update && sudo apt full-upgrade -yAfter editing cameras.conf, restart the monitor:
sudo systemctl restart rtsp-monitorUpdating the script later (keeps your cameras.conf):
cd ~/rtsp-monitor && wget -O monitor.py https://dad-the-engineer.com/s/monitor.py
sudo systemctl restart rtsp-monitor