Webcam mit Bewegungserkennung am Raspberry mit „motion“

Das motion paket nutzt eine USB Webcam am raspberry und schreibt geänderte Bilder in ein Verzeichnis. Später sollen die Bilder per PHP Script über den Webserver einsehbar sein.

Motion installieren

Paket motion installieren und Backup des Config-Files:

[sourcecode language=“plain“]sudo apt-get install motion
sudo cp /etc/motion/motion.conf /etc/motion/motion.conf.bak[/sourcecode]

Verzeichnis erstellen, wo die Bilder abgelegt werden (wenn nicht Standard). Das Verzeichnis sollte beim Raspberry auf einen USB-Stick ausgelagert werden wg. vieler Schreibzugriffe (Siehe USB Stick mit Raspberry)

[sourcecode language=“plain“]sudo mkdir /var/www/motion
sudo mkdir /var/www/motion/cams
sudo mkdir /var/www/motion/cam1
sudo mkdir /var/www/motion/cam1/tmb[/sourcecode]

Gemeinsame Berechtigungen für motion und den webserver

Möchten wir auf die Bilddatein vom PHP Script oder anderweitig vom Webserver zugreifen, müssen die korrekten Berechtigungen gesetzt werden und die Gruppenmitgliedschaft angepasst werden:

[sourcecode language=“plain“]sudo groupadd www
sudo usermod -a -G www www-data
sudo usermod -a -G www motion
[/sourcecode]

PHP Script für Webcam Bilder anzeige

Das Script gibt es hier für Euch kostenlos zum Download.

Die Dateien nach /var/www/motion kopieren (zb. mit WinSCP kopieren oder mit wget direkt runterladen und entzipppen).

Evlt noch Berechtigungen setzen:

