Skip to content

Raspberry pi 3

Installation

source: https://www.raspberrypi.com/software/operating-systems/

I downloaded Raspberry Pi OS Lite 64 bit: https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-05-13/2025-05-13-raspios-bookworm-arm64-lite.img.xz

Preparing sdcard with imager

To install the os into the sdcard i use raspberry pi imager in a windows vm.

Open
  > Show virtual hardware details
    > Add hardware
      > USB Host Device

I had troubles mounting the usb on the windows vm, thankfully this user already found the solution: https://superuser.com/questions/1517074/usb-host-is-not-a-valid-device-name-error-when-trying-to-pass-usb-to-qemu

sudo pacman -S qemu-hw-usb-host

REMEMBER THIS while using the imager

  1. Create the username and password for login.
  2. Enable sshd
  3. Set the country code for wifi

After booting into os:

After booting:

To get keyd:

sudo apt update
sudo apt install software-properties-common python3-launchpadlib

I got the keyd deb link from https://packages.debian.org/sid/arm64/keyd/download

wget http://ftp.us.debian.org/debian/pool/main/k/keyd/keyd_2.5.0-4_arm64.deb

The default config:

/etc/keyd/default.conf
[ids]

*

[main]

# Maps capslock to escape when pressed and control when held.
capslock = overload(control, esc)

Raspberry Pi as an adapter+router (wifi to wired internet access)

This bridge setup is identical to the one in CCTV camera setup, the only difference is that in the latter we denied internet connection to the network.

Files:

/etc/systemd/network/10-bridge.netdev
1
2
3
[NetDev]
Name=br0
Kind=bridge
/etc/systemd/network/11-bridge-br0.network
1
2
3
4
5
[Match]
Name=br0

[Network]
Address=192.168.2.1
/etc/systemd/network/12-eth0-slave.network
1
2
3
4
5
[Match]
Name=eth0

[Network]
Bridge=br0
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd

Before proceeding to the remaining steps, confirm that br0 gets created and it gets the IP address mentioned earlier.

Note

  1. For br0 to show UP, at least one of its slave interfaces (here eth0) needs to have a physical link.
  2. APIPA address: 169.254.39.205/16 — This is a self-assigned address that Windows/Linux assigns when it tries to get a DHCP address but fails to get one from a DHCP server.

Now for the DNS server setup

sudo apt update
sudo apt install dnsmasq
/etc/dnsmasq.conf
# Only listen on the bridge interface
interface=br0

# Specify the DHCP range and lease time
dhcp-range=192.168.2.100,192.168.2.200,12h # Start IP, End IP, Lease time

# Gateway for DHCP clients (your bridge's IP)
dhcp-option=3,192.168.2.1

# DNS servers for DHCP clients (Google's DNS or your preferred)
dhcp-option=6,8.8.8.8,8.8.4.4

# Enable logging of DHCP transactions
log-dhcp

# You might want to uncomment this if you have issues with DNS resolution on the Pi itself
# bind-interfaces

Restart:

sudo systemctl restart dnsmasq

IPTables Rules for Internet Access (NAT/Masquerade)

sudo apt install iptables
sudo sysctl -w net.ipv4.ip_forward=1

To make it permanent

/etc/sysctl.conf
net.ipv4.ip_forward=1

Masquerade rule:

sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -i wlan0 -o br0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i br0 -o wlan0 -j ACCEPT

Save this configuration with:

sudo apt install iptables-persistent

To do save it later manually:

sudo netfilter-persistent save

Deny eth0 interface an ip address

Inspecting journalctl -u dhcpcd I realized it was dhcpcd that was assigning the APIPA address (169.254.39.205) to the interface eth0, when it shouldn't get any IP address in our configuration (it being merely a port to our br0 switch). To fix this:

Append denyintefaces eth0 to the end of /etc/dhcpcd.conf.

/etc/dhcpcd.conf
# Rest of the file
denyinterfaces eth0

Note: It is denyinterfaces (plural)

Viewing CCTV cameras from Sony Bravia Google TV

Launch mpv from shell rc

In the shell rc (~/.bashrc, ~/.zshrc, or ~/.zshrc.local if you use grml zsl config), add:

~/.zshrc.local
# Check if already running to avoid duplicates
if pgrep -f "mpv.*rtsp" > /dev/null; then
    echo "Camera stream already running"
else
    mpv --input-ipc-server=/tmp/mpv-socket --vo=gpu \
        'rtsp://admin:password@192.168.1.50:1101/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1101/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1102/live/0/MAIN' \
        'rtsp://admin:password@192.168.1.50:1103/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1103/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1105/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1105/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif' \
        'rtsp://admin:password@192.168.1.50:1106/live/0/MAIN'
fi

