Weather Camera How-To

Our weather camera project originated during the Bullock Fire in 2002. We archived images from a television station’s camera on Mount Bigelow and manually generated our first timelapse. Soon afterwards, we installed a DLink DCS-1000 IP camera in a CCTV outdoor enclosure at the site. In 2006, we installed another DCS-1000 on Mount Lemmon, 4 miles away. These cameras have been upgraded over the years and are now 5 megapixel devices. The software has evolved as well. We now grab snapshots every 30 seconds and generate a timelapse for each camera nightly. The timelapses are uploaded to YouTube via API.

This document shows the commands necessary to implement our cameras on CentOS 8. The same packages should be available on other flavors of Linux, such as Rocky, Debian and Ubuntu. Replace the package manager from CentOS to that of your distribution. The various shell scripts may need to have command paths adjusted.

Since we have SSDs for some OS drives, camera images are stored in /export/cameras to permit a traditional hard drive or NAS to be mounted in this location. This avoids putting the high number of writes on the SSD drive.

Here are the various scripts:



#Camtype 1 is older HTTP GW Security 5MP Model AC400L
#Camtype 2 is newer RTSP GW Security 5MP Model

CAMNAME=`echo $1`
CAMIP=`echo $2`
CAMTYPE=`echo $4`
DATE=`/usr/bin/date +%Y%m%d%H%M%S`

if [ -f /tmp/$CAMNAME.lock ] ; then
echo Lock Exists
/usr/bin/kill `/usr/bin/ps ax | /usr/bin/egrep -v grep | /usr/bin/grep cameras/$CAMNAME | /usr/bin/awk '{print $1}'`
exit 1

if [ ! -d $DIRECTORY/$CAMNAME ]; then

#/usr//bin/sleep 25
/bin/touch /tmp/$CAMNAME.lock

if [ "$CAMTYPE" = "" ]; then
        echo "Error: CAMTYPE is non-existent or empty"
        elif [ "$CAMTYPE" -eq 1 ]; then
                /usr/bin/curl --connect-timeout 5 --max-time 30 -s -o $DIRECTORY/$CAMNAME/$DATE.jpg "http://$CAMIP/cgi-bin/snapshot.cgi?stream=1"
        elif [ "$CAMTYPE" -eq 2 ]; then
                /usr/bin/ffmpeg -rtsp_transport tcp -loglevel fatal -ss 2 -i rtsp://$CAMIP:554/h264?username=cam\&password=cam -y -f image2 -qscale 0 -frames 1 $DIRECTORY/$CAMNAME/$DATE.jpg
        elif [ "$CAMTYPE" -eq 3 ]; then
                /usr/bin/curl --connect-timeout 5 --max-time 30 -s -o $DIRECTORY/$CAMNAME/$DATE.jpg "http://$CAMIP/cgi-bin/snapshot.cgi?stream=0%20(main)&username=admin&password=123456"

/usr/bin/ln -sf $DIRECTORY/$CAMNAME/$DATE.jpg $DIRECTORY/$CAMNAME-raw.jpg
/bin/convert $DIRECTORY/$CAMNAME/$DATE.jpg -resize 320x240 $DIRECTORY/$CAMNAME-thumb-new.jpg
/usr/bin/mv $DIRECTORY/$CAMNAME-thumb-new.jpg $DIRECTORY/$CAMNAME-thumb.jpg
/usr/bin/rm -f $DIRECTORY/$CAMNAME/$DATE.jpg

/usr/bin/rm -f /tmp/$CAMNAME.lock




/home/camera/daily-timelapse publiccamera public "Camera at the Repeater Site"
/home/camera/daily-timelapse privatecamera unlisted "Camera at my House"



#Invoke with daily-timelapse CAMNAME PRIVACY CAMDESCRIPTION
#PRIVACY is unlisted public or private

CAMNAME=`echo $1`
PRIVACY=`echo $2`

DATE=`/bin/date +%Y-%m-%d --date="yesterday"`

/usr/bin/rm -f $CAMDIR/timelapse.avi
/usr/bin/ffmpeg -f image2 -framerate 30 -pattern_type glob -i '*.jpg' -framerate 30 -vcodec mjpeg timelapse-new.avi

/usr/bin/rm $CAMDIR/2*.jpg

/usr/bin/mv $CAMDIR/timelapse-new.avi $CAMDIR/timelapse.avi
/usr/local/bin/youtube-upload --description=Timelapse --title="$CAMDESCRIPTION $DATE" --privacy $PRIVACY $CAMDIR/timelapse.avi
/usr/bin/rm -f $CAMDIR/timelapse.avi


YEAR=`/usr/bin/date +%Y`

#declare -a CameraList=("lomalinda" "mlre" "house5" "mlfd2" "bigelow-w")
#declare -a CameraList=("cabin1" "cabin 7")
declare -a CameraList=("lemmon1" "lemmon2" "lemmon9")
for cam in ${CameraList[@]}; do



