Raspberry Pi WebCam with 3G (Solarcam)

From Wurst-Wasser.net
(Redirected from Raspberry Pi WebCam with 3G)
Jump to: navigation, search

The Idea

  • Use solar panel to harvest energy
  • Store the energy in a left over car battery
  • Power a Pi, camera and 3G-Stick Mobile HotSpot[1]
  • Make a nice webcam out of it :)

Shopping List

  • A Pi with camera :)
  • A mobile Hotspot (I used a Huawei E1750C). Because getting an UMTS-Stick getting to work with an Pi sucks. Additionally you can easily join the WiFi network for maintenance)
  • SIM Card
  • (used) Car battery, in my case an 74Ah battery with one dead cell
  • Solar panel (12V, 30W eBay around 40€) NOTE: Might be to small!
  • Solar charger (f.e. "10A 12V/24V PWM solar panel regler solarpanel" @ ebay for around 15€)
  • more to come…

How To

Update your Pi to the latest Linux version

See https://www.datenreise.de/raspberry-pi-raspbian-linux-wheezy-jessie-upgrade/

Scripts

Scripts running on the Pi

cronjobs

Add this to crontab:

# DS18B20
*/15 * * * *	/home/pi/Development/temperature/readTemperature.sh > /dev/null 2>&1
#
# PiCam - Take Snapshot(s)
*/15 * * * *	/home/pi/Development/webcam/takePicture.sh > /dev/null 2>&1
knock.sh
#!/bin/bash

# knock on -heavens- servers door...(bad joke. sorry)

for x in 2323 4242 5555; do nmap -Pn --host_timeout 201 --max-retries 0 -p $x my_fqdn; done

(needs knockd)

takePicture.sh
#!/bin/bash
#set -x
#
# http://www.wurst-wasser.net/wiki/index.php/Raspberry_Pi_WebCam_with_3G
#
# Globals
#
GFOLDERTARGET="/var/www/html/webcam-archive"
GFILELOCK="${GFOLDERTARGET}/takePicture.lck"
GFILELOCKAGEMAX="60"
GTIMESTAMP="`date +'%Y-%m-%d_%H-%M-%S'`" 
GTIMESTAMPDATE="`date +'%Y-%m-%d'`" # Human readable
GTIMESTAMPTIME="`date +'%H:%M:%S'`" # Human readable
GTIMESTAMPMINUTES="`date +'%M'`"
GTIMESTAMPHOURS="`date +'%H'`"
#GTIMESTAMPHOURS="16"
#GTIMESTAMPMINUTES="00"
GFILETARGET="${GFOLDERTARGET}/${GTIMESTAMP}.jpg"
GFILELASTSNAP="/var/www/html/webcam/lastsnap.jpg"
GUPLOADEVERYTIME=0
GBASENAME="${0}"
function log
{
   logger "${GBASENAME}: $1"
   echo $1
} 
if [ "${1}" = "-f" ]; then
  log "Forcing upload."
  GUPLOADEVERYTIME=1
fi
#
#
#
# Main
#
#
# Check locking
if [ -f "${GFILELOCK}" ]; then 
  LOCKFILEAGE="`stat --format=\"%Z\" \"${GFILELOCK}\"`"
  NOW="`date +'%s'`"
  AGE="`expr ${NOW} - ${LOCKFILEAGE}`"
  if [ "${AGE}" -gt "${GFILELOCKAGEMAX}" ]; then
     log "Lockfile is ${AGE} seconds old, removing it."
     rm "${GFILELOCK}"
  else
     log "Lockfile is ${AGE} seconds old, exiting."
     exit 1
  fi
