A set of tools/scripts to automate the taking and creation of timelapse videos and videos with a Raspberry Pi
Important
In November 2023, I posted an update regards the performance of the PiJuice, even when using PiSupply's own batteries.
However, in the last month (November 2024) I've deployed one further camera that is not using a third party modem board Waveshare SIM7600-H 4G HAT, but instead has reverted back to a USB thumb modem. Since swapping to this modem, the PiJuice board has behaved perfectly with no missed wakeups or random restarts.
In addition the ability to purchase PiSupply's PiJuice batteries seems to have returned (even if stock has not).
TL;DR - as long as not combined with either a LiFePO4 battery chemistry or the Waveshare hat above, the PiJuice seems to perform well.
Raspberry Pi OS Lite (32 bit - Pi Zero W) Debian version 11 (bullseye) - https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2022-09-26/2022-09-22-raspios-bullseye-armhf-lite.img.xz
Raspberry Pi OS Lite (64 bit - Pi Zero 2 W Debian version 11 - https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2022-01-28/2022-01-28-raspios-bullseye-arm64-lite.zip
Burn using Pi Imager. Give a default name, but you can change this inthe install script below.
Set username and password, and authentication methods as desired.
Mount (reinsert) SD card
cp ~/wpa_supplicant.conf /Volumes/bootfs
diskutil unmount /Volumes/bootfs
If updating the wpa_supplicant file, and you have network access:
scp ~/wpa_supplicant.conf pi@[pi name]:~
And then on the pi:
sudo cp ~/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf
sudo reboot
Bookworm uses NetorkManager rather than WPA Supplicant.
Ensure you have local wifi network access when you burn the image.
Individual network addition:
sudo nmcli dev wifi connect <wifi-ssid> password "<network-password>"
scp ~/nmcli-connect.sh pi@[pi name]:~
And then on the pi:
chmod u+x ~/nmcli-connect.sh
sudo ./nmcli-connect.sh
Note - this will only connect to WIFI networks that are present, so ensure modem stays with camera.
Turn on and find the pi Pi Zero W 2:
arp -a | grep e4:5f
raspberrypi.lan (192.168.86.37) at e4:5f:1:5a:6e:b3 on en0 ifscope [ethernet]
Pi Zero W:
arp -a | grep b8:27
raspberrypi.lan (192.168.86.32) at b8:27:eb:94:ac:b1 on en0 ifscope [ethernet]
sudo apt-get install byobu -y
byobu-enable
byobu
bash <(curl -fsSL "https://github.com/venari/timelapse/raw/main/install.sh?$RANDOM")
ssh -o ConnectTimeout=60 -o ConnectionAttempts=30 pi@sediment-pi-zero-w-v1-a 'tail -f -n 100 logs/timelapse.log'
Connect to intermitently connected Pi and trigger reboot:
ssh -o ConnectTimeout=60 -o ConnectionAttempts=30 pi@sediment-pi-zero-w-v1-a 'sudo reboot now'
Copy log files to local machine:
scp -o ConnectTimeout=60 -o ConnectionAttempts=30 pi@sediment-pi-zero-w-v1-a:/home/pi/logs/*.* .
watch --color SYSTEMD_COLORS=1 systemctl status enviro*.service
sudo systemctl restart envirocam-telemetry
sudo systemctl restart envirocam*
- Enable Serial Communication
sudo raspi-config nonint do_serial 2 # Disable serial login shell and enable serial port hardware
sudo reboot
https://core-electronics.com.au/guides/raspberry-pi/raspberry-pi-4g-gps-hat/
sudo apt-get install minicom
pip3 install pyserial
wget https://www.waveshare.com/w/upload/2/29/SIM7600X-4G-HAT-Demo.7z
sudo apt-get install p7zip-full
7z x SIM7600X-4G-HAT-Demo.7z -r -o/home/pi
sudo chmod 777 -R /home/pi/SIM7600X-4G-HAT-Demo
sudo nano /etc/rc.local
Add following line, just above exit 0
:
sh /home/pi/SIM7600X-4G-HAT-Demo/Raspberry/c/sim7600_4G_hat_init
cd /home/pi/SIM7600X-4G-HAT-Demo/Raspberry/c/bcm2835
chmod +x configure && ./configure && sudo make && sudo make install
Plug in USB cable, testing with minicom CTRL-A, E to echo CTRL-A, Q to exit
minicom -D /dev/ttyUSB2
AT+CPIN?
+CME ERROR:10 # No SIM card
Switch to RNDIS dial up mode https://www.waveshare.com/wiki/Raspberry_Pi_RNDIS_dial-up_Internet_access
AT+CUSBPIDSWITCH=9011,1,1
Nope - prevents camera from working...
Disable HDMI:
https://picockpit.com/raspberry-pi/raspberry-pi-zero-2-battery/
sudo raspi-config
Then, go to Advanced Options -> GL Driver -> Legacy
Add to /etc/rc.local:
/usr/bin/tvservice -o
sudo apt-get update
sudo apt-get install python3-pip
sudo apt-get install python3-pil
sudo pip3 install RPi.GPIO
sudo pip3 install waveshare-epaper
Onboard image resizing
sudo apt-get install imagemagick
convert pic/image.org.jpg -resize 200x200 -background white -gravity center -extent 200x200 pic/image.jpg
- Enable Serial Communication
sudo raspi-config nonint do_spi 0
sudo reboot
sudo apt-get install uptimed
sudo systemctl enable uptimed
https://www.youtube.com/watch?v=dbBWyeHbGs0&ab_channel=WillyKjellstrom
Set screen VNC resolution: sudo raspi-config -> 2 Display Options -> D5 VNC Resolution
To disable: https://help.realvnc.com/hc/en-us/articles/5060068870813-Disabling-direct-capture-on-Raspberry-Pi-using-the-command-line sudo sed -i '/CaptureTech/d' /root/.vnc/config.d/vncserver-x11 sudo vncserver-x11 -service -reload
Issues in Bullseye on Zero2? https://www.raspberrypi.com/news/bullseye-camera-system/
- related - possibly not - raspberrypi/rpicam-apps#278
ssh -D 8080 pi@sediment-pi-[machine name]
Use FoxyProxy and setup proxy to localhost:8080
Browse to http://192.168.1.1/html/index.html or http://192.168.8.1/html/index.html
ffmpeg -r 30 -f image2 -pattern_type glob -i "./<YYYY-MM-DD>*.jpg" -s 1014x760 -vcodec libx264 <YYYY-MM-DD>.mp4
ffmpeg -r 30 -f image2 -pattern_type glob -i "*11_2023-01-03*.jpg" -s 3280x1844 -vcodec libx264 output.mp4
mkdir -p mod
label="<Label for bottom left>"
for filename in <filter>_*.jpg; do
date_time="$(echo ${filename:3:10} ${filename:14:2}\\:${filename:16:2})"
ffmpeg -i $filename -y -vf "drawtext=fontfile=/System/Library/Fonts/Avenir.ttc:text='$label':fontcolor=white:fontsize=90:box=1:[email protected]:boxborderw=5:x=10:y=h-th-10,drawtext=fontfile=/System/Library/Fonts/Avenir.ttc:text='$date_time':fontcolor=white:fontsize=90:box=1:[email protected]:boxborderw=5:x=w-tw-10:y=h-th-10" -hide_banner -loglevel error mod/$filename
done
ffmpeg -pattern_type glob -i "mod/14_*.jpg" -vf "scale='min(1280,iw)':-2,format=yuv420p" -c:v libx264 -preset medium -profile:v main -c:a aac -shortest -movflags +faststart ../Output/<SiteName>.mp4
https://www.paulirish.com/2021/video-stabilization-with-ffmpeg-and-vidstab/
ffmpeg -i unstabilized.mp4 -vf vidstabdetect -f null -
ffmpeg -i unstabilized.mp4 -vf vidstabdetect=shakiness=1:tripod=1 -f null -
ffmpeg -i unstabilized.mp4 -vf vidstabtransform stabilized.mp4
Comparison video:
# vertically stacked
ffmpeg -i unstabilized.mp4 -i stabilized.mp4 -filter_complex vstack compare-stacked.mp4
# side-by-side
ffmpeg -i unstabilized.mp4 -i stabilized.mp4 -filter_complex hstack compare-sxs.mp4
Prerequisites:
brew install dotnet-sdk
brew install pgadmin4
dotnet tool install --global dotnet-ef
Postgres DB Server:
docker pull postgres
mkdir ${HOME}/postgres-data/
docker run -d \
--name dev-postgres \
-e POSTGRES_PASSWORD=Pass2020! \
-v ${HOME}/postgres-data/:/var/lib/postgresql/data \
-p 5432:5432 \
postgres
docker exec -it dev-postgres bash
User Secrets:
dotnet tool install dotnet-user-secrets
dotnet user-secrets --project timelapse.api init
dotnet user-secrets --project timelapse.api set "ConnectionStrings:DefaultConnection" "Host=localhost;Port=5432;User ID=postgres;Password=Pass2020!;Database=timelapse"
When making changes to the objects in the code, add migrations and update the database script:
dotnet ef --project timelapse.api migrations add --context "AppDbContext" <migrationName>
dotnet ef --project timelapse.api migrations script -i --context "AppDbContext" -o timelapse.api/Migrations/scripts.sql
Updating database:
dotnet ef --project timelapse.api database update
Note - Wake up should be automatically enabled in saveTelemetry.py
, but you will need to set the RTC time during installation.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ PiJuice CLI ββββββββββββββββββββββββββββββ
ββββββ Wakeup Alarm ββββββ
ββββββ ββββββ
ββββββ Status: OK ββββββ
ββββββ UTC Time: Sun 2000-01-02 19:36:02 ββββββ
ββββββ [ ] Wakeup enabled ββββββ
ββββββ < Set RTC time > ββββββ
ββββββ ββββββ
ββββββ (X) Day ( ) Weekday ββββββ
ββββββ Day: 0 [ ] Every day ββββββ
ββββββ Hour: 0 [ ] Every hour ββββββ
ββββββ ββββββ
ββββββ (X) Minute ( ) Minutes period ββββββ
ββββββ Minute: 0 ββββββ
ββββββ Second: 0 ββββββ
ββββββ ββββββ
ββββββ < Set alarm > ββββββ
ββββββ < Back > ββββββ
ββββββ ββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
γ£ 10 0:-* 3m 1.00 1.0GHz 367M13% 14G12% 2022-04-05 02:22:05
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ PiJuice CLI ββββββββββββββββββββββββββββββ
ββββββ Wakeup Alarm ββββββ
ββββββ ββββββ
ββββββ Status: OK ββββββ
ββββββ UTC Time: Tue 2022-04-05 01:23:15 ββββββ
ββββββ [X] Wakeup enabled ββββββ <---
ββββββ < Set RTC time > ββββββ <---
ββββββ ββββββ
ββββββ (X) Day ( ) Weekday ββββββ
ββββββ Day: 0 [ ] Every day ββββββ
ββββββ Hour: 0 [ ] Every hour ββββββ
ββββββ ββββββ
ββββββ ( ) Minute (X) Minutes period ββββββ <---
ββββββ Minute: 10 ββββββ <---
ββββββ Second: 0 ββββββ
ββββββ ββββββ
ββββββ < Set alarm > ββββββ
ββββββ < Back > ββββββ
ββββββ ββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
γ£ 10 0:-* 3m 1.00 1.0GHz 367M13% 14G12% 2022-04-05 02:23:15
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββ PiJuice CLI ββββββββββββββββββββββββββββββββββββ
βββββββββββ System Task ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [X] System task enabled ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [ ] Watchdog Expire period [ ] Restore ββββββββββββ
βββββββββββ [minutes]: 4 ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [X] Wakeup on charge Trigger level [%]: 20 [X] Restore ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [X] Min charge Threshold [%]: 10 ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [ ] Min battery voltage 3.3 ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ [X] Software Halt Power Delay period [seconds]: 20 ββββββββββββ
βββββββββββ Off ββββββββββββ
βββββββββββ ββββββββββββ
βββββββββββ < Refresh > ββββββββββββ
βββββββββββ < Apply settings > ββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
[X] Low Charge : <SYS_FUNC_HALT_POW_OFF>
Headway 38120s 10ah lifepo4 cell
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ PiJuice CLI ββββββββββββββββββββββββββββββ
ββββββ Battery settings ββββββ
ββββββ ββββββ
ββββββ Status: Custom profile by: HOST ββββββ
ββββββ < Profile: CUSTOM > ββββββ
ββββββ ββββββ
ββββββ [X] Custom ββββββ <-- Set to Custom
ββββββ Chemistry: LIFEPO4 ββββββ <-- Set to LIFEPO4
ββββββ Capacity [mAh]: 15000 ββββββ <-- Set to 15000
ββββββ Charge current [mA]: 2500 ββββββ <-- Set to 2500
ββββββ Termination current [mA]: 50 ββββββ
ββββββ Regulation voltage [mV]: 3600 ββββββ <-- Set to 3600
ββββββ Cutoff voltage [mV]: 2500 ββββββ <-- Set to 2500
ββββββ Cold temperature [C]: 0 ββββββ
ββββββ Cool temperature [C]: 2 ββββββ
ββββββ Warm temperature [C]: 49 ββββββ
ββββββ Hot temperature [C]: 65 ββββββ
ββββββ NTC B constant [1k]: 3450 ββββββ
ββββββ NTC resistance [ohm]: 10000 ββββββ
ββββββ OCV10 [mV]: 3131 ββββββ <-- Set to 3131
ββββββ OCV50 [mV]: 3263 ββββββ <-- Set to 3263
ββββββ OCV90 [mV]: 3303 ββββββ <-- Set to 3303
ββββββ R10 [mOhm]: 91.0 ββββββ <-- Set to 91
ββββββ R50 [mOhm]: 83.0 ββββββ <-- Set to 83
ββββββ R90 [mOhm]: 76.0 ββββββ <-- Set to 76
ββββββ ββββββ
ββββββ < Temperature sense: ON_BOARD > ββββββ <-- Set to ON_BOARD
ββββββ ββββββ
ββββββ < Rsoc estimation: DIRECT_BY_MCU > ββββββ <-- Set to DIRECT_BY_MCU
ββββββ ββββββ
ββββββ < Refresh > ββββββ
ββββββ < Apply settings > ββββββ
ββββββ < Back > ββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ PiJuice CLI βββββββββββββββββββββββββββββ
ββββββ User Scripts βββββ
ββββββ βββββ
ββββββ USER FUNC1: βββββ
ββββββ /home/pi/dev/timelapse/scripts/indicateStatus.sh βββββ
ββββββ USER FUNC2: βββββ
ββββββ USER FUNC3: βββββ
ββββββ USER FUNC4: βββββ
ββββββ USER FUNC5: βββββ
ββββββ USER FUNC6: βββββ
ββββββ USER FUNC7: βββββ
ββββββ USER FUNC8: βββββ
ββββββ USER FUNC9: βββββ
ββββββ USER FUNC10: βββββ
ββββββ USER FUNC11: βββββ
ββββββ USER FUNC12: βββββ
ββββββ USER FUNC13: βββββ
ββββββ USER FUNC14: βββββ
ββββββ USER FUNC15: βββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββ PiJuice CLI βββββββββββββββββββββββββββββ
ββββββ Settings for SW1 βββββ
ββββββ βββββ
ββββββ < PRESS: USER_FUNC1, 0 > βββββ
ββββββ < RELEASE: NO_FUNC, 0 > βββββ
ββββββ < SINGLE_PRESS: HARD_FUNC_POWER_ON, 800 > βββββ
ββββββ < DOUBLE_PRESS: NO_FUNC, 0 > βββββ
ββββββ < LONG_PRESS1: SYS_FUNC_HALT, 10000 > βββββ
ββββββ < LONG_PRESS2: HARD_FUNC_POWER_OFF, 20000 > βββββ
ββββββ βββββ
ββββββ < Back > βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
ββββββ βββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
python3 /usr/bin/pijuice_log.py --enable WAKEUP_EVT
sudo -u pijuice /home/pi/dev/timelapse/scripts/indicateStatus.sh
Naccon 3.7V 26650HP 5000mAh On continuously Charge from wall charger about 5 hours Discharge 95 -> 5% about 12 hours. Discharge 4 -> 1$ about 10 hours.
Using tailscale to manage updates to pi's:
From https://tailscale.com/download/linux/rpi-bullseye:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
Use SSH to establish a dynamic port forwarding session to the pi.
ssh -D 8080 pi@<device name>
Use FoxyProxy to route local traffic on port 8080 through the SSH tunnel.
Then browse to the modem's IP address (e.g. http://192.168.1.1)
pssh --hosts ~/dev/venari/timelapse/hosts.txt -t 900 -i 'cd dev/timelapse; git log -1'
pssh --hosts ~/dev/venari/timelapse/hosts.txt -t 900 -x '-o ConnectTimeout=60 -o ConnectionAttempts=15' -i 'cd dev/timelapse; git log -1;git status'
pssh --hosts ~/dev/venari/timelapse/hosts.txt -t 900 -x '-o ConnectTimeout=60 -o ConnectionAttempts=15' -i 'cd dev/timelapse; git diff'
3D models
- Raspberry Pi Camera Module v3 STL files: https://www.printables.com/model/368779-raspberry-pi-camera-module-3-v3/