Raspberry telefoniert nach draussen

Aus Flipdot

Wechseln zu: Navigation, Suche

Beschreibung eines RasPi als sehr praktisches Gerät, das zwischen serieller Schnittstelle und Internet vermittelt. Eine Serveradresse + Parameter geht an den RasPi, vom RasPi an die serielle Schnittstelle zurück kommt die Serverantwort.


datei:Rasp_schema.jpg


Anwendung als Server hinter der Firewall, der Messdaten nach draussen überträgt und Benutzereingaben aus dem Internet zurück ins Intranet überträgt (Messen und steuern)


Seite des Servers im Internet mit Daten des AVR, die der RasPi aus dem Intranet überträgt
Auf dem Raspberry wird alle 30 sec. ein Uploadscript (serial_00_sec.php und serial_30_sec.php) ausgeführt. Dies
  • liest einen String mit url und GET Variablen von einem an den seriellen Port angeschlossenen uC ein
  • sendet diesen String an den in der url genannten Remote Server
  • holt die Antwort von dem Remote Server ab
  • und sendet diese an den uC zurück

Auf diesem Remote Server gibt es eine Verzeichnisstruktur:

/remote
index.html
        /control
        index.php
                /settings
                settings.txt
                change.php 
  • index.html ist die Datei, die der Benutzer aufruft, um die Werte zu sehen, die der Raspi bereitstellt
  • index.php ist die Datei, die der Raspi aufruft, um seine Werte an den Remote Server loszuwerden. Von index.php wird die index.html geschrieben
  • settings.txt ist die Datei, in der die Daten bereitgestellt werden, die sich der raspi vom Server draussen abholt und drinnen z.B. Lampen schaltet etc.
  • change.php dient dazu, die Datei settings.php zu ändern.

Die Staffelung in Verzeichnissen deswegen, damit /settings durch .htaccess und .htpasswd im Zugriff beschränkt werden kann.

Hier sind alle Dateien des Projekts in einem zip.


Inhaltsverzeichnis

Raspberry vorbereiten

Apache mit PHP installieren


Uploadscript auf dem RasPi: serial_00_sec.php

Liegt auf dem Raspberry im Apache Web-Verzeichnis /var/www und wird von einem Cronjob einmal in der Minute aufgerufen. Das Script fordert mit "?" von einem angeschlossenen uC einen String mit einer kompletten url und angehängten GET Parametern an. Dieser String wird über die serielle Schnittstelle eingelesen und zum entfernten Server im Internet übertragen. Anschliessend wird die Serverantwort abgeholt und mit "! [Serveranwort]" zum uC zurück übertragen.

Die serielle Übertragung hatte ich zuerst mit einem FTDI-Adapter am USB versucht, dabeiist der RasPi wiederholt abgestürzt. Es gibt bekannte Unstabilitäten mit dem FTDI. Deswegen benutze ich nun die serielle Schnittstlle an der Pfostenleiste, /dev/ttyAMA0.


<?php

//get system uptime as a short string ----------------------------------------------------------------------------------------
function uptime() {
if( $fd = @fopen('/proc/uptime', 'r')) {
        $ar_buf = split(' ', fgets($fd, 4096));
        fclose($fd);
        $sys_ticks = trim($ar_buf[0]);
        $min   = $sys_ticks / 60;
        $hours = $min / 60;
        $days  = floor($hours / 24);
        $hours = floor($hours - ($days * 24));
        $min   = floor($min - ($days * 60 * 24) - ($hours * 60));
        $result = $days."d,";
        $result .= $hours."h,";
        $result .= $min."m";
    } else
{
        $result = "no_uptime";
    }
    return $result;
}

// get system ticks ----------------------------------------------------------------------------------------------------------
// https://github.com/openenergymonitor/PHP-Emoncms-Serial-Link-/blob/master/serial.php
function microtime_float()
{
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
}


// set up serial connection on raspi within php ------------------------------------------------------------------------------
include "php_serial.class.php";
// start the class
  $serial = new phpSerial;
  $serial->deviceSet("/dev/ttyAMA0");
  //pi's serial port is used by kernel, edit some config files according to
  //http://www.irrational.net/2012/04/19/using-the-raspberry-pis-serial-port/
  $serial->confBaudRate(9600);
  $serial->confParity("none");
  $serial->confCharacterLength(8);
  $serial->confStopBits(1);
  $serial->confFlowControl("none");
  // Then we need to open it
  $serial->deviceOpen();

// send request via serial to uController connected to raspi via ttyAMA0 -----------------------------------------------------
  $serial->sendMessage("?\r\n");

// read data from uController within 5 seconds ------------------------------------------------------------------------------
// the string read will then be sent to the remote webserver
  $read = '';
  $theResult = '';
  $start = microtime_float();

  //5 second limit to read
  while ( ($read == '') && (microtime_float() <= $start + 5))
  {
    $read = $serial->readPort();
    if ($read != '')
    {
      $theResult .= $read;
      $read = '';
    }
  }

  if ($theResult == '')
  {
    $theResult = "http://mydomain.de/remote/control/index.php?error=1&";
  }