fi
#
#
# Lock
echo $$ > "${GFILELOCK}"
#
# timeout should not be 0, since images will be to dark and are noisy
#raspistill --nopreview -t 1 -w 1024 -h 768 -o -
# 
# Since my cam is mounted wrong, I prefer to flip it :) (and flop it)
#raspistill --nopreview -t 2 -w 1024 -h 768 -o - | convert - -flip -flop -
#raspistill --nopreview -w 1024 -h 768 -o - | convert - -flip -flop -
raspistill --nopreview -w 1024 -h 768 -o - | convert - -flip -flop - > "${GFOLDERTARGET}/lastsnap.tmp"
#
# Bild verkleinern...
mogrify -quality "80%" -interlace Plane "${GFOLDERTARGET}/lastsnap.tmp"
#mogrify -stroke white -pointsize 10 -gravity SouthEast -draw "text 0,0 '${GTIMESTAMPHR}'" "${GFOLDERTARGET}/lastsnap.tmp"
#mogrify -stroke white -pointsize 10 -gravity SouthWest -draw "text 0,0 '${GTIMESTAMPDATE}'" "${GFOLDERTARGET}/lastsnap.tmp" 
#mogrify -stroke white -pointsize 10 -gravity SouthEast -draw "text 0,0 '${GTIMESTAMPTIME}'" "${GFOLDERTARGET}/lastsnap.tmp"
mogrify -stroke black -pointsize 10 -gravity NorthWest -draw "text 0,0 '${GTIMESTAMPDATE}'" "${GFOLDERTARGET}/lastsnap.tmp"
mogrify -stroke black -pointsize 10 -gravity NorthEast -draw "text 0,0 '${GTIMESTAMPTIME}'" "${GFOLDERTARGET}/lastsnap.tmp"
#
# Save the taken picture to the archive, give the file a nice timestamped name
mv "${GFOLDERTARGET}/lastsnap.tmp" "${GFILETARGET}"
#
# Copy the latest image to the "current view" - folder
cp "${GFILETARGET}" "${GFILELASTSNAP}"
#
#raspistill -t 0 -w 1024 -h 768 -o -
#
# Make a upload to our webserver
if [ "${GTIMESTAMPHOURS}" -ge 0 -a "${GTIMESTAMPHOURS}" -le 23 -o "${GUPLOADEVERYTIME}" -eq 1 ]; then # Between 0 and 0 o'clock
  if [ "${GTIMESTAMPMINUTES}" -eq 0 -o "${GTIMESTAMPMINUTES}" -eq 15 -o "${GTIMESTAMPMINUTES}" -eq 30  -o "${GTIMESTAMPMINUTES}" -eq 45 -o "${GUPLOADEVERYTIME}" -eq 1 ]; then
     log "Uploading picture..."
     ~/Development/webcam/knock.sh 
     #   scp "${GFILETARGET}" dt:tmp/
     sftp dt:tmp/ <<< "put ${GFILETARGET}"
  fi
fi
#
# Cleanup: Delete all files older than n*24 hours
find "${GFOLDERTARGET}" -ctime +1 -delete
#
# Unlock
rm "${GFILELOCK}"
#
# Exit
exit 0
readTemperature.sh
#!/bin/bash
#set -x
#
# Script: readTemperature.sh
# Author: Heiko Kretschmer
# Purpose: Reading the temperature and generating a nice plot
#
#

#
# Globals
#
GDEVICEID="10-000802ae4298"
#GDEVICEID="10-000802ad5087"
GDEVICESPATH="/sys/bus/w1/devices"
GDEVICEVALUEFILE="w1_slave"
GTIMESTAMPFORMAT="%Y-%m-%d_%H:%M"
GTIMESTAMP="`date +${GTIMESTAMPFORMAT}`"
GTIMESTAMPTIME="`date '+%H:%M'`"
GTIMESTAMPDATE="`date '+%Y-%m-%d'`" # ISO 8601 date format
GTIMESTAMPDATEHUMANREADABLE="`date '+%A, %Y-%m-%d'`"
GFOLDERBASE="/home/pi/Development/temperature"
GFOLDERLOGS="${GFOLDERBASE}/logs"
GFOLDERGRAPHS="${GFOLDERBASE}/graphs"
GFOLDERTMP="${GFOLDERBASE}/tmp"
GFILELOG="${GFOLDERLOGS}/readTemperature_${GTIMESTAMPDATE}.log"
GFILEGRAPH="${GFOLDERGRAPHS}/readTemperature_${GTIMESTAMPDATE}.svg"
GFILEPLOTCOMMANDS="${GFOLDERTMP}/readTemperature-plot.cmd"
GTIMESTAMPMINUTES="`date +'%M'`"
GTIMESTAMPHOURS="`date +'%H'`" 
GUPLOADEVERYTIME=0
GBASENAME="${0}"
function log
{
  logger "${GBASENAME}: $1"
  echo $1
}

if [ "${1}" = "-f" ]; then
  log "Forcing upload."
  GUPLOADEVERYTIME=1
fi


#
# Main
# 

# Init
test ! -d "${GFOLDERLOGS}" && mkdir "${GFOLDERLOGS}"
test ! -d "${GFOLDERGRAPHS}" && mkdir "${GFOLDERGRAPHS}"
test ! -d "${GFOLDERTMP}" && mkdir "${GFOLDERTMP}"