Note:

  1. I used shell rc instead of systemd user service because when systemd launches mpv the keybindings set in ~/.config/mpv/mpv.conf don't work.
  2. mpv process is only launched conditionally even though it is present in the shell rc. So logging in from the second tty won't start mpv all over again, and would just echo 'Camera stream already running.'
  3. As it is right now, with keys 'n' and 'p' on the keyboard we can move to next and previous streams in mpv.
  4. With input-ipc-server we have setup a channel through which we can communicate with this mpv process (via TV remote in the coming sections).
  5. Some rtsp streams appear to be duplicates (only differing in the subtype), the two subtypes are the two different quality streams (SD and HD).

With this right after raspberry pi boots up into console and you login, mpv starts. Enable autologin to automate even the login:

Enable auto-login

auto-login with raspi- config.

Run: sudo raspi-config
Choose option: 1 System Options
Choose option: S5 Boot / Auto Login
Choose option: B2 Console Autologin
Select Finish, and reboot the Raspberry Pi.

source: https://raspberrypi.stackexchange.com/questions/40415/how-to-enable-auto-login

Control mpv with Sony TV remote

sudo apt install cec-utils socat

Run cec-client on the console and note that corresponds to different buttons on the remote when pressing it.

TRAFFIC: [          122391]     >> 01:44:30
DEBUG:   [          122391]     SetCurrentButton channel up (30) D:0ms cur:55a18669a0
DEBUG:   [          122391]     key pressed: channel up (30) current(ff) duration(0)
DEBUG:   [          122391]     Changed key channel up (30) D:0ms cur:ff
DEBUG:   [          122391]     key pressed: channel up (30, 0)
DEBUG:   [          122391]     CheckKeypressTimeout T:462.198
DEBUG:   [          122391]     Key channel up: idle (duration:0) (30) timeout:500ms (rel:500,rep:0,prs:1,rel:0)
DEBUG:   [          122391]     >> TV (0) -> Recorder 1 (1): user control pressed (44)
DEBUG:   [          122460]     CLinuxCECAdapterCommunication::Process - ioctl CEC_RECEIVE - rx_status=01 len=2 addr=01 opcode=45
TRAFFIC: [          122460]     >> 01:45
DEBUG:   [          122460]     key released: channel up (30) D:69ms
DEBUG:   [          122461]     >> TV (0) -> Recorder 1 (1): user control release (45)
DEBUG:   [          125195]     CLinuxCECAdapterCommunication::Process - ioctl CEC_RECEIVE - rx_status=01 len=3 addr=01 opcode=44
TRAFFIC: [          125195]     >> 01:44:31
DEBUG:   [          125195]     SetCurrentButton channel down (31) D:0ms cur:55a18669a0
DEBUG:   [          125195]     key pressed: channel down (31) current(ff) duration(0)
DEBUG:   [          125195]     Changed key channel down (31) D:0ms cur:ff
DEBUG:   [          125195]     key pressed: channel down (31, 0)
DEBUG:   [          125195]     CheckKeypressTimeout T:465.002
DEBUG:   [          125195]     Key channel down: idle (duration:0) (31) timeout:500ms (rel:500,rep:0,prs:1,rel:0)
DEBUG:   [          125196]     >> TV (0) -> Recorder 1 (1): user control pressed (44)
DEBUG:   [          125264]     CLinuxCECAdapterCommunication::Process - ioctl CEC_RECEIVE - rx_status=01 len=2 addr=01 opcode=45
TRAFFIC: [          125264]     >> 01:45
DEBUG:   [          125264]     key released: channel down (31) D:69ms
DEBUG:   [          125264]     >> TV (0) -> Recorder 1 (1): user control release (45)
~/bin/mpv-control.sh
#!/bin/bash

cec-client | while read line; do
    if [[ $line == *"01:44:30"* ]]; then
        echo '{"command": ["playlist-next"]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:31"* ]]; then
        echo '{"command": ["playlist-prev"]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:21"* ]]; then
        echo '{"command": ["set_property", "playlist-pos", 0]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:22"* ]]; then
        echo '{"command": ["set_property", "playlist-pos", 2]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:23"* ]]; then
        echo '{"command": ["set_property", "playlist-pos", 3]}' | socat - /tmp/mpv-socket
    # Since camera 4 is under maintenance
    # elif [[ $line == *"01:44:24"* ]]; then
    #     echo '{"command": ["set_property", "playlist-pos", 5]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:25"* ]]; then
        echo '{"command": ["set_property", "playlist-pos", 5]}' | socat - /tmp/mpv-socket
    elif [[ $line == *"01:44:26"* ]]; then
        echo '{"command": ["set_property", "playlist-pos", 7]}' | socat - /tmp/mpv-socket
    fi
done

Systemd user service to auto-start this shell script at login:

~/.config/systemd/user/camera-remote.service
[Unit]
Description=Camera Streams
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/home/vector/bin/mpv-control.sh
Restart=always
RestartSec=5

[Install]
WantedBy=default.target

Comments