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
- Set up the connection to wifi hotspot when burning the image and enable ssh
- Start the pi and check on hotspot that more devices are connected
- Find the IP address of the raspberry pi (
arp -aornmap -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
- Start the Pi
- Connect with Ethernet cable
- Enable sharing network on WiFi
- Run
arp -ain powershell to see what devices are connected and get
- ‘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
ssh <name>@<ip-address>- 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:
- noefetch to get info about the system
- vim for editing
- motion for streaming video
- 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
fgand then<Ctrl>+<C> - If it is not in the background process of this shell
sudo pkill motionto terminate the process
Run it as a service (works)
- Change the ownership of the directory for logs
sudo chown -R motion:motion /var/log/motion
- 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
- Reload the daemon
sudo systemctl daemon-reload
- Check the file really contains the changes
systemctl cat motion
- Start and check
sudo systemctl start motion
test if it is streaming in the browser http://<pi-ip>:8081
- 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-camerasshould 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:
- Make a copy of this file
- Adjust it
- 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 1296height 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
- Right click
This Computer - Select the letter for a disk
- Put
\\<pi-ip>\<share-name>, in case it does not work, try\\<pi-ip>\<user-name>\<share-name> - 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 Ubuntu 24.04 (Win 11 too complicated)
For this to work you need to be able to connect to the raspberry pi over ssh or take out the card and change following files
Change the config and cmdline files
in /boot/firmware/config.txt
[all]
dtoverlay=dwc2
in /boot/firmware/cmdline.txt add this after what is already written there
rootwait modules-load=dwc,g_ether
Reboot and connect via ssh
On the raspberry pi check if the usb0 is up
ip a show usb0
If it is down bring it up
sudo ip link set usb0 up
When you check with ip a show.. would still be down as it is not connected yet through the cable
Set up the ip addresses
Connect the pi with the ubuntu pc with a cable
In Raspberry Pi:
sudo ip addr add 192.168.7.2/24 dev usb0
On Ubuntu: check for the connection “usb0” and set it up
ip a
In the output from the terminal search for: usb0: <BROADCAST, MULTICAST>... could also have a different name(?)
Bring the connection up
sudo ip link set usb0 up
Check connection
Ssh into it
ssh <user>@192.168.7.2
Set it for automatic connection using network manager
On Pi:
sudo vim /etc/systemd/network/usb0.network
Enter following in the file
[Match]
Name=usb0
[Network]
Address=192.168.7.2/24
LinkLocalAddressing=yes
Enable and restart service on the Pi
sudo systemctl enable systemd-networkd
sudo systemctl restart systemd-networkd
On Ubunutu:
nmcli con add type ethernet ifname usb0 con-name "pi0B-USB" ipv4.method manual ipv4.addresses 192.168.7.1/24
nmcli con up "pi0B-USB"
So this works
13. Moving files from raspberry pi (working)
Check how much of disk and/or directory is occupied
df # for whole device
du -sh dir # for directory
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)
- Start the http server in the folder containg the files on the raspberry pi
sudo nohup python3 -m http.server 8000 &
- Get the files from the server
wget -c http://<pi-ip>:8000/<file-name>
- 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 To make a dry run of copy and remove the data from the host:
time rsync -avzn --remove-source-files --progress [email protected]:/var/lib/motion/snapshots/ /home/vld/Videos/Gabik/
To run it for real, remove the n from -azvn
Test of 50GB transfer of jpg files from pi camera over USB. Time:
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
- Rescan the networks first
sudo nmcli device wifi rescan
- See what wifi connections are avalialbe
sudo nmcli device wifi list
- 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
- 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"
- Enable auto connect
sudo nmcli connection modify MyWiFi connection.autoconnect yes
- Set the priority for the different networks the higher connects first
sudo nmcli connection modify HomeWiFi connection.autoconnect-priority 10
- Verify the priorities
nmcli -f NAME,AUTOCONNECT-PRIORITY connection show
16. SetUp Raspberry Pi own hotspot
17. Let Raspberry Pi display its own IP address
Getting the 16x2 display work
- Using the Led display with the i2c converter needs just four wires:
- Set up that I2C is working in raspi-config “interfacing opitons -> I2C”
sudo raspi-config #navigate to
- Install drivers(?)
sudo apt-get install i2c-tools -y
sudo apt install wireless-tools -y
- 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
- Install the virtual environments for python
sudo apt install python3-venv -y
- 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
- 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
- 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.")
- 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
- 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
- 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
- Switch on/off a lamp, pump device connected to mains
- Indicate by LEDs if the raspberry pi is working/online/streaming etc
- 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]