Raspberry Pi WebCam with 3G (Solarcam)
(Redirected from Raspberry Pi WebCam with 3G)
The Idea
- Use solar panel to harvest energy
- Store the energy in a left over car battery
- Power a Pi, camera and
3G-StickMobile 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:
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)
?
- Links
- Raspberry Pi WiFi issue (Solarcam)
- https://www.datenreise.de/raspberry-pi-raspbian-linux-wheezy-jessie-upgrade/
- http://zieren.de/raspberry-pi/huawei-e303-3g-stick/
- http://garethhowell.com/wp/connect-raspberry-pi-3g-network/
- My buddy's solar webcam project: http://buchkopfturm.solar-webcam.de/ which is documented here: https://www.foto-webcam.eu/wiki/
- ↑ Since setting up the 3G-Stick was a pain in...