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


Next
Next

Hall of Shame - YT comment edition, pt. 2