// send data from uController to remote webserver ----------------------------------------------------------------------------
  $uptime=uptime();
  //data from uC must be formatted to be sent via GET. for example"http://mydomain.de/remote/control/index.php?b7=1&b6=0&b5=1&b4=0&b3=1&b2=0&b1=1&b0=0&"
  $url="";
  $url = $theResult."&uptime=".$uptime;
  $remote_homepage = file_get_contents($url);
  $serial->sendMessage($remote_homepage."\r\n");
  $serial->deviceClose();

  exit();
?>


Cronjob

Cronjob eintragen, dazu crontab im editor öffnen:

crontab -e

dort eintragen:

* * * * * wget http://127.0.1.1/serial/serial_00_sec.php -O /dev/null
* * * * * wget http://127.0.1.1/serial/serial_30_sec.php -O /dev/null

Die Ausgaben von wget werden nach /dev/null verklappt. Das Script steht in zwei Varianten zur gleichen Ausführungszeit in crontab, die Kopie ist um den Befehl sleep(30) erweitert - so wird der Upload alle 30 sec ausgeführt.

Remote Script auf dem Server "draussen": index.php

<?php
// script receives several parameters from microcontroller via GET string
// is able to send email and to write received data to an target html file
// to be visited by the enduser
//
// A setting file is read by the script and transferred to the controller for back channel
// The user can modify this file via an webform

// set filename of target file to be displayed in browser
$strUploaddata = "../index.html";
$strSettings = "settings/settings.txt";

//transmit server time to raspi
//echo strftime("at time %H %M %S\r\n");

//read settings file and transmit it to raspi
$fp = fopen($strSettings, "r") or die ("Kann 'settings.txt' nicht lesen.");
fpassthru($fp);
fclose($fp);

//assign variables with pretty names
$raspi_uptime = $_GET['uptime'];
$analog_0 = (int)$_GET['ana0'];
$analog_1 = (int)$_GET['ana1'];
$analog_2 = (int)$_GET['ana2'];
$analog_3 = (int)$_GET['ana3'];
$analog_4 = (int)$_GET['ana4'];
$analog_5 = (int)$_GET['ana5'];
$analog_6 = (int)$_GET['ana6'];
$analog_7 = (int)$_GET['ana7'];

$temp_0 = (int)$_GET['temp0'];
$temp_1 = (int)$_GET['temp1'];
$temp_2 = (int)$_GET['temp2'];

$error = (int)$_GET['error'];

$port_a_0 = (int)$_GET['a0'];
$port_a_1 = (int)$_GET['a1'];
$port_a_2 = (int)$_GET['a2'];
$port_a_3 = (int)$_GET['a3'];
$port_a_4 = (int)$_GET['a4'];
$port_a_5 = (int)$_GET['a5'];
$port_a_6 = (int)$_GET['a6'];
$port_a_7 = (int)$_GET['a7'];

$port_c_0 = (int)$_GET['c0'];
$port_c_1 = (int)$_GET['c1'];
$port_c_2 = (int)$_GET['c2'];
$port_c_3 = (int)$_GET['c3'];
$port_c_4 = (int)$_GET['c4'];
$port_c_5 = (int)$_GET['c5'];
$port_c_6 = (int)$_GET['c6'];
$port_c_7 = (int)$_GET['c7'];

//check for mail to send
if ($_GET['mailto'] <> "" ) {
mail($_GET['mailto'],$_GET['mailsubj'],$_GET['mailmess'],'From: RasPi<noreply@mydomain.de>');
}

// to do: write error msg to logfile!

/* ####################### write raspi data to html file for user ####################### */

//write to target file, pointer is always at the first character in file
//use parameter "a" to append instead of "w" to overwrite
 $datei = fopen($strUploaddata,"w");

fwrite($datei, "<html><head><meta http-equiv='refresh' content='5'><meta http-equiv='pragma' content='no-cache'><body><font face=Courier>");
fwrite($datei, "<body bgcolor='#000000' text='#00FF00' >");

if ($error == 1) {
fwrite($datei, "<b>Fehler: Keine Antwort vom angeschlossenen uC!</b></b><br>\n");
}
fwrite($datei, "<b>RemoteBerry</b><br>\n<br>\n");
fwrite($datei, "Raspberry uptime:...".$raspi_uptime."<br>\n<br>\n");

$temp_0 = $temp_0 / 10;
$temp_0 = number_format($temp_0, 1, ',', '');
fwrite($datei, "Temp Bureau.........".$temp_0." C<br>\n");

$temp_1 = $temp_1 / 10;
$temp_1 = number_format($temp_1, 1, ',', '');
fwrite($datei, "Temp Raspi LAN IC...".$temp_1." C<br>\n");