/usr/bin/rm -f $DIRECTORY/timelapse.avi
/usr/bin/ffmpeg -f image2 -framerate 30 -pattern_type glob -i '*.jpg' -framerate 30 -vcodec mjpeg timelapse-new.avi

/usr/bin/rm $DIRECTORY/$YEAR*.jpg

/usr/bin/mv $DIRECTORY/timelapse-new.avi $DIRECTORY/timelapse.avi
/usr/local/bin/youtube-upload --description=Timelapse --title="$cam $YEAR" --privacy public $DIRECTORY/timelapse.avi
#/usr/bin/rm -f $DIRECTORY/timelapse.avi


noonscript: (Be sure to update your coordinates.)

DATE=`/usr/bin/date +%Y%m%d`
NOON=`/usr/local/bin/sunwait report 32.441972N 110.780247W | /usr/bin/grep directly | /usr/bin/awk '{print $4}' | /usr/bin/cut -b 1,2,4,5`

#declare -a CameraList=("lomalinda" "mlre" "house5" "mlfd2" "bigelow-w")
#declare -a CameraList=("cabin1" "cabin7")
declare -a CameraList=("lemmon1" "lemmon2" "lemmon9")
for cam in ${CameraList[@]}; do

if [ ! -d $DIRECTORY/$cam-noon ]; then
  mkdir -p $DIRECTORY/$cam-noon

FILE=`/usr/bin/ls $DIRECTORY/$cam/$DATE$NOON??.jpg | /usr/bin/tail -1`
#echo File is $FILE
/usr/bin/cp $FILE $DIRECTORY/$cam-noon/

Here’s a sample crontab for the camera user:

#Create timelapses at midnight
1 0 * * *       /home/camera/daily-master >> /dev/null 2>&1

#Grab the solar noon image from selected cameras for annual timelapse
0 14 * * * /home/camera/noonscript >> /dev/null 2>&1

#Generate annual timelapse on New Years Eve Day
0 15 31 12 * /home/camera/noonencode >> /dev/null 2>&1

#Each camera gets 2 entries for 30-second image collection
* * * * * /home/camera/camerascript publiccamera logooverlay.png 1 >> /dev/null 2>&1
* * * * * /usr/bin/sleep 30 ; /home/camera/camerascript publiccamera logooverlay.png 1 >> /dev/null 2>&1
* * * * * /home/camera/camerascript privatecamera logooverlay.png 1 >> /dev/null 2>&1
* * * * * /usr/bin/sleep 30 ; /home/camera/camerascript privatecamera logooverlay.png 1 >> /dev/null 2>&1

Create an index.html file indexing your cameras in /export/cameras . The logooverlay.png files are transparent PNG files with a club logo, copyright notice and/or other information which is superimposed onto the publicly viewable images.

Be sure to create directories for each camera as /export/cameras/cameraname

Creating a Youtube channel and setting up the API key is beyond the scope of this document, but is well documented elsewhere.

To keep modern browsers happy, we use Let’s Encrypt and CertBot for SSL-encrypted connections to the webserver.  Let’s Encrypt has extensive documentation on setup for the Apache webserver that we use.

Here are the various commands to install and configure the required packages, assuming a minimal OS installation. Some packages are to make future troubleshooting easier.

#yum -y update

# vi /etc/selinux/config


# systemctl disable firewalld

# yum -y install postfix

# vi /etc/aliases

root: [email protected]
camera: root

# newaliases

# vi /etc/postfix/

relayhost =
myhostname =
mydomain =

# systemdctl enable postfix

# yum -y install httpd httpd-tools

# dnf install epel-release dnf-utils

# dnf config-manager --set-enabled powertools

# yum-config-manager --add-repo=

# dnf install ffmpeg

# yum -y install net-tools ntp ftp whois curl wget lsof

# yum -y install python38

# easy_install-3.8 --upgrade google-api-python-client

# adduser camera -m -d /home/camera -s /bin/bash

# passwd camera

# vi /etc/sshd/sshd_config

PermitRootLogin no

# dnf install ImageMagick ImageMagick-devel

# yum -y install telnet

# mkdir -p /export/cameras

# chown camera:camera /export/cameras

$ mkdir youtube-upload
$ cd youtube-upload/
$ wget
$ chmod +x

# pip-3.8 install --upgrade google-auth google-auth-oauthlib google-auth-httplib2

# pip3.8 install googlecl

# pip3.8 install --upgrade google-api-python-client oauth2client progressbar2

# cd /usr/local/src/

# wget

# mv

# yum -y install unzip

# unzip

# mv youtube-upload-master youtube-upload

# cd youtube-upload

# python3.8 install

# cd /usr/local/src

# wget

# mv

# unzip

# mv sunwait-master sunwait

# cd sunwait

# yum -y install gcc-c++ gcc make

# make

# mv sunwait /usr/local/bin/

Edit httpd.conf to change the root directory from /var/www/html to /export/cameras

# systemctl enable httpd

# systemctl start httpd