Download this tutorial as a pdf

Overview

  • Set up raspberry pi (<pi><number>) and get it connected to wifi hotspot
  • Connect to device using ssh
  • Update the system
  • Manage ssh connection to the device using ssh keys
  • Install editing, streaming, and networking tools (vim, libcamera, motion, tailscale)
  • Set up motion for streaming over internet
  • Get the pi display its ip address

1A. Get connected to wifi

  1. Set up the connection to wifi hotspot when burning the image and enable ssh
  2. Start the pi and check on hotspot that more devices are connected
  3. Find the IP address of the raspberry pi (arp -a or nmap -sn <router-ip>/24) alternatively look into the router interface to find the ip addresses this is a painpoint

1B. Get connected to Raspberry pi using ethernet cable

  1. Start the Pi
  2. Connect with Ethernet cable
  3. Enable sharing network on WiFi
  4. Run arp -a in powershell to see what devices are connected and get
  5. ‘ssh @

In case you want to know how to setup multiple wifi connections for the raspberry pi, this is explained in section 15

2. Access the raspberry pi device

  1. ssh <name>@<ip-address>
  2. Input your password

3. Update the system (working)

Raspberry Pi Zero

sudo apt update && \
sudo apt full-upgrade -y && \
sudo reboot 

Raspberry Pi 4 and higher

sudo apt update && \
sudo apt full-upgrade -y && \
sudo rpi-eeprom-update -a && \
sudo reboot # for changes to take place

4. Setup access through ssh keys (working)

NOTE: Log out of the remote raspberry pi and work on your computer command line

Generate the keys on your machine

NOTE: This is needed just for the super safe connection using the public keys not password, can be skipped.

ssh-keygen -t rsa -b 4096 -f <pc>-for-<pi><number>
cp  <pc>-for-<pi><number>* .ssh/
ssh-copy-id -i <pc>-for-<pi><number>.pub <pi-ip>

Create/Add connection to this pi on given network

NOTE: This is needed just for the super safe connection using the public keys not password, can be skipped. Open /.ssh/config

sudo vim ~/.ssh/config

Edit the file

# On S75 hotspot network
Host <pi><number>_s75
        HostName <pi-ip>
        User <user>
        IdentityFile ~/.ssh/<pc>-for-<pi><number>

Test connection

ssh <pi-ip>

5. Install tools (working)

Tools:

  1. noefetch to get info about the system
  2. vim for editing
  3. motion for streaming video
  4. libcamera to make motion work with raspberry pi camera

Install

sudo apt install neofetch -y && \
sudo apt install vim -y && \
sudo apt install motion -y && \
sudo apt install libcamera-tools -y && \
sudo apt install libcamera-dev -y && \
sudo apt install libcamera-v4l2 -y && \
sudo sed -i 's/stream_localhost on/stream_localhost off/g' /etc/motion/motion.conf && \
echo "done"

Verify it runs

Test by running

sudo libcamerify motion -n

Access in browser at an address: <pi-ip>:8081

Shut down <Ctrl>+<C>

Start so that ssh logout will keep it running

sudo nohup libcamerify motion -n &

After pressing <Enter> you can work in the shell

NOTES:

  • To close the stream get back to background process fg and then <Ctrl>+<C>
  • If it is not in the background process of this shell sudo pkill motion to terminate the process

Run it as a service (works)

  1. Change the ownership of the directory for logs
sudo chown -R motion:motion /var/log/motion
  1. Adjust the service file
sudo mkdir -p /etc/systemd/system/motion.service.d
sudo tee /etc/systemd/system/motion.service.d/override.conf >/dev/null <<'EOF'
[Service]
# Keep Type/User from the main unit; no need to repeat them here.
# Replace the ExecStart from the main unit:
ExecStart=
ExecStart=/usr/bin/libcamerify /usr/bin/motion -n -c /etc/motion/motion.conf