$temp_2 = $temp_2 / 10;
$temp_2 = number_format($temp_2, 1, ',', '');
fwrite($datei, "Temp outside........".$temp_2." C<br>\n<br>\n");


$analog_0 = $analog_0 * 0.005;
$analog_0 = number_format($analog_0, 2, ',', '');
fwrite($datei, "Analog 0:...........".$analog_0." V<br>\n");

$analog_1 = $analog_1 * 0.005;
$analog_1 = number_format($analog_1, 2, ',', '');
fwrite($datei, "Analog 1:...........".$analog_1." V<br>\n");

$analog_2 = $analog_2 * 0.005;
$analog_2 = number_format($analog_2, 2, ',', '');
fwrite($datei, "Analog 2:...........".$analog_2." V<br>\n");

$analog_3 = $analog_3 * 0.005;
$analog_3 = number_format($analog_3, 2, ',', '');
fwrite($datei, "Analog 3:...........".$analog_3." V<br>\n");

$analog_4 = $analog_4 * 0.005;
$analog_4 = number_format($analog_4, 2, ',', '');
fwrite($datei, "Analog 4:...........".$analog_4." V<br>\n");

$analog_5 = $analog_5 * 0.005;
$analog_5 = number_format($analog_5, 2, ',', '');
fwrite($datei, "Analog 5:...........".$analog_5." V<br>\n");

$analog_6 = $analog_6 * 0.005;
$analog_6 = number_format($analog_6, 2, ',', '');
fwrite($datei, "Analog 6:...........".$analog_6." V<br>\n");

$analog_7 = $analog_7 * 0.005;
$analog_7 = number_format($analog_7, 2, ',', '');
fwrite($datei, "Analog 7:...........".$analog_7." V<br>\n<br>\n");

fwrite($datei, "Port A .............76543210"."<br>\n");
fwrite($datei, "digital in:.........".$port_a_7.$port_a_6.$port_a_5.$port_a_4.$port_a_3.$port_a_2.$port_a_1.$port_a_0."<br>\n<br>\n");

fwrite($datei, "Port C .............76543210"."<br>\n");
fwrite($datei, "digital out:........".$port_c_7.$port_c_6.$port_c_5.$port_c_4.$port_c_3.$port_c_2.$port_c_1.$port_c_0."<br>\n<br>\n");

//display icons for port values
//fwrite($datei, "<img src='".$port_b_7."x.png' ><img src='".$port_b_6."x.png' ><img src='".$port_b_5."x.png' ><img src='".$port_b_4."x.png' ><img src='".$port_b_3."x.png' ><img src='".$port_b_2."x.png' ><img src='".$port_b_1."x.png' ><img src='".$port_b_0."x.png' > <br>\n");

// show last modification of file ---------------------------------------------
$filename = $strUploaddata;
if (file_exists($filename)) {
 fwrite($datei,  "last modified:......" . date ("d.m.Y H:i:s.", filemtime($filename))."<br>\n" );
}
fwrite($datei, "Auto reload 5 sec.  <a href='/remote/control/settings/change.php'>ControlBerry</a><br>\n");

fwrite($datei, "</font></body></head></html><br>\n");
 fclose($datei);
?>


Settings script auf dem Server draussen: change.php

Dient dazu, die Datei settings.txt zu bescheiben, die der RasPi nach erfolgreichem Upload seiner lokalen Werte zurückbekommt. Die empfangenen Werte = Befehlszeile werden dann an einen an den Raspi angeschlossenen Mikrocontroller übertragen.

<?php

// set filename of settings file wich is transferred from server to arduino
$strSettings = "settings.txt";


//write to target file, pointer is always at the first character in file
//use parameter "a" to append instead of "w" to overwrite

if ($_GET['commands'] <> "") {
  $datei = fopen($strSettings,"w");
  fwrite($datei, $_GET['commands']);
  fclose($datei);
  }
?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
    <head>
        <title>Settings</title>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    </head>
    <body>
        <h3>Control Controller</h3>
        <form action="<?php print $_SERVER['PHP_SELF']; ?>" method="get">
            <textarea name="commands" cols="40" rows="5"><?php
                //read settings file and display
                $fp = @fopen($strSettings, "r") or die ("Kann 'settings.txt' nicht lesen.");
                fpassthru($fp);
                fclose($fp)
                ?></textarea>
            <p>
            <input type="submit" value="Senden" />&nbsp &nbsp <a href="../../index.html">See result page</a>
            </p>
            <p><h3>Actual content of settings file:</h3></p>
                <p>
                <?php
                //read settings file and display

                $output="";
                $file = fopen($strSettings, "r");
                while(!feof($file)) {
                  //read file line by line into variable
                          $output = $output . fgets($file, 4096);
                        }
                fclose ($file);
                echo nl2br ($output);
                ?>
                </p>
        </form>
    </body>
</html>


Links

Benutzung des seriellen Ports mit PHP Referenz zur php-serial class