# Get the temperature
VALUERAW="`cat \"${GDEVICESPATH}/${GDEVICEID}/${GDEVICEVALUEFILE}\" | grep t= | cut -d= -f2`"
VALUE="`echo \"scale = 3; ${VALUERAW} / 1000\" | bc`"
#echo Temperature: ${VALUE} 

# Write it into the log (one logfile per day to get nice graphs)
echo -e "${GTIMESTAMPTIME}\t${VALUE}" >> "${GFILELOG}"

# Generate the graph
test -f "${GFILEPLOTCOMMANDS}" && rm "${GFILEPLOTCOMMANDS}"
echo "reset" >> "${GFILEPLOTCOMMANDS}"
echo "set key inside right top vertical Right noreverse enhanced autotitles columnhead nobox" >> "${GFILEPLOTCOMMANDS}"

# Set time format for X axis
echo "set timefmt \"%H:%M\"" >> "${GFILEPLOTCOMMANDS}"
echo "set xdata time" >> "${GFILEPLOTCOMMANDS}"
echo "set format x \"%H:%M\"" >> "${GFILEPLOTCOMMANDS}"

# Setup line style (#1) for the temperature line
echo "set style line 1 lc rgb '#8b1a0e' pt 1 ps 1 lt 1 lw 2" >> "${GFILEPLOTCOMMANDS}" # http://www.gnuplotting.org/tag/grid/
echo "set style data lines" >> "${GFILEPLOTCOMMANDS}"

# Set X tics (one tic per hour, rotate that tick-labels by 90 deg and move em a bit)
echo "set xtics \"01:00\" rotate by 90 offset 0,-2 out nomirror" >> "${GFILEPLOTCOMMANDS}"

# Setup Grid (with line style #12)
echo "set style line 12 lc rgb '#E0E0E0' lt 0 lw 1" >> "${GFILEPLOTCOMMANDS}" # http://www.gnuplotting.org/tag/grid/
echo "set grid back ls 12" >> "${GFILEPLOTCOMMANDS}" # http://www.gnuplotting.org/tag/grid/

# Setup Title
echo "set title \"Temperature on ${GTIMESTAMPDATEHUMANREADABLE}\"" >> "${GFILEPLOTCOMMANDS}"

# Label X and Y Axis
echo "set ylabel \"°C\"" >> "${GFILEPLOTCOMMANDS}"
echo "set xlabel \"Time\" offset 0,-0.5" >> "${GFILEPLOTCOMMANDS}" 

# Setup Y range
echo "set yrange [ 0.000 : ] noreverse nowriteback" >> "${GFILEPLOTCOMMANDS}"
 
# Set output file type to svg and plot it into file
echo "set term svg size 640,480" >> "${GFILEPLOTCOMMANDS}"
echo "set output \"${GFILEGRAPH}\"" >> "${GFILEPLOTCOMMANDS}"
echo "plot \"${GFILELOG}\" using 1:2 title 'Temperature' with l ls 1" >> "${GFILEPLOTCOMMANDS}"
cat "${GFILEPLOTCOMMANDS}" | gnuplot 

# Copy this to Webserver
if [ "${GTIMESTAMPMINUTES}" -eq 0 -o "${GTIMESTAMPMINUTES}" -eq 30 -o "${GUPLOADEVERYTIME}" -eq 1 ]; then
  log "Uploading hourly graph..."
#   scp "${GFILEGRAPH}" dt:tmp/
  ~/Development/webcam/knock.sh 
  sftp dt:tmp/ <<< "put ${GFILEGRAPH}"
  rm "${GFILEGRAPH}"
fi
#
# EOF

Scripts running on the web server

cronjobs
# m h  dom mon dow   command
02,17,32,47 * * * *	~/bin/movePictures.sh >> /dev/null 2>&1
#
# Create a daily time-lapse
50 23 * * *    ~/bin/mergeAllPicturesInOneAnimatedGIF.sh >> /dev/null 2>&1
movePictures.sh

(called by crond)

#!/bin/bash
#set -x

# movePictures.sh
# Author: Heiko
# Purpose: Move files into place (Webserver), set lastsnap and delete old files... 

# Globals
GFOLDERWWW="/var/www/html/solarcam"
GFOLDERWWWIMAGES="${GFOLDERWWW}/webcam-archive"
GFILEWWWLASTSNAP="${GFOLDERWWW}/webcam/lastsnap.jpg"
GFILEWWWLASTGRAPH="${GFOLDERWWW}/webcam/lastgraph.svg"
GHOME="/home/solarcam" 