[sourcecode language=“plain“]sudo chown -R www-data:www /var/www/motion/*[/sourcecode]

Dann noch das Wartungs-Script ausführbar machen

[sourcecode language=“plain“]sudo chmod +x /var/www/motion/jobdo.php
sudo chown www-data:www /var/www/motion/jobdo.php[/sourcecode]

Default ACL und Berechtigungen für neue Bilder müssen gesetzt werden. Dies geschieht mit den erweiterten Berechtigungen an den Ordnern:

[sourcecode language=“plain“]
sudo chmod g+s /var/www/motion/cams/cam1
sudo setfacl -d -m g:www:rw /var/www/motion/cams/cam1
sudo chown motion:www /var/www/motion/cams/cam1/*
sudo chmod www-data:www /var/www/motion/cams/cam1/*
sudo chmod u+w,g+rw,o+r /var/www/motion/cams/cam1/*
[/sourcecode]

motion.conf anpassen

[sourcecode language=“plain“]sudo nano /etc/motion/motion.conf[/sourcecode]

Geänderte Werte:

[sourcecode language=“plain“]daemon on
framerate 2
minimum _frame_time 2
threshold 1500
ffmpeg_cap_new off
target_dir /var/www/motion/cams/cam1
webcam_quality 50
on_event_start /var/www/motion/jobdo.php "%v-%Y%m%d%H%M%S"
on_event_end /var/www/motion/jobdo.php[/sourcecode]

nun nur noch motion und den apache neu starten:

[sourcecode language=“plain“]sudo /etc/init.d/motion restart
sudo /etc/init.d/apache2 restart[/sourcecode]

Eine Raspberry Grundinstallation mit PHP und Apache

Raspbian installieren auf SD-Karte 

Downloads

Image schreiben mit http://sourceforge.net/projects/win32diskimager/ (Achtung unter Admin Ausführen!)

Pakete Aktualisieren

Auf den Raspberry connecten und dann erstmal Pakete aktualisieren:

[sourcecode language=“plain“]sudo apt-get update
sudo apt-get upgrade[/sourcecode]

Pakete Nachinstallieren

 

[sourcecode language=“plain“]
sudo apt-get install php5 acl[/sourcecode]

Bei Dauerbetrieb Zugriffe auf SD-Karte reduzieren

Log und Temp Dateien in den Memory auslagern:

[sourcecode language=“plain“]
sudo nano /etc/fstab[/sourcecode]

Folgendes in der fstab ändern bzw. hinzufügen:

[sourcecode language=“plain“]
none /var/run tmpfs size=5M,noatime 00
none /var/log tmpfs size=5M,noatime 00[/sourcecode]

Evtl Swapping deaktivieren, aber nur wenn genügend Speicher zur Verfügung steht:

[sourcecode language=“plain“]sudo dphys-swapfile swapoff
sudo dphys-swapfile uninstall
sudo update-rc.d dphys-swapfile remove[/sourcecode]

Freien Speicher kann man auch so anzeigen:

[sourcecode language=“plain“]free -m
[/sourcecode]

-m zeigt die Daten in Megabyte an -k in Kilobyte

Apache2 anpassen

Achtung: Apache läuft nicht, wenn das Log-Directory temporär ist, da apache den Ordner /var/log/apache2 erwartet. Dieser ist beim Start mit dieser Variante nicht mehr vorhanden.
Umgehen kann man das indem man den Ordner beim Starten anlegt.

Die schnellste aber auch nicht ganz saubere Methode ist das apache2 startupscript zu modifizieren:

[sourcecode language=“plain“]sudo nano /etc/init.d/apache2
[/sourcecode]

Direkt nach den Header Kommentaren folgende Zeile einfügen:

[sourcecode language=“plain“]mkdir /var/log/apache2
[/sourcecode]

 

PHP Secure Connection RSA / AES Javascript

Da SSL nicht immer verfügbar ist, und ggf. doch sicher Daten ausgetauscht werden müssen, habe ich mich dazu entschlossen eine PHP und JavasScript Klasse zu bauen, die genau dies tut:

  • Serverseitig Public und Private key (RSA) generieren.
  • Public Key an den Client schicken
  • Client erstellt einen gemeinsamen Schlüssel und sendet ihn per RSA Verschlüsselung an den Server.
  • Ab jetzt erfolgt die Kommunikation AES verschlüsselt.
  • Der Server kann AES verschlüsselte HTML-Daten (texte, Divs etc.) übermitteln, der Client empfängt diese und entschlüsselt sie.
  • Der Client kann GET Parameter und POST Formulare verschlüsselt and den Server senden
  • Key Renewal und Connection Ping eingebaut.

Auf PHP Server-Seite braucht man fast nichts tun – auf Client Seite ebenfalls nicht.

Beispiel einer PHP Date auf dem Server, die HTML Generiert und ausgibt, sowie Daten empfängt.

<?php
 require_once("secon/secon.cls.inc.php");
 $secon = new secon();
?>
<html>
<!-- BEGIN SECON INCLUDE -->
 <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
 <script type="text/javascript" src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/pbkdf2.js"></script> <!-- Used for Key Generation and Salting --->
 <script type="text/javascript" src="secon/js/jsbn.js"></script> <!-- BigInt etc. Java Helper Tools --->
 <script type="text/javascript" src="secon/js/rsa2.js"></script> <!-- RSA Encryption --->
 <script type="text/javascript" src="secon/js/sha1.js"></script> <!-- Used SHA1 Hashing --->
 <script type="text/javascript" src="secon/js/aes.js"></script> <!-- AES Ecnryption --->
 <script type="text/javascript" src="secon/js/storage.js"></script> <!-- Browser Window Storage of Variables and Content --->
 <script type="text/javascript" src="secon/js/secon.js"></script> <!-- Secure Connection Class --->
 
 <!-- Activate Secon for this page and assign handlers -->
 <script type="text/javascript">
 var secon = new secon(); // Init Secon
 $( document ).ready(function() { // When loading is finished do
 secon.Ping(); // Initiate a Ping to check if Server and Client are OK.
 secon.DeCryptContent('.seconCrypted'); // Run Decrypting HTML Content deliverd by server, specify Jquery selector
 secon.AttachForms(".secon"); // Attach Secon Handler for secure Form - Transfer
 secon.AttachLinks(".secon"); // Attach Secon Handler for secure GET (Link) Transfer
 });
 </script> 
 
<!-- END SECON INCLUDE ---> 
 
 <body>
 <?php
 echo "<div class='seconCrypted'>";
 echo $secon->CryptText('Dies ist ein geheimer Text')
 echo "</div>"; 
 ?>
 </body>
</html>

Im Debug Fenster des Clients (Browser z.B. Chrome oder Firefox, kann man sehen, dass die Daten vershlüsselt werden und dann übertragen werden.form

Formular verschlüsselt senden

Das Formular wird verschlüsselt abgeschickt indem es einfach die Klasse .secon bekommt.

<form method="post" class="secon">

Normalerweise würden bei PHP die Daten in $_POST stehen. Da wir verschlüsselt übertragen stehen diese Daten in gleicher Form jedoch in $secon->DATA zur verfügung.

Parameter von Links verschlüsselt senden

Es ist einfach möglich Links wie zb. http://myserver.de/mydat.php?secret=1234&type=mode Verschlüsselt an den Server zu übertragen. Dazu muss der <a href link einfach die klasse secon bekommen.

<a href="index.php?mode=test&secret=1234&irgendwas=2" class="secon">My Link</a>

Im empfangenden PHP Script auf dem Server stehen die Daten dann ebenfalls in $secon->DATA zur verfügung.

Daten vom Server verschlüsselt an den Client senden:

Auch wird es notwendig sein bestimmte Daten vom Server in verschlüsselter Form an den Client zu transferieren. Die Daten müssen in einen Container z.B. DIV gepackt werden, da der Client per Javascript JQUERY Selector dann die Daten entschlüsselt. In dem Container dürfen dann nur verschlüsselte Daten stehen!

<div class="seconCrypted">
<?php echo $secon->CryptText('<b>My very secure data here...</b>');?>
</div>

 

Auf dem Client kommen die Daten dann wie unten zu sehen an und werden erst dort entschlüsselt und visualisiert:response

Auf diese Art könnte nicht nur ein DIV, sondern sogar der ganze Body verschlüsselt übertragen werden.

Secon Komplettpaket (PHP, JS und DEMO) als ZIP

 

Raspberry GPIOs mit PHP schalten

Die einfachste Methode ist die, dass wir dem www-data User (unter welchem der Apache und auch die PHP Skripte laufen in die GPIO gruppe aufnehmen. Dabei ist zu beachten, dass dies ggf. ein Sicherheitsrisiko darstellt.

[sourcecode language=“plain“]sudo usermod -aG gpio www-data
sudo usermod -aG gpio pi
sudo /etc/init.d/apache2 restart[/sourcecode]

PHP Beispiel für REST mit Json response

[sourcecode language=“php“]<?php
// Valid GPIOS
$C["GPIOS"] = Array(17,22);

$mode = @$_GET["mode"];
switch($mode){
case "SWGPIO": $on = intval(@$_GET["on"]);
$id = intval(@$_GET["id"]);
SwitchGPIO($id, $on);
break;

case "GETGPIO": $id = intval(@$_GET["id"]);
GetGPIO($id);
break;
}

function GetGPIO($id){
global $C;
if(!in_array($id, $C["GPIOS"])){
Status(false, "GPIO $id not allowed.");
}
if(!is_dir("/sys/class/gpio/gpio$id/")){
Status(false,"GPIO $id not active.");
}
$fn = "/sys/class/gpio/gpio$id/value";
if(!file_exists($fn)){
Status(false, "GPIO $id Valuefile does not exist.");
}
$val = trim(file_get_contents($fn));
Status(true,$val);
}
function SwitchGPIO($id, $on){
// Vorher beim Booten Berechtigungen setzen!
// sudo usermod -aG sudo usermod -aG gpio www-data

global $C;
if(!in_array($id, $C["GPIOS"])){
Status(false, "GPIO $id not allowed.");
}
if(!in_array($on, Array(0,1))){
Status(false, "GPIO status $on not allowed.");
}
// check if GPIO is open
if(!is_dir("/sys/class/gpio/gpio$id/")){
file_put_contents("/sys/class/gpio/export",$id);
sleep(1);
file_put_contents("/sys/class/gpio/gpio$id/direction","out");
}
if(!is_dir("/sys/class/gpio/gpio$id/")){
Status(false,"Cannot access GPIO $id");
}

// Set correct status
$fn = ‚/sys/class/gpio/gpio‘.$id.’/value‘;
$val = file_get_contents($fn);
if($val == $on){
Status(true, "Switch status already $on");
}
file_put_contents($fn,$on);
usleep(250000);
// Check status
$val = file_get_contents($fn);
if($val == $on){
Status(true, "Switch status now: $on");
} else {
Status(false, "Switch status wrong");
}
}

Status(false,"Wrong Parameters");

function Status($stat, $txt){
$ret["STATUS"] = ($stat)? "OK" : "ERROR";
$ret["MSG"] = $txt;
echo json_encode ($ret);
die;
}

function warning_handler($errno, $errstr) {
echo $errno;
return false;
}
?>
[/sourcecode]

GPIOs sollten jedoch bereits beim Neustart des Raspberry initialisiert werden
dazu ein Script anlegen und ggf. anpassen: im pi home:

[sourcecode language=“plain“]nano ~/init_gpio[/sourcecode]

Inhalt:

[sourcecode language=“plain“]#!/bin/bash

#Init GPIOS on Startup with Script
#Ohne SUDO gehen die Befehle nur wenn pi Mitglied der Gruppe gpio ist.
echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction
echo "0" > /sys/class/gpio/gpio17/value[/sourcecode]

Script Berechtigungen:

[sourcecode language=“plain“]sudo chmod u+x ~/init_gpio[/sourcecode]

Autostart mit der crontab des pi

[sourcecode language=“plain“]crontab -e -u pi[/sourcecode]

letzte Zeile anfügen:

[sourcecode language=“plain“]@reboot ~/init_gpio[/sourcecode]

Prüfen ob alles OK:

[sourcecode language=“plain“]crontab -l -u pi[/sourcecode]