How to re-create the image
Create the base image and boot
This steps gives you a SD card that can connect to your WiFi network, so that you can control it through SSH. This is necessary for the later steps. You might want to delete your network information at the end of the process.
- Download latest legacy RPI OS here. Select the latest release and then download the file which name ends with
-lite.xz
- Itās crucial to get the legacy version (based on Debian Buster) instead of the newer ones (based on Debian Bullseye), because the latter removed support for the Python camera bindings.
- Unzip it
- Flash onto SD:
dd if=nome-immagine.img of=/dev/sdX bs=4M status=progress oflag=sync
withsudo
- Add empty
ssh
file in theboot
partition - Add
config.txt
file in theboot
partition (if it exists already, delete it)- Content:
# Disable the rainbow splash screen
disable_splash=1
# No boot delay
boot_delay=0
# Disable the LED
dtparam=act_led_trigger=none
dtparam=act_led_activelow=on
# Enable camera
start_x=1 # essential, although a bit misleading
gpu_mem=224 # the memory allocated for the camera
disable_camera_led=1 # optional, if you don't want the led to glow
# Disable Bluetooth
dtoverlay=disable-bt
- Change
/etc/wpa_supplicant/wpa_supplicant.conf
onrootfs
with your local wifi data- Example content:
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=IT
network={
ssid="SSIDNAME"
psk="PASSWORD"
}
- Boot the Pi with this SD card.
- SSH into it. Assuming your routerās IP is something like 192.168.1.0 (mind the first three numbers and change them accordingly in the commands below):
- Find its IP with
nmap -p 22 192.168.1.0/24
. In the list that appears, one of the devices should be your Raspberry Pi (find it by the name or by exclusion). - Connect to it with SSH using the IP just discovered:
ssh pi@192.168.1.xxx
(password is āraspberryā)
- Find its IP with
Basic setup (libraries and configuration)
This step installs a few libraries required for the webcam to work.
- Switch from the
pi
user tozanzocam-bot
:- Create user with home directory:
sudo useradd -m zanzocam-bot
- Change password:
sudo passwd zanzocam-bot
- Make sudoer:
sudo usermod -aG sudo zanzocam-bot
- Allow passwordless
sudo
forzanzocam-bot
:sudo nano /etc/sudoers
- Add ad the end:zanzocam-bot ALL=(ALL:ALL) NOPASSWD:ALL
Allow
zanzocam-bot
to access the webcam:sudo usermod -aG video zanzocam-bot
- Leave the SSH connection and reconnect with
zanzocam-bot
- Lock
pi
:sudo passwd -l pi
- Kill all processes (shold be none):
sudo pkill -KILL -u pi
- Remove the user:
sudo deluser --remove-home pi
- Create user with home directory:
- Generate locales:
- Uncomment the IT locale in the proper file:
sudo perl -pi -e 's/# it_IT.UTF-8 UTF-8/it_IT.UTF-8 UTF-8/g' /etc/locale.gen
- Comment the default EN locale in the proper file:
sudo perl -pi -e 's/en_GB.UTF-8 UTF-8/# en_GB.UTF-8 UTF-8/g' /etc/locale.gen
- Generate locales:
sudo locale-gen it_IT.UTF-8
- Set locale:
sudo localectl set-locale LANG=it_IT.UTF-8
- Export
LANG
:export LANG="it_IT.UTF-8"
- Update locales:
sudo update-locale it_IT.UTF-8
- Uncomment the IT locale in the proper file:
Note: this procedure might raise some Perl errors, like:
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_ADDRESS = "fr_FR.UTF-8",
LC_NAME = "fr_FR.UTF-8",
LC_MONETARY = "fr_FR.UTF-8",
LC_PAPER = "fr_FR.UTF-8",
LC_IDENTIFICATION = "fr_FR.UTF-8",
LC_TELEPHONE = "fr_FR.UTF-8",
LC_MEASUREMENT = "fr_FR.UTF-8",
LC_NUMERIC = "fr_FR.UTF-8",
LANG = "en_GB.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
These are due to a directive on your local machine, in /etc/ssh/ssh_config
: SendEnv LANG LC_*
. Comment it out to avoid such errors from arising on your Pi.
Set timezone:
sudo timedatectl set-timezone Europe/Rome
- Create alias for
ll
in~/.bashrc
:nano ~/.bashrc
- Add at the end:
alias ll="ls -lah"
- Add at the end:
- Update the index:
sudo apt update
- Install utilities, graphics libraries, fonts:
sudo apt install -y git whois libopenjp2-7-dev libtiff-dev fonts-dejavu
.- Note: weāre installing the DejaVu font. If you want to change it, install the proper package at this stage and, at the end of the procedure, modify
constants.py
in/home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/
. This file constains a constant calledFONT_PATH
which you should point to your fontās path.
- Note: weāre installing the DejaVu font. If you want to change it, install the proper package at this stage and, at the end of the procedure, modify
- Setup cronjob to turn off HDMI at reboot:
sudo nano /etc/cron.d/no-hdmi
- Content:
# Disable the HDMI port (to save power)
@reboot /usr/bin/tvservice -o
- Disable the swap (swaps tends to āburnā SD cards very fast due to the very frequent writes. Turns out the Raspberry Pi has quite enough RAM to work without swap):
sudo dphys-swapfile swapoff
sudo dphys-swapfile uninstall
sudo update-rc.d dphys-swapfile remove
sudo apt purge -y dphys-swapfile
- Install the webcam module:
- Install pip3 and venv:
sudo apt install -y python3-pip python3-venv
- Create venv in the home:
cd ~ && python3 -m venv venv
- Activate venv:
source venv/bin/activate
- Install Zanzocam webcam into it:
pip install "zanzocam[deploy] @ git+https://github.com/zanzocam/zanzocam-core.git"
- Leave venv:
deactivate
- Install pip3 and venv:
Setup the autohotspot feature
This step makes the Pi able to generate its own WiFi network (SSID & password, see below) when no known WiFi network is detected.
Instructions found here
- Install
hostapd
anddnsmasq
sudo apt install -y hostapd dnsmasq
hostapd
anddnsmasq
run when the Raspberry is started, but they should only start if the home router is not found. So automatic startup needs to be disabled andhostapd
needs to be unmasked:sudo systemctl unmask hostapd
sudo systemctl disable hostapd
sudo systemctl disable dnsmasq
- Edit
hostapd
configuration file to create a network calledzanzocam-setup
and passwordwebcamdelrifugio
(on WiFi channel 8):sudo nano /etc/hostapd/hostapd.conf
- Content:
# 2.4GHz setup wifi 80211 b,g,n
interface=wlan0
driver=nl80211
ssid=zanzocam-setup
hw_mode=g
channel=8
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=webcamdelrifugio
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP TKIP
rsn_pairwise=CCMP
# 80211n
country_code=IT
ieee80211n=1
ieee80211d=1
- Edit
hostapd
defaultssudo nano /etc/default/hostapd
- Change
#DAEMON_CONF=""
intoDAEMON_CONF="/etc/hostapd/hostapd.conf"
- Check that
DAEMON_OPTS=""
is preceded by a#
, so that the line reads#DAEMON_OPTS=""
- Edit
dnsmasq
configuration:sudo nano /etc/dnsmasq.conf
- Add at the bottom:
#AutoHotspot Config
#stop DNSmasq from using resolv.conf
no-resolv
#Interface to use
interface=wlan0
bind-interfaces
dhcp-range=10.0.0.50,10.0.0.150,12h
- Modify the
interfaces
file:sudo nano /etc/network/interfaces
- Must be equal to:
# interfaces(5) file used by ifup(8) and ifdown(8)
# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d
- Stop
dhcpcd
from starting the wifi network, so that theautohotspot
script in the next step can work:sudo nano /etc/dhcpcd.conf
- Add at the bottom:
nohook wpa_supplicant
- Create a service which will run the autohotspot script when the Raspberry Pi starts up
sudo nano /etc/systemd/system/autohotspot.service
- Content:
[Unit]
Description=Automatically generates an hotspot when a valid ssid is not in range
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/autohotspot
[Install]
WantedBy=multi-user.target
- Enable the service:
sudo systemctl enable autohotspot.service
- Create the
autohotspot
script:sudo nano /usr/bin/autohotspot
- Content:
#!/bin/bash
#version 0.961-N/HS
#You may share this script on the condition a reference to RaspberryConnect.com
#must be included in copies or derivatives of this script.
#A script to switch between a wifi network and a non internet routed Hotspot
#Works at startup or with a seperate timer or manually without a reboot
#Other setup required find out more at
#http://www.raspberryconnect.com
wifidev="wlan0" #device name to use. Default is wlan0.
#use the command: iw dev ,to see wifi interface name
IFSdef=$IFS
cnt=0
#These four lines capture the wifi networks the RPi is setup to use
wpassid=$(awk '/ssid="/{ print $0 }' /etc/wpa_supplicant/wpa_supplicant.conf | awk -F'ssid=' '{ print $2 }' | sed 's/\r//g'| awk 'BEGIN{ORS=","} {print}' | sed 's/\"/''/g' | sed 's/,$//')
IFS=","
ssids=($wpassid)
IFS=$IFSdef #reset back to defaults
#Note:If you only want to check for certain SSIDs
#Remove the # in in front of ssids=('mySSID1'.... below and put a # infront of all four lines above
# separated by a space, eg ('mySSID1' 'mySSID2')
#ssids=('mySSID1' 'mySSID2' 'mySSID3')
#Enter the Routers Mac Addresses for hidden SSIDs, seperated by spaces ie
#( '11:22:33:44:55:66' 'aa:bb:cc:dd:ee:ff' )
mac=()
ssidsmac=("${ssids[@]}" "${mac[@]}") #combines ssid and MAC for checking
createAdHocNetwork()
{
echo "Creating Hotspot"
ip link set dev "$wifidev" down
ip a add 10.0.0.5/24 brd + dev "$wifidev"
ip link set dev "$wifidev" up
dhcpcd -k "$wifidev" >/dev/null 2>&1
systemctl start dnsmasq
systemctl start hostapd
}
KillHotspot()
{
echo "Shutting Down Hotspot"
ip link set dev "$wifidev" down
systemctl stop hostapd
systemctl stop dnsmasq
ip addr flush dev "$wifidev"
ip link set dev "$wifidev" up
dhcpcd -n "$wifidev" >/dev/null 2>&1
}
ChkWifiUp()
{
echo "Checking WiFi connection ok"
sleep 20 #give time for connection to be completed to router
if ! wpa_cli -i "$wifidev" status | grep 'ip_address' >/dev/null 2>&1
then #Failed to connect to wifi (check your wifi settings, password etc)
echo 'Wifi failed to connect, falling back to Hotspot.'
wpa_cli terminate "$wifidev" >/dev/null 2>&1
createAdHocNetwork
fi
}
chksys()
{
#After some system updates hostapd gets masked using Raspbian Buster, and above. This checks and fixes
#the issue and also checks dnsmasq is ok so the hotspot can be generated.
#Check Hostapd is unmasked and disabled
if systemctl -all list-unit-files hostapd.service | grep "hostapd.service masked" >/dev/null 2>&1 ;then
systemctl unmask hostapd.service >/dev/null 2>&1
fi
if systemctl -all list-unit-files hostapd.service | grep "hostapd.service enabled" >/dev/null 2>&1 ;then
systemctl disable hostapd.service >/dev/null 2>&1
systemctl stop hostapd >/dev/null 2>&1
fi
#Check dnsmasq is disabled
if systemctl -all list-unit-files dnsmasq.service | grep "dnsmasq.service masked" >/dev/null 2>&1 ;then
systemctl unmask dnsmasq >/dev/null 2>&1
fi
if systemctl -all list-unit-files dnsmasq.service | grep "dnsmasq.service enabled" >/dev/null 2>&1 ;then
systemctl disable dnsmasq >/dev/null 2>&1
systemctl stop dnsmasq >/dev/null 2>&1
fi
}
FindSSID()
{
#Check to see what SSID's and MAC addresses are in range
ssidChk=('NoSSid')
i=0; j=0
until [ $i -eq 1 ] #wait for wifi if busy, usb wifi is slower.
do
ssidreply=$((iw dev "$wifidev" scan ap-force | egrep "^BSS|SSID:") 2>&1) >/dev/null 2>&1
#echo "SSid's in range: " $ssidreply
printf '%s\n' "${ssidreply[@]}"
echo "Device Available Check try " $j
if (($j >= 10)); then #if busy 10 times goto hotspot
echo "Device busy or unavailable 10 times, going to Hotspot"
ssidreply=""
i=1
elif echo "$ssidreply" | grep "No such device (-19)" >/dev/null 2>&1; then
echo "No Device Reported, try " $j
NoDevice
elif echo "$ssidreply" | grep "Network is down (-100)" >/dev/null 2>&1 ; then
echo "Network Not available, trying again" $j
j=$((j + 1))
sleep 2
elif echo "$ssidreply" | grep "Read-only file system (-30)" >/dev/null 2>&1 ; then
echo "Temporary Read only file system, trying again"
j=$((j + 1))
sleep 2
elif echo "$ssidreply" | grep "Invalid exchange (-52)" >/dev/null 2>&1 ; then
echo "Temporary unavailable, trying again"
j=$((j + 1))
sleep 2
elif echo "$ssidreply" | grep -v "resource busy (-16)" >/dev/null 2>&1 ; then
echo "Device Available, checking SSid Results"
i=1
else #see if device not busy in 2 seconds
echo "Device unavailable checking again, try " $j
j=$((j + 1))
sleep 2
fi
done
for ssid in "${ssidsmac[@]}"
do
if (echo "$ssidreply" | grep -F -- "$ssid") >/dev/null 2>&1
then
#Valid SSid found, passing to script
echo "Valid SSID Detected, assesing Wifi status"
ssidChk=$ssid
return 0
else
#No Network found, NoSSid issued"
echo "No SSid found, assessing WiFi status"
ssidChk='NoSSid'
fi
done
}
NoDevice()
{
#if no wifi device,ie usb wifi removed, activate wifi so when it is
#reconnected wifi to a router will be available
echo "No wifi device connected"
wpa_supplicant -B -i "$wifidev" -c /etc/wpa_supplicant/wpa_supplicant.conf >/dev/null 2>&1
exit 1
}
chksys
FindSSID
#Create Hotspot or connect to valid wifi networks
if [ "$ssidChk" != "NoSSid" ]
then
if systemctl status hostapd | grep "(running)" >/dev/null 2>&1
then #hotspot running and ssid in range
KillHotspot
echo "Hotspot Deactivated, Bringing Wifi Up"
wpa_supplicant -B -i "$wifidev" -c /etc/wpa_supplicant/wpa_supplicant.conf >/dev/null 2>&1
ChkWifiUp
elif { wpa_cli -i "$wifidev" status | grep 'ip_address'; } >/dev/null 2>&1
then #Already connected
echo "Wifi already connected to a network"
else #ssid exists and no hotspot running connect to wifi network
echo "Connecting to the WiFi Network"
wpa_supplicant -B -i "$wifidev" -c /etc/wpa_supplicant/wpa_supplicant.conf >/dev/null 2>&1
ChkWifiUp
fi
else #ssid or MAC address not in range
if systemctl status hostapd | grep "(running)" >/dev/null 2>&1
then
echo "Hostspot already active"
elif { wpa_cli status | grep "$wifidev"; } >/dev/null 2>&1
then
echo "Cleaning wifi files and Activating Hotspot"
wpa_cli terminate >/dev/null 2>&1
ip addr flush "$wifidev"
ip link set dev "$wifidev" down
rm -r /var/run/wpa_supplicant >/dev/null 2>&1
createAdHocNetwork
else #"No SSID, activating Hotspot"
createAdHocNetwork
fi
fi
- Make it executable:
sudo chmod +x /usr/bin/autohotspot
- Some boards actually come with rfkilled WiFi interface and wlan0 down. Therefore:
sudo nano /etc/crontab
(this file can run sudo commands)- Append at the end of the file:
@reboot root rfkill unblock 0 && ifconfig wlan0 up && /usr/bin/autohotspot
- To test:
sudo reboot
.- After reboot it should manage to connect to your home network.
ssh
into it.- Remove your network details from
/etc/wpa_supplocant/wpa_supplicant.conf
sudo reboot
.- You should see a wifi network called āzanzocam-setupā that you can connect to.
- Connect and
ssh
into the Raspberry again (IP is10.0.0.5
)
Prepare setup server
Once the Pi can generate its own network, we make it able to receive HTTP requests by setting up a web server: Nginx.
Instructions here for Nginx and here for Flask.
- Install Nginx:
- Install it:
sudo apt install -y nginx libssl-dev libffi-dev build-essential apache2-utils
- Start it:
sudo /etc/init.d/nginx start
- Create a
.htpasswd
file in the home:htpasswd -c /home/zanzocam-bot/.htpasswd zanzocam-user
(the image password here islampone
) - Create a systemdunit file:
sudo nano /etc/systemd/system/zanzocam-web-ui.service
. Content:
- Install it:
[Unit]
Description=uWSGI instance to serve the ZANZOCAM web UI
After=network.target
[Service]
User=zanzocam-bot
Group=www-data
WorkingDirectory=/home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/web_ui
Environment="PATH=/home/zanzocam-bot/zanzocam/venv/bin"
ExecStart=/home/zanzocam-bot/venv/bin/uwsgi --ini web-ui.ini
[Install]
WantedBy=multi-user.target
- Enable the service:
sudo systemctl enable zanzocam-web-ui
- Start the service:
sudo systemctl start zanzocam-web-ui
- Create Nginx configuration:
sudo nano /etc/nginx/sites-available/zanzocam-web-ui
- Content:
server {
listen 80;
auth_basic "Login";
auth_basic_user_file /home/zanzocam-bot/.htpasswd;
location / {
include uwsgi_params;
uwsgi_pass unix:/home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/web_ui/web-ui.sock;
uwsgi_read_timeout 600;
}
}
- Enable the new config:
sudo ln -s /etc/nginx/sites-available/zanzocam-web-ui /etc/nginx/sites-enabled
- Disable the default config:
sudo rm /etc/nginx/sites-enabled/default
- Check for errors with
sudo nginx -t
- Restart Nginx:
sudo systemctl restart nginx
Create the image
- Delete the wpa_supplicant configuration file (unless you want to preserve WiFi info):
sudo rm /etc/wpa_supplicant/wpa_supplicant.conf
- If you tested the ZanzoCam, delete the content of the
data
folder:rm /home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/data
- Do not delete the
overlays
folder, but you might want to delete its content:rm /home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/data/overlays
- Do not delete the
- Insert the SD card into the computer and mount it
- Resize
rootfs
to be approximately 3.5G (can be done with GParted). - Clone the resized content of the SD into an image:
dd if=/dev/sdX of=zanzocam.img bs=4M status=progress oflag=sync
withsudo
- Install resizing tool:
sudo apt install -y qemu-utils
- Shrink it to a smaller size (4GB should be safe):
sudo qemu-img resize --shrink zanzocam.img 3.8G
- If youāre building an image to be used with the Raspberry Imager, use a multiple of 512:
4013109760B
instead of3.8G
- If youāre building an image to be used with the Raspberry Imager, use a multiple of 512:
The image is ready to be flashed on any SD card larger than 4GB. Note that the filesystem will not expand.
If you want to upload/send it, compress it first with tar -czvf zanzocam.tar.gz zanzocam.img
or zip zanzocam.zip zanzocam.img
Working with a modem
A few ZanzoCams were tested on a Teltonika TRM240 modem. In order to make this specific modem work on the ZanzoCam, you should follow these steps.
- Connect your Raspberry Pi to a monitor and a keyboard. In this case, we used a Raspberry Pi 3B due to the presence of enough USB ports to connect power, the modem, and a keyboard, but we also managed to use an externally powered USB switch on a Raspberry Pi Zero W. Remember that you can setup this system on a different Raspberry Pi and then move the SD card on the board you will use as a camera.
- Flash the latest ZanzoCam version on your SD card and boot it up.
- Install Modem Manager and Network Manager on the Pi:
sudo apt install modemmanager network-manager
- Disable dhcpcd:
sudo systemctl disable --now dhcpcd
- Disable the autohotspot:
sudo systemctl disable --not autohotspot
(this will make the hotspot unavailable) - Type
sudo nano /etc/dnsmasq.conf
and comment out the autohostpot modifications at the bottom of the file. - Reboot (Modem Manager will not detect the modem until the first reboot):
sudo reboot
- Make sure the modem is detected:
mmcli -L
should return one line, andmmcli -m 0
should return a detailed description of the modem. In the Status section, you should be able to seestate: registered
(or evenstate: connected
),power state: on
, and the signal quality. If you see anywhere ano-sim-detected
or similar messages, then double-check that your modem is powered up, the SIM is inserted correctly, and works properly when put in a phone. If it has a PIN, remove it before retrying (or check yourself how to deal with it). - Under System, note down the value of
primary port
. In the case of the Teltonika TRM240, this value should becdc-wdm0
. - Tell Network Manager to use the modem:
nmcli c add type gsm ifname cdc-wdm0 con-name <a recognizable name for this connection> apn <your operator's APN>
- You can check that the connection was created with
nmcli c
- Disable the WiFi:
nmcli r wifi off
if you wish: itās not necessary, but itās useful to see if the modem is actually working. You can check the status withnmcli r
. - You should now have Internet connection through the router.
Keep in mind that the router takes usually several second to get any uplink, so be patient after a reboot!
Headless operation
The ZanzoCam setup procedure assumes you will be able to access the green UI panel to perform the initial test shoot. If this is not the case, you can still configure the ZanzoCam to start spontaneously at the first boot without any manual intervention.
With either SSH access or the SD inserted in your conputer:
- Open
/etc/cron.d/zanzocam
and write:# ZANZOCAM - shoot picture @reboot zanzocam-bot sleep 60 && /home/zanzocam-bot/venv/bin/z-webcam
- Open
/home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/data/configuration.json
and write:- for FTP:
{ "server": { "protocol": "FTP", "hostname": "<your hostname>", "tls": true or false, "username": "<username>", "password": "<password>", "max_photos": integer number of photos or 0 to keep them all with timestamps } }
- for HTTP:
{ "server": { "protocol": "HTTP", "url": "<your server's complete url>", "username": "<username>", "password": "<password>", "max_photos": integer number of photos or 0 to keep them all with timestamps } }
This should be sufficient for the ZanzoCam to start autoomously at the first power on. You might want to enable sending the logs to the server: in this case, simply add a file called
send-logs.flag
under/home/zanzocam-bot/venv/lib/python3.7/site-packages/zanzocam/data/
and writeYES
in it.
- for FTP:
Working with a VPN
Assuming that you have a working VPN infrastructure, you can easily install a client on the ZanzoCam to be able to reach it via SSH remotely.
- Install OpenVPN:
sudo apt install openvpn
- Make the client start at boot:
sudo crontab -e
(sudo
is important here:crontab -e
would open a different configuration file). Add at the end:@reboot sleep 90 && /usr/sbin/openvpn /etc/openvpn/client/vpn_client.ovpn &
- Add your
.ovpn
client file asvpn_client.ovpn
in/etc/openvpn/client/
The VPN should need no extra configuration.