# Move the image in place
mv "${GHOME}/tmp/"*.jpg "${GFOLDERWWWIMAGES}/"
mv "${GHOME}/tmp/"*.svg "${GFOLDERWWWIMAGES}/"

# Make the newest "lastsnap"
FILENEWEST="`ls -Art \"${GFOLDERWWWIMAGES}\" | grep jpg | tail -1`"
cp -p "${GFOLDERWWWIMAGES}/${FILENEWEST}" "${GFILEWWWLASTSNAP}"
FILENEWEST="`ls -Art \"${GFOLDERWWWIMAGES}\" | grep svg |  tail -1`"
cp -p "${GFOLDERWWWIMAGES}/${FILENEWEST}" "${GFILEWWWLASTGRAPH}"

# Cleanup: Delete all files older than n*24hrs
find "${GFOLDERWWWIMAGES}" -ctime +30 -delete

# EOF
mergeAllPicturesInOneAnimatedGIF.sh

(called by crond)

#!/bin/bash
set -x
# 
# Script: mergeAllPicturesInOneAnimatedGIF.sh
# Author: Heiko Kretschmer
# Purpose: Merge all .jpg into one animated .gif
#
# History:
# 2014-03-30,	09:52, h:	Creation. Note. default ticks in -delay is n/100 of a second
# 2018-03-31,   08:11, h:	Moved to dt. Make daily GIF.
# 

# Globals
#
GFOLDERSOURCE="/var/www/html/solarcam/webcam-archive"
GSUFFIXSOURCE="jpg"
GTIMESTAMP="`date +'%Y-%m-%d'`"
#GTIMESTAMP="2018-03-29"
#echo "${GTIMESTAMP}"
#exit 0
GFILETARGET="/var/www/html/solarcam/webcam-archive/${GTIMESTAMP}.gif"

# (Un)Main
#
convert -limit area 0 -delay 25 -loop 1 "${GFOLDERSOURCE}/${GTIMESTAMP}*.${GSUFFIXSOURCE}" "${GFILETARGET}"
index.php

(place in your web server directory)

<?php
?>
<!doctype html public "-//W3C//DTD HTML 4.0 //EN"> 
<html> 
 <head> 
       <title>Webcam History</title> 
       <META HTTP-EQUIV="refresh" CONTENT="3600">
 </head> 
 <body style="background-color:#EEEEEE">
 
       <H1>Webcam Live Image</H1> 
       <img src="./webcam/lastsnap.jpg" />        
       <img src="./webcam/lastgraph.svg" />        
 
       <H1>Webcam History</H1> 
 	 
 <?php 

	$imagepath = "./webcam-archive"; 
	$images = scandir($imagepath); 
	$images = array_reverse($images); // newest on top
	foreach($images as $curimg)
	{ 
		if (substr($curimg, 0, 1)!="." && filesize("$imagepath/$curimg")>10000 &&
                    ( strstr($curimg, ".swf") || strstr($curimg, ".avi") || strstr($curimg, ".mpg") || strstr($curimg, ".jpg") || strstr($curimg, ".gif")  ) )
		{
			/* Link to Viewing-Page */
			$curimgencoded=urlencode($curimg);
	  		echo "<a href='./imageviewer.php?filename=$curimgencoded' alt='Show $curimg'  />$curimg<br>\n";

			/* Direct Link to Image File */
 			if (0)
			{
  				#echo "<H2>$curimg</H2>\n";
	  			#echo "<img src='$imagepath/$curimg' alt='$curimg' /><br>$curimg<br>\n";
	  			echo "<a href='$imagepath/$curimg' alt='$curimg' />$curimg<br>\n";
				#echo "<hr>\n";
			}
		}
	}; 

 ?> 
 
	<hr>
	<br/>
	<a href='./index.php'>Back!</a>

 </body> 
 </html>
 
imageviewer.php

(place in your web server directory)