# Optional hardening/niceties:
# Ensure files created by the service aren't world-readable
UMask=007
# Have systemd create /var/log/motion owned by the service user if supported
LogsDirectory=motion
EOF
  1. Reload the daemon
sudo systemctl daemon-reload
  1. Check the file really contains the changes
systemctl cat motion
  1. Start and check
sudo systemctl start motion

test if it is streaming in the browser http://<pi-ip>:8081

  1. Check if it restarts after reboot
sudo reboot

Should run when rebooted

Saving files when running as a service

In motion.conf, set:

target_dir /var/lib/motion/snapshots

Then on the command line:

sudo mkdir -p /var/lib/motion/snapshots
sudo chown -R motion:motion /var/lib/motion
sudo chmod 750 /var/lib/motion/snapshots
sudo usermod -aG motion <user> # so that the user can work with the files 
sudo systemctl restart motion

Create a directory in your home and bind-mount it:

mkdir -p /home/vld/shared/snapshots

Mount bind the system dir to your home path

sudo mount --bind /var/lib/motion/snapshots /home/vld/shared/snapshots

Now you’ll see the same files in both places. To make it permanent, add a line to /etc/fstab: NOT TESTED

/var/lib/motion/snapshots   /home/vld/shared/snapshots   none   bind   0   0

Add ACL entry so vld can traverse and read TESTED

sudo setfacl -m u:vld:rwx /var/lib/motion/snapshots

Retry

cd /home/vld/shared/snapshots
ls -al

6. Install tailscale for remote access (working)

Tailscale is a networking tool (like vpn) to access your devices over internet link

Install using commands and activate:

curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.noarmor.gpg | \
sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/debian/bookworm.tailscale-keyring.list | \
sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt-get update -y 
sudo apt-get install tailscale -y
sudo tailscale up

Resource

Detailed instruction how to install tailscale on Debian bookworm (on which the raspberry pi OS is based): link

7. Verify the streaming works over tailscale (working)

In browser: <tailscale-pi-name>:8081
In case it is not working:

  • first reboot the raspberry pi, check that you can connect (ip address can change) and try again sudo reboot
  • check if the camera is recognized by running rpicam-hello --list-cameras should list several lines (see section Cameras at the bottom of this page)

8. Put the link online to website (working)

NOTE: works for hugo static website Create a file webcam.md in the content/ folder and copy inside this text with updated link and instructions:

[webcam link](http://<tilscale-pi-name>:8081)  
Activate the stream from camera over ssh:  
`nohup sudo libcamerify motion -n &`

9. Tweaks (working)

Set alias for streaming

sudo echo 'alias start-stream="sudo nohup libcamerify motion -n &" # alias to start streaming' >> ~/.bashrc
source ~/.bashrc

Now typing start-stream starts the stream in the background

Rotate the camera image

Adding rotate <number-in-degrees> into motion.conf rotates the image clockwise

# if the camera is upside down
sudo echo "rotate 180" >> /etc/motion/motion.conf

Restart the stream after the modification for it to take place

10. Motion config files (working)

The default config file for motion is in /etc/motion/motion.conf.
To run a different configuration:

  1. Make a copy of this file
  2. Adjust it
  3. Call it using sudo libcamerify motion -n -c <path-to-config-file>

Example config files

  • Snapshots: Take a 2592x1944pxs image every 10 seconds saved in /var/lib/motion/snapshots/ Download

Parameters to set

  • width 1296
  • height 972
  • to be filled in…

11. Installing samba for shared folder (working)

For easier transfer of files from the raspberry pi to the pc you can mount a shared folder on the pi as a network disk. Samba project link.
Instructions for raspberry pi setup: link.

Install samba from repository

sudo apt update
sudo apt install samba samba-common-bin smbclient cifs-utils -y

Create a shared folder with the proper rights

cd ~
mkdir shared
chmod 0740 shared

Create the user who will be connecting

Think about user name and a password and write it on a piece of paper or use the same as you use to access rapsberry pi

sudo smbpasswd -a <username>

Configure samba

Open the config file

sudo vim /etc/samba/smb.conf

Add following at the end

[<share-name>] 
    path = /home/<username>/shared
    read only = no
    public = yes
    writable = yes

NOTE: The text inside the brackets is how the folder will be called when you are connecting from outside (mapping the network disk)

Check it is working

sudo systemctl status smbd

NOTE: in case you use sudo systemctl status samba gives a condition failed message which does not mean the share is not working

Conect to the folder from a Win11 machine

  1. Right click This Computer
  2. Select the letter for a disk
  3. Put \\<pi-ip>\<share-name>, in case it does not work, try \\<pi-ip>\<user-name>\<share-name>
  4. Fill in the user name and a password (for samba)

NOTE: instead of <pi-ip> also the <pi-name> can be used but initially, the <pi-ip> is safer

12. Use USB cable for ssh connection from Win11 (not working)

in /boot/firmware/config.txt

dtoverlay=dwc2

in /boot/firmware/cmdline.txt

rootwait modules-load=dwc,g_ether

reboot and connect

check if the usb0 is up

ip a show usb0

if it is dowb bring it up

sudo ip link set usb0 up

in etc/dhcpcd.conf should set it automatically

interface usb0
static ip_address=192.168.7.2/24
nolink

sudo reboot

Is not recognized from windows side, need insatllaiton of rndis drivers, downloaded from here emm2a post link but not tested it

13. Moving files from raspberry pi (working)

First, you may want to compress files into a reasonably sized zip chunks, then use the samba folder mapped as a network drive or one of the following:
NOTE:Did not benchmark, but zipping on Raspberry Pi Zero takes ages, so I am going without it

zip -r -s 50m <archive-name>.zip /path/to/folder

To unzip the files:

unzip <archive-name>.zip

http server (working)

  1. Start the http server in the folder containg the files on the raspberry pi
sudo nohup python3 -m http.server 8000 &
  1. Get the files from the server
wget -c http://<pi-ip>:8000/<file-name>
  1. Kill the server from the pi
ps aux | grep http.server
kill <PID>

is supposed to be slower

rsync utility (not tested yet)

On your machine navigate to the folder where you want the file to land

rsync -avz --progress <user>@<pi-ip>:/path/to/<file-name> .

is supposed to be the fastest, not tested yet

14. Renaming files from the snapshot

NOTE: Fixed, is not needed for 60sec snapshots
The files from snapshots are time coded such that 18:05 is coded as 185, while 18:15 is coded as 1815.
To fix it use following rename command:

rename -n 's/(_\d{4}-\d{2}-\d{2}_)(\d{2})(\d{1}-)(\d+\.jpg)/$1${2}0${3}$4/' snapshot*.jpg
  • recognized pattern enclosed in ( .. ) gets saved into a variable
  • regex testing site
  • needs to be done in wsl, not from GitBash

15. Seting up wifi connections using nmcli (working)

Better to use sudo for all of the nmcli commands

See what wifi’s are available

  1. Rescan the networks first
sudo nmcli device wifi rescan
  1. See what wifi connections are avalialbe
sudo nmcli device wifi list
  1. See what all connections are possible
sudo nmcli connection show

In the device column, check what device is used to connect to wifi (supposing you are actively connected to one)

Add a new wifi connection with a password

  1. Add a new connection
sudo nmcli connection add type wifi ifname wlan0 con-name MyWiFi ssid "MyWiFi"

ifname parameter is what you see under the device column in previous point 2. Add the password

sudo nmcli connection modify MyWiFi wifi-sec.key-mgmt wpa-psk wifi-sec.psk "mypassword123"
  1. Enable auto connect
sudo nmcli connection modify MyWiFi connection.autoconnect yes
  1. Set the priority for the different networks the higher connects first
sudo nmcli connection modify HomeWiFi connection.autoconnect-priority 10
  1. Verify the priorities
nmcli -f NAME,AUTOCONNECT-PRIORITY connection show

16. SetUp Raspberry Pi own hotspot

screenshot-from-docs

link

17. Let Raspberry Pi display its own IP address

Getting the 16x2 display work

  1. Using the Led display with the i2c converter needs just four wires:
  2. Set up that I2C is working in raspi-config “interfacing opitons -> I2C”
sudo raspi-config #navigate to 
  1. Install drivers(?)
sudo apt-get install i2c-tools -y
sudo apt install wireless-tools -y
  1. Check it recoginzes the device
i2cdetect -y 1

give a table with dashes and a number, that is the number of the interface, 27 for me

  1. Install the virtual environments for python
sudo apt install python3-venv -y
  1. Make specific environment just for this, activate and install packages
python3 -m venv lcd_env
source lcd_env/bin/activate
pip install RPLCD smbus2 netifaces
  1. Check a demo script is running
from RPLCD.i2c import CharLCD

# Adjust the address if necessary (0x27 or 0x3F are common)
lcd = CharLCD('PCF8574', 0x27)

line1 = input("Enter text for line 1: ")
line2 = input("Enter text for line 2: ")

lcd.clear()
lcd.clear()
lcd.cursor_pos = (0, 0)  # row 0, column 0
lcd.write_string(line1[:16])
lcd.cursor_pos = (1, 0)  # row 1, column 0
lcd.write_string(line2[:16])

print("Text displayed. Press Ctrl+C to exit.")

try:
    while True:
        pass
except KeyboardInterrupt:
    lcd.clear()
    print("\nLCD cleared.")
python lcd_01.py
  1. Make a script which displays the wifi/eth network pi is connected to and displays ip address
from RPLCD.i2c import CharLCD
import time
import netifaces
import subprocess

# --- LCD setup ---
lcd = CharLCD('PCF8574', 0x27)  # change to 0x3F if needed

def get_ip_address(iface):
    """Return IP address for given interface, or None if not connected."""
    try:
        return netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']
    except (KeyError, ValueError):
        return None

def get_wifi_ssid():
    """Return connected Wi-Fi SSID using 'iwgetid' command."""
    try:
        ssid = subprocess.check_output(
            ["iwgetid", "-r"], stderr=subprocess.DEVNULL
        ).decode().strip()
        return ssid if ssid else None
    except subprocess.CalledProcessError:
        return None

def get_active_connections():
    """Return dict of {display_name: ip_address} for wlan0/eth0 if active."""
    result = {}

    # Wi-Fi
    wifi_ip = get_ip_address("wlan0")
    wifi_ssid = get_wifi_ssid()
    if wifi_ip and wifi_ssid:
        result[wifi_ssid] = wifi_ip
    elif wifi_ip:
        result["wlan0"] = wifi_ip

    # Ethernet
    eth_ip = get_ip_address("eth0")
    if eth_ip:
        result["eth0"] = eth_ip

    return result

def display(interface, ip):
    """Show interface name/SSID and IP on LCD."""
    lcd.clear()
    lcd.cursor_pos = (0, 0)
    lcd.write_string(interface[:16].center(16))  # Centered text
    lcd.cursor_pos = (1, 0)
    lcd.write_string(ip[:16])

try:
    print("Starting LCD network monitor with SSID display...")
    while True:
        interfaces = get_active_connections()

        if not interfaces:
            lcd.clear()
            lcd.cursor_pos = (0, 0)
            lcd.write_string("No Network".center(16))
            lcd.cursor_pos = (1, 0)
            lcd.write_string("Connected".center(16))
            time.sleep(5)
            continue

        # Alternate display every 10 seconds
        for iface, ip in interfaces.items():
            display(iface, ip)
            time.sleep(10)

except KeyboardInterrupt:
    lcd.clear()
    print("\nLCD cleared and program stopped.")
  1. Make a script which clears the lcd at the end lcd_clear.py
from RPLCD.i2c import CharLCD
lcd = CharLCD('PCF8574', 0x27)  # adjust address
lcd.clear()
try:
    lcd.backlight_enabled = False  # optional
except Exception:
    pass
  1. Set it running as a service
(lcd_env) vld@pi4:~/Documents/lcd $ sudo cat /etc/systemd/system/lcd_network.service
[Unit]
Description=LCD Network Display
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/home/vld/lcd_env/bin/python /home/vld/Documents/lcd/lcd_networks_ssid.py
ExecStop=/home/vld/lcd_env/bin/python /home/vld/Documents/lcd/lcd_clear.py
KillSignal=SIGINT
WorkingDirectory=/home/vld/Documents/lcd
StandardOutput=journal
StandardError=journal
Restart=always
User=vld

[Install]
WantedBy=multi-user.target
  1. Reload deamons see if it starts, stops
sudo systemctl daemon-reload
sudo systemctl enable lcd_network.service
sudo systemctl start lcd_network.service
sudo systemctl status lcd_network.service
sudo systemctl stop lcd_network.service
journalctl -u lcd_network.service -f

17. Using GPIO pins (in progress)

gpiozero library enables you to use the general purpose input output pins

Use cases

  1. Switch on/off a lamp, pump device connected to mains
  2. Indicate by LEDs if the raspberry pi is working/online/streaming etc
  3. Take measurements and do action

Resources

link to the projects documentation online
link to the versions of downloadable documentation
Download the current pdf docs version

System

The above works for following systems

Camera(s)

Raspberry Pi camera V1.3

Raspberry Pi Zero W

Firmware

vld@pi0D:~ $ vcgencmd version
Apr 30 2025 13:35:18
Copyright (c) 2012 Broadcom
version 5560078dcc8591a00f57b9068d13e5544aeef3aa (clean) (release) (start)

OS

vld@pi0D:~ $ neofetch --off --color_blocks off
ld@pi0D
--------
OS: Raspbian GNU/Linux 12 (bookworm) armv6l
Host: Raspberry Pi Zero W Rev 1.1
Kernel: 6.12.34+rpt-rpi-v6
Uptime: 2 hours, 33 mins
Packages: 1595 (dpkg)
Shell: bash 5.2.15
Terminal: /dev/pts/2
CPU: BCM2835 (1) @ 1.000GHz
Memory: 130MiB / 427MiB

Camera

vld@pi0D:~ $ rpicam-hello --list-cameras
Available cameras
-----------------
0 : ov5647 [2592x1944 10-bit GBRG] (/base/soc/i2c0mux/i2c@1/ov5647@36)
    Modes: 'SGBRG10_CSI2P' : 640x480 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             1296x972 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             1920x1080 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             2592x1944 [30.00 fps - (65535, 65535)/65535x65535 crop]

Raspberry Pi 4

Firmware

vld@pi4:~ $ vcgencmd version
Aug 30 2024 19:17:39
Copyright (c) 2012 Broadcom
version 2808975b80149bbfe86844655fe45c7de66fc078 (clean) (release) (start)

OS

vld@pi4:~ $ neofetch --off --color_blocks off
vld@pi4
-------
OS: Debian GNU/Linux 12 (bookworm) aarch64
Host: Raspberry Pi 4 Model B Rev 1.4
Kernel: 6.6.51+rpt-rpi-v8
Uptime: 2 hours, 46 mins
Packages: 1610 (dpkg)
Shell: bash 5.2.15
Terminal: /dev/pts/2
CPU: (4) @ 1.800GHz
Memory: 405MiB / 7809MiB

Camera

vld@pi4:~ $ rpicam-hello --list-cameras
Available cameras
-----------------
0 : ov5647 [2592x1944 10-bit GBRG] (/base/soc/i2c0mux/i2c@1/ov5647@36)
    Modes: 'SGBRG10_CSI2P' : 640x480 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             1296x972 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             1920x1080 [30.00 fps - (65535, 65535)/65535x65535 crop]
                             2592x1944 [30.00 fps - (65535, 65535)/65535x65535 crop]