<?php
 ?>
 <!doctype html public "-//W3C//DTD HTML 4.0 //EN"> 
 <html> 
 <head> 
       <title>Webcam History</title> 
       <META HTTP-EQUIV="refresh" CONTENT="60">
 </head> 
 <body style="background-color:#EEEEEE">
 	 
 <?php 

	/* Chosen Image */
    $filename=htmlspecialchars($_GET["filename"]);
    echo("<H1>Webcam Archive Image: $filename</H1>\n"); 
	//echo("<H2>Showing " . $filename . "</H2><br/>\n");
	$imagepath = "./webcam-archive"; 

	/* Alle Bilder im Archiv finden (Non-Bilder wegfiltern) */
	$images = scandir($imagepath); // Alle Bilder im Ordner
    #echo("images:\n");
    #print_r($images);
    #echo("---\n");
	$imagesfiltered=array();
	foreach($images as $curimg) // filter alles weg, was kein Bild ist!
	{ 
		#echo("Checking: $curimg<br/>\n");
		if (substr($curimg, 0, 1)!="." && filesize("$imagepath/$curimg")>10000 &&
                    ( strstr($curimg, ".swf") || strstr($curimg, ".avi") || strstr($curimg, ".mpg") || strstr($curimg, ".jpg") ) )
		{
			array_push($imagesfiltered, $curimg);
		}
	}
    #echo("imagesfiltered:\n");
	#print_r($imagesfiltered);
	#echo("---\n");

	/* Finde das vorherige und das nachfolgende Bild */
	$idx=array_search($filename, $imagesfiltered);
	#echo("idx: $idx<br/>\n");

	/* Previous Image-Link erzeugen */
	$previouslink="";
	if ($idx>0)
	{
		$previousimage=$imagesfiltered[$idx-1];
		$previousimageencoded=urlencode($previousimage);
		$previouslink="<a href='./imageviewer.php?filename=$previousimageencoded' alt='Show previous image'>< Previous Image <</a>\n";
	}
	
	/* Next Image-Link erzeugen */
	$nextlink="";
    if ($idx<count($imagesfiltered)-1)
	{
		$nextimage=$imagesfiltered[$idx+1];
		$nextimageencoded=urlencode($nextimage);
		$nextlink="<a href='./imageviewer.php?filename=$nextimageencoded' alt='Show next image'>> Next Image ></a>\n";
	}
	
	/* Obere Navigation */
	echo("<p align='center'>\n");
		//echo("$previouslink");
		//if (strlen($previouslink) > 1) { echo($previouslink); }
		if (strlen($previouslink) > 1 ) { echo($previouslink); } 
		echo("     "); // http://de.wikihow.com/Leerzeichen-in-HTML-einfügen
		if (strlen($nextlink) > 1) { echo($nextlink); }
	echo("</p>\n");	

	/* Das Bild */
	echo("<p align='center'>\n");
	echo("<img src='" . $imagepath . "/" . $filename . "' alt='$filename' />\n");
	echo("</p>\n");

	/* Untere Navigation */
	echo("<p align='center'>\n");
		if (strlen($previouslink) > 1 ) { echo($previouslink); } 
		echo("     "); // http://de.wikihow.com/Leerzeichen-in-HTML-einfügen
		if (strlen($nextlink) > 1) { echo($nextlink); }
	echo("</p>\n");	

	if (0)
	foreach($images as $curimg)
	{ 
		if (substr($curimg, 0, 1)!="." && filesize("$imagepath/$curimg")>10000 &&
                    ( strstr($curimg, ".swf") || strstr($curimg, ".avi") || strstr($curimg, ".mpg") || strstr($curimg, ".jpg") ) )
		{
			#echo "<H2>$curimg</H2>\n";
	  		#echo "<img src='$imagepath/$curimg' alt='$curimg' /><br>$curimg<br>\n";
	  		echo "<a href='$imagepath/$curimg' alt='$curimg' />$curimg<br>\n";
			#echo "<hr>\n";
		}
	}; 

 ?> 
 
	<hr>
	<br/>
	<a href='./index.php'>Back!</a>
 
 </body> 
 </html>

Photos

The finished product looks like this:
RaspberryPiSolarCam01.jpg RaspberryPiSolarCam02.jpg RaspberryPiSolarCam03.jpg


Tips

  • Remember that this Pi is in the wild and easily stolen or all the data read
  • Don't use your usual passwords for this project
  • Don't store keys on the Pi
  • Let your master server fetch the data (so no private keys are stored on the Pi) or use a chroot environment
  • Create a special user to access the data from remote, or even better, get it per HTTP and use .htaccess for privacy (and less 3G-traffic ;) )
  • If your WiFi connection breaks regularly, see Raspberry Pi WiFi issue (Solarcam)

?


  • Since setting up the 3G-Stick was a pain in...
  • Retrieved from "http://www.wurst-wasser.net/wiki/index.php?title=Raspberry_Pi_WebCam_with_3G_(Solarcam)&oldid=5983"