Anleitung zum schalten von GPIO

  • Da hier immer wieder Probleme / Fragen bezüglich des schaltens von GPIO's auftauchen und ich es langsam leid bin das immer wieder zu wiederholen sowie zu begründen wieso shell_exec(); Mist ist, verfasse ich hier mal eine kurze Zusammenfassung und Erläuterung wie es richtig geht.

    Ich zeige euch hier auch Anwendungsbeispiele sowie Code. Ich schneide hier aber auch nur einige hierfür relevante Grundlagen kurz an - die allgemeinen Grundlagen sind aber selbstverständlich umfassender..


    Fragen, Anregungen usw bitte im Diskussions-Thread stellen! :danke_ATDE:

    UnderConstruction.jpg
    Under Construction


    [an=Index][/an]Index:

    • [al=Intro]Einführung[/al]
    • [al=Options]Möglichkeiten[/al]

      • [al=FirstDescription]Beschreibung 1.Möglichkeit[/al]
      • [al=SecondDescription]Beschreibung 2.Möglichkeit[/al]
    • [al=WebsiteBasics]Grundlage einer gültigen Webseite[/al]
    • [al=PHPBasics]Grundlagen PHP[/al]
    • [al=FirstCode]Code zur 1.Möglichkeit[/al]
    • [al=AdvancedFirstCode]Fortgeschrittener Code zur 1.Möglichkeit[/al]
    • [al=SecondCode]Code zur 2.Möglichkeit[/al]
    • [al=AdvancedSecondCode]Fortgeschrittener Code zur 2.Möglichkeit[/al]
    • [al=][/al]
    • [al=][/al]
    • [al=Changelog]Changelog[/al]


    [an=Intro][/an]Einführung:

    Ein Webserver läuft i.d.R. über einen bestimmten Benutzer: www-data
    Dieser Benutzer hat Standardmäßig keine Rechte die Hardware zu beeinflussen. Auch haben normale Benutzer allgemein kein Recht auf andere Benutzerverzeichnisse zuzugreifen.
    Generell ist es so das normale Benutzer nicht auf die Verzeichnisse anderer Benutzer zugreifen dürfen - das wäre unsicher und uncool. Der www-data Benutzer sieht also nicht die Dateien in /home/pi/ da er darauf nicht zugreifen kann.


    Auch ist wichtig das der Webserver das Recht besitzt die Dateien zu lesen, evtl. sogar in seinem DocumentRoot Verzeichnis zu schreiben. Allgemein erleichtert es vieles wenn einfach alle Dateien und Verzeichnisse im DocumentRoot Verzeichnis dem Webserver Benutzer gehören.
    DocumentRoot ist eine Einstellung von apache2: Das root-Verzeichnis ab dem die Dateien und Verzeichnisse über den Webserver ansprechbar sind. Nicht zu verwechseln mit dem Benutzerverzeichnis von root.
    Seit Jessie ist DocumentRoot /var/www/html/ vorher war es noch /var/www/. Wenn ihr also zum Beispiel http://raspberrypi/index.html aufrufen möchtet dann muss die Datei /var/www/html/index.html existieren. Wenn ihr einen Ordner /var/www/html/bla/ erstellt könnt ihr diesen über http://raspberrypi/bla/ ansurfen.
    Bezüglich Besitzerrechte siehe:
    https://wiki.ubuntuusers.de/Rechte/
    https://wiki.ubuntuusers.de/chown/
    Es reicht i.d.R. das der Webserver Lese-Rechte besitzt.
    Ausführ-Rechte wie sie oft in irgendwelchen Blogs beschrieben werden sind überhaupt nicht nötig und machen auch gar kein Sinn, da die Dateien dem Interpreter direkt übergeben werden. "chmod 755" oder höher sind also nur Bequemlichkeit aber unnötig!
    In der Regel reicht es nach erstellen/hochladen eurer Dateien nur den Besitzer auf www-data zu ändern:

    Code
    sudo chown -R www-data:www-data /var/www/html/


    Das " -R " sorgt dafür das auch Unterverzeichnisse betroffen sind: Recursive. www-data:www-data ... das erste ist Benutzer das zweite ist Gruppe. Da steht aber auch alles im bereits erwähnten Link bezüglich chown noch mal beschrieben.


    Bei Raspbian ist es so das der Benutzer pi bereits Mitglied in einer bestimmten Gruppe ist um die GPIO's beeinflussen zu können (also nicht nur auszulesen sondern auch zu setzen). Diese System-Gruppe heißt: gpio
    Desweiteren darf der Benutzer pi ohne Eingabe eines Passwords und uneingeschränkt sudo verwenden.

    Diese besonderen Umstände gelten aus Sicherheitsgründen nicht für den Benutzer www-data und das ist i.d.R. auch gut so.

    Wenn ihr euren PI ausschließlich (wirklich!) nur in eurem Lokalen Netzwerk (LAN) verwenden wollt und diesen auf gar keinen Fall übers Internet zugänglich macht, dann besteht für euch kein so großes Sicherheitsproblem.
    Aber die Erfahrung hat gezeigt das gerade Anfänger doch irgendwie den PI übers Internet ansprechen wollen, oder später nicht mehr an dieses Sicherheitsproblem denken und in ein paar Wochen/Monaten dann doch eine Portweiterleitung einrichten.... Ich möchte hier jetzt aber nicht wirklich ausführlicher über Sicherheitskonzepte quatschen, das würde denk ich den Rahmen sprengen.

    Deshalb an dieser Stelle nur so viel: Es ist eine sehr sehr schlechte Idee dem www-data Benutzer uneingeschränkten Zugriff via sudo zu geben! :stumm:Macht das bitte auf gar keinen Fall!
    Wie es eingeschränkt dennoch möglich ist beschreibe ich weiter unten. (sudo webscript)


    Standardmäßig ist es auch so das der Webserver, zum Beispiel apache2, eine Logdatei für Fehler hat. Wenn ihr also Probleme in irgendeiner Form habt dann prüft diese Logdatei: /var/log/apache2/error.log


    [an=Changelog][/an]Changelog

  • [an=Options][/an]Möglichkeiten:

    Es gibt mehrere Möglichkeiten die GPIO's zu verwenden. Ich rate euch an dieser Stelle aber davon ab wiringPi oder allgemein Konsolen Befehl wie gpio zu nutzen da es mehr CPU-Last erzeugt und langsamer sind als der Weg den ich euch hier zeigen werde. Auch wenn ihr PWM oder ähnliches nutzen wollt solltet ihr das lieber über Python Scripte und dem dortigen Module RPi.GPIO umsetzen. Generell sollte man mit PHP so wenig wie möglich Konsolen Befehle verwenden - macht so gut es geht nativ/direkt in PHP.

    Es gibt auch verschiedene Ebenen um die GPIO's zu nutzen. Konsolen Programme wie gpio nutzen i.d.R. Mechanismen um direkte mit dem Kernel bzw der Hardware zu sprechen. Es gibt aber auch noch eine Möglichkeit auf Dateisystem-Ebene, nämlich die virtuellen vom Kernel erzeugten/kontrollierten Dateien in /sys/ (sysfs) und für GPIO zuständig sind die Dateien im Verzeichnis /sys/class/gpio/


    Ich gehe hier auf 2 Möglichkeiten ein, dem www-data Benutzer Zugriff auf GPIO zu geben:

    • Den Benutzer in die Systemgruppe gpio aufnehmen
    • Ein extra bash Script erzeugen welches als einziges über sudo ausgeführt werden kann


    Das erste reicht völlig um nur die GPIO's als normale Ein/Ausgänge zu verwenden. Mehr geht damit aber nicht.

    Das zweite erweitert die Möglichkeiten sodass man zum Beispiel auch den PI über die Webseite rebooten könnte. Hierbei ist aber extrem wichtig das dem www-data Benutzer ausschließlich das ausführen dieses einen Scripts erlaubt wird!
    Und die meisten PI Nutzer wollen oftmals auch root Rechte verwenden können, dann wäre dies die sicherste Lösung.


    [an=FirstDescription][/an]Beschreibung zur 1.Möglichkeit:

    Den Benutzer www-data kann man mit folgendem Befehl zur Gruppe gpio hinzufügen:

    Code
    sudo usermod -G gpio -a www-data

    Anschließend muss man den Webserver noch neu starten:

    Code
    /etc/init.d/apache2 restart

    Nun hat www-data vollen Zugriff auf die virtuellen Dateien in /sys/class/gpio/

    Die Dateien entsprechen der Broadcom GPIO Nummerierung (GPIO.BCM), nicht der physikalischen Pin Nummerierung!


    Wichtig:

    Seit Einführung eines neuen Kernels (ab 4.1.6) und dem damit verbundenen DeviceTree, gibt es leider Probleme mit der Rechtevergabe für die /sys/class/gpio/ Dateien.
    Eine Lösung dieses Problems ist aber relativ einfach, und zwar braucht ihr hierfür nur folgende Datei in UDEV einbinden:

    Code
    sudo nano /etc/udev/rules.d/80-gpio-noroot.rules
    Code
    # /etc/udev/rules.d/80-gpio-noroot.rules
    # Zugriff auf GPIO ohne root-Rechte ermoeglichen
    #
    # Gruppe aendern
    SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chown -R root:gpio /sys/devices/platform/soc/*.gpio/gpio'"
    # Zugriffsrechte setzen
    SUBSYSTEM=="gpio", PROGRAM="/bin/sh -c '/bin/chmod -R ug+rw /sys/devices/platform/soc/*.gpio/gpio'"


    [an=SecondDescription][/an]Beschreibung zur 2.Möglichkeit:

    Siehe dazu bitte den bereits älteren Beitrag FAQ --> Nützliche Links / Linksammlung --> Befehle über PHP mit root Rechten ausführen (sudo webscript)


    - > [al=Index]Index[/al] < -

  • [an=WebsiteBasics][/an]Grundlage einer gültigen Webseite:

    Jede Webseite hat eigentlich den gleichen Aufbau. Am Anfang muss das HTML Dokument geöffnet und am Ende wieder geschlossen werden. Oben befindet sich zudem der Header indem Informationen, Einschränkunge, CSS oder JavaScript's eingefügt werden. Danach öffnet man den sog. Body indem die Ausgaben bzw Text für den Betrachter eingefügt werden.

    In PHP Dateien kann man problemlos HTML Code verwenden. Damit eine Datei als PHP Datei verarbeitet wird muss diese aber als Dateiendung *.php haben (oder *.php5 usw, das wird in der Konfiguration des Webservers definiert/erweitert). Es würde also nicht funktionieren wenn die Datei zum Beispiel index.html heißen würde, dann würde kein PHP Code verarbeitet werden.

    Ein Grundgerüst könnte also wie folgt aussehen:
    [code=php]
    <!DOCTYPE html>
    <html>
    <head>
    <title>GPIO test</title>
    </head>
    <body>

    <?php

    echo "PHP Test";

    ?>

    </body>
    </html>
    [/php]

    Ihr seht hier außerdem das der PHP Code in einem bestimmten Bereich stehen muss, eingeleitet mit <?php und wieder geschlossen mit ?>
    Abgekürzt ginge auch <? und ?>

    In den unteren Code Beispielen verzichte ich auf dieses Grundgerüst und gehe mal davon aus dass das soweit verstanden wurde ;)

    Ein Hinweis an dieser Stelle noch: Mithilfe von PHP kann man Webseiten dynamisch erzeugen. Das PHP Script läuft Serverseitig und nur die mögliche Ausgabe kriegt der Client zu sehen.
    Auch werden diese Dateien von oben nach unten abgearbeitet, ebenso wie bash oder python Scripts (und eigentlich alles).


    - > [al=Index]Index[/al] < -

  • [an=PHPBasics][/an]Grundlagen PHP:

    Genau so wie in Python oder Bash kann man auch in PHP eigene Befehle bzw Funktionen definieren. Das sollte man insbesondere dann machen wenn man ständig wiederholenden Code hat. Beispielsweise das prüfen ob ein GPIO initialisiert wurde oder auslesen des Wertes, oder das erstellen von mehreren Buttons für jeden GPIO usw. Man definiert dann am besten nur eine Funktion die man dann ggf mehrmals aufruft, aber wenn man etwas ändern will brauch man nur den Code in der Funktion selbst verändern. Das machts also nicht nur leichter sondern auch übersichtlicher.

    Wo in der Datei diese Funktion definiert wird spielt keine Rolle. Anders als bei bash oder python wird bei PHP nämlich erst die ganze Datei in den Ram geladen, dabei überprüft und erst dann ausgeführt. Ihr könnt also problemlos die Funktionen ganz ans Ende der Datei packen und weiter oben aufrufen.

    Wie bereits beschrieben werden auch PHP Dateien von oben nach unten verarbeitet. Ändert man einen Wert eines GPIO's sollte man also möglichst weit am Anfang der Datei das auslesen des aktuellen Wertes vornehmen, damit weiter unten eine Ausgabe (ob zB eine am GPIO angeschlossene LED an ist oder nicht) auch tatsächlich aktuell ist.

    Übrigens können auch in PHP Dateien Kommentare stehen und es hilft ungemein auch welche zu nutzen/setzen. ;)

    Buttons (Schaltflächen, besser als input) müssen innerhalb eines Formulars definiert werden. Beim öffnen dieses Formulars gibt man eine sog. method an, welche festlegt wie Daten beim absenden des Formulars übermittelt werden sollen.
    Drückt man einen Button auf der Webseite gibt es mehrere Möglichkeiten einen Wert an das PHP Script zu übergeben, die Gängigsten sind $_GET und $_POST welche zu den Superglobals PHP Variablen gehören. Es gäbe auch noch $_REQUEST aber ich nutze lieber die ersten beiden. $_REQUEST zu nutzen ist zwar einfach aber sehr unsicher da es von einem Angreifer leichter manipuliert werden kann als es bei $_POST der Fall ist. Da man den Code selber schreibt legt man sich dabei auch selbst auf POST oder GET fest und sollte dann auch den jeweiligen Pendanten dazu direkt verwenden.
    Diese Variablen sind sog. Array's

    GET wird an die URL angehängt, für den ersten Wert mit einem ? und für alle weiteren Werte mithilfe eines & ... Wenn also zum Beispiel die PHP Datei index.php heißt und man im Formular die method GET festgelegt hat sowie der erste Button den name "AN" mit dem value "4" trägt, wird beim drücken auf den Knopf die URL geändert auf: index.php?AN=4
    Diese Methode birgt gewisse Risiken da es einfacher ist unerwünschte Aktionen auszuführen, und sieht auch nicht so schön aus.

    POST wird intern an die Datei übermittelt und ist nicht "sichtbar" wie bei GET. Diese Methode sollte bevorzugt werden da sie nicht nur schöner sondern weitaus sicherer ist.

    Wie man diese Daten verarbeitet erklär ich im nächsten Absatz:

    Nun ist es so das man generell erst mal prüfen sollte ob überhaupt POST oder GET Daten übermittelt wurden... Allgemein ist es zu empfehlen PHP Variablen, wo es nicht 100% klar ist ob diese auch wirklich zu diesem Zeitpunkt existieren, zu prüfen ob diese gesetzt sind.
    Wenn also die Webseite zB. das erste mal geladen wird, können auch noch keine GET oder POST Daten vorhanden sein, also wäre es fatal diese verarbeiten zu wollen und würde folglich Fehlermeldungen hageln.
    Deshalb prüft man zunächst ob die gewünschten Schlüssel (key) des Arrays vorhanden sind - wie gesagt wäre $_GET ein Array ebenso wie $_POST, der Schlüssel ist sowas wie ein Index in den Eckklammern und die Kombination aus Array+Schlüssel ergibt den Wert (value) welcher dadrin gespeichert ist.
    So ein Array sähe für obiges AN=4 Beispiel wie folgt aus:
    [code=php]$_GET["AN"] = 4;
    $_POST["AN"] = 4;[/php]

    Man prüft also erst mal ob der Key "AN" existiert bzw gesetzt ist, und am besten auch gleich ob der Inhalt nicht leer ist:
    [code=php]if (isset($_GET["AN"]) AND !empty($_GET["AN"])) {
    ...
    }
    // oder:
    if (isset($_POST["AN"]) AND !empty($_POST["AN"])) {
    ...
    }[/php]Ein Ausrufezeichen ( ! ) steht (wie auch in bash) für: NOT , also NICHT. Wenn also $_GET["AN"] gesetzt ist UND $_GET["AN"] NICHT leer; dann...

    Wenn ihr Probleme habt und nicht wisst ob eine der Übergaben funktioniert, gibt es einen einfachen Trick sich die Daten auf der Webseite anzeigen zu lassen. Einfach irgendwo am Anfang des PHP-Teils folgendes einfügen:
    [code=php]
    echo "<pre>"; var_dump($_GET); echo "</pre>";

    echo "<pre>"; var_dump($_POST); echo "</pre>";
    [/php]


    - > [al=Index]Index[/al] < -

  • [an=FirstCode][/an]Code zur 1.Möglichkeit:

    Ist der Benutzer www-data in der Systemgruppe gpio hat er uneingeschränkten Zugriff auf die Dateien in /sys/class/gpio/

    Das beste wäre nun am Anfang des Scripts erst zu prüfen ob die benötigten GPIO's initialisiert wurden, denn es macht kein Sinn diese stumpf jedes mal erneut zu initialisieren und würde zudem auch ausbremsen.
    Allgemein lässt sich das (wie auch in bash) am einfachsten bewerkstelligen indem man prüft ob eine Datei des GPIO's bereits existiert. Wenn der GPIO nämlich noch nicht aktiviert (export) wurde sind auch keine Dateien von diesem GPIO verfügbar.

    Also angenommen ihr wollt GPIO#17 (pin#11) als Ausgang verwenden, dann sähe diese Prüfung bzw Initialisierung wie folgt aus:
    [code=php]
    if (!file_exists("/sys/class/gpio/gpio17")) {
    file_put_contents("/sys/class/gpio/export", "17");
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio17/direction", "out");
    }
    [/php]
    file_exists prüft auf sowohl Datei als auch Verzeichnis. Desweiteren ist PHP derart schnell das man einen kurzen Moment warten muss bevor man auf die 'direction' Datei zugreifen kann, sonst existiert die Datei noch nicht und folglich kann man den Wert des GPIO's noch nicht verändern... Aber wie gesagt wird dieser Teil nur ein mal ausgeführt auch wenn man die Webseite refreshed.

    Eine weitere Möglichkeit wäre anstatt file_put_contents zu verwenden, das klassische fopen:
    [code=php]
    if (!file_exists("/sys/class/gpio/gpio17")) {
    $handle = @fopen("/sys/class/gpio/export", 'a');
    @fwrite($handle, "17");
    @fclose($handle);
    $handle = @fopen("/sys/class/gpio/gpio17/direction", 'a');
    @fwrite($handle, "out");
    @fclose($handle);
    }
    [/php]Das @ unterdrückt mögliche Rückgaben.

    Danach solltet ihr zunächst auf eine GET oder POST Variable prüfen sofern ihr den GPIO über die Webseite auch verändern können wollt. Siehe dazu am Ende vom [al=PHPBasics]Grundlagen PHP[/al] Abschnitt.
    Wie in dem [an=PHPBasics]Grundlagen PHP[/an] Abschnitt beschrieben sähe sowas wie folgt aus:
    [code=php]
    if (isset($_POST["17an"]) AND !empty($_POST["17an"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "1");
    } elseif (isset($_POST["17aus"]) AND !empty($_POST["17aus"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "0");
    }
    [/php]Würde zum Beispiel $_POST["17an"] übergeben würde der GPIO auf HIGH gestellt werden.

    Danach könnt ihr dann den aktuellen Wert wie folgt auslesen:
    [code=php]$GPIO17 = trim(file_get_contents("/sys/class/gpio/gpio17/value"));[/php]
    Der trim Befehl schneidet eventuell am Anfang oder am Ende stehende Leerzeichen oder Sonderzeichen (zB Zeilenumbruch) weg.
    Nun beinhaltet die Variable $GPIO17 den aktuellen Wert des GPIO#17. Den könnt ihr dann an beliebiger Stelle überprüfen und wenn der Wert zum Beispiel 0 ist echo'd ihr "LED ist Aus" oder wenn der Wert auf 1 steht "LED ist An"...

    Wie gesagt könnte man sich den aktuellen Status des GPIO's auch anzeigen lassen - das geht auch vor dem weiter unten gezeigten Formular oder innerhalb dessen oder zB auch indem man den Button farblich verändert usw, die Möglichkeiten sind hier quasi unendlich ;)

    Es gibt in PHP 2 Möglichkeiten HTML Code auszugeben.
    Die erste wäre den PHP-Code Abschnitt zu schließen und nur an den Stellen wo man PHP-Variablen benötigt kurzfristig zu öffnen...
    Oder die zweite wäre einfach mithilfe des echo Befehls den HTML Code auszugeben. Ich persönlich bevorzuge letzteres, aber ich beschreibe trotzdem beides.
    1.
    [code=php]
    if ($GPIO17 == 0) {
    ?>
    <b>LED ist Aus</b><br/>
    <?php
    } elseif ($GPIO17 == 1) {
    ?>
    <b>LED ist An</b><br/>
    <?php
    }
    [/php]

    2.
    [code=php]
    if ($GPIO17 == 0) {
    echo "<b>LED ist Aus</b><br/>";
    } elseif ($GPIO17 == 1) {
    echo "<b>LED ist An</b><br/>";
    }
    [/php]

    Wie bereits erwähnt wird PHP Serverseitig ausgeführt. Bei der ersten Möglichkeit wird also erst geprüft ob die Bedingung passt und erst dann der HTML Code bzw das innerhalb der Bedingung ausgegeben. Funktioniert, sieht aber doof aus und ist auch schwerer zu lesen :fies:

    An dieser Stelle sollte ich aber auch erwähnen das es noch eine weitere Möglichkeit gäbe und vielleicht sogar noch mehr, aber ich kann/möchte hier nicht alle eventuallitäten abdecken, weil ihr merkt vielleicht das es jetzt schon recht viel ist, obwohl ich versuche mich kurz zu fassen....

    Jetzt fehlt noch das Formular für die Schaltflächen um den GPIO entsprechen schalten zu können. Allerdings können wir hier keine Buttons mit type=submit nutzen, da man nicht mehr als ein submit button pro Formular haben haben kann. Macht aber nichts... Ich zeig euch später im [al=AdvancedFirstCode]Fortgeschrittener Code zur 1.Möglichkeit[/al] Abschnitt eine schickere Lösung ;)
    [code=php]
    ?>
    <form method="POST" name="GPIOform" action="">
    <input type="submit" name="17aus" value="LED aus"/>
    <input type="submit" name="17an" value="LED an"/>
    </form>
    <?php
    [/php]

    Und auch hier noch ein Hinweis: Es ist immer besser " if elseif " usw zu verwenden wenn nur einer der beiden Zustände passen kann. Dadurch wird der Code schneller abgearbeitet, da ja nur einer der Bedingungen zutreffen kann und somit die anderen innerhalb dieser Schleife/Kontrollstruktur nicht verarbeitet werden sobald eine übereinstimmt. Natürlich könnte in diesem Fall aber auch nur "else" genutzt werden, da es ja nur 0 oder 1 sein kann..

    Das vollständige Konstrukt sähe dann also wie folgt aus:

    &quot;1.&quot;

    [code=php]
    if (!file_exists("/sys/class/gpio/gpio17")) {
    file_put_contents("/sys/class/gpio/export", "17");
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio17/direction", "out");
    }

    if (isset($_POST["17an"]) AND !empty($_POST["17an"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "1");
    } elseif (isset($_POST["17aus"]) AND !empty($_POST["17aus"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "0");
    }

    $GPIO17 = trim(file_get_contents("/sys/class/gpio/gpio17/value"));

    if ($GPIO17 == 0) {
    ?>
    <b>LED ist Aus</b><br/>
    <?php
    } elseif ($GPIO17 == 1) {
    ?>
    <b>LED ist An</b><br/>
    <?php
    }

    ?>
    <form method="POST" name="GPIOform" action="">
    <input type="submit" name="17aus" value="LED aus"/>
    <input type="submit" name="17an" value="LED an"/>
    </form>
    [/php]

    &quot;2.&quot;

    [code=php]
    if (!file_exists("/sys/class/gpio/gpio17")) {
    file_put_contents("/sys/class/gpio/export", "17");
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio17/direction", "out");
    }

    if (isset($_POST["17an"]) AND !empty($_POST["17an"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "1");
    } elseif (isset($_POST["17aus"]) AND !empty($_POST["17aus"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "0");
    }

    $GPIO17 = trim(file_get_contents("/sys/class/gpio/gpio17/value"));

    if ($GPIO17 == 0) {
    echo "<b>LED ist Aus</b><br/>";
    } elseif ($GPIO17 == 1) {
    echo "<b>LED ist An</b><br/>";
    }

    ?>
    <form method="POST" name="GPIOform" action="">
    <input type="submit" name="17aus" value="LED aus"/>
    <input type="submit" name="17an" value="LED an"/>
    </form>
    [/php]

    Soweit wäre dieser Abschnitt dann fertig :cool: Einen ausführlicheren/verbesserten Code findet ihr im Abschnitt [al=AdvancedFirstCode]Fortgeschrittener Code zur 1.Möglichkeit[/al]

    Die aus meiner Sicht beste Möglichkeit der 2 hier vorgestellten, inkl. Grundgerüst:

    Spoiler anzeigen

    [code=php]
    <!DOCTYPE html>
    <html>
    <head>
    <title>GPIO test</title>
    </head>
    <body>

    <?php
    if (!file_exists("/sys/class/gpio/gpio17")) {
    file_put_contents("/sys/class/gpio/export", "17");
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio17/direction", "out");
    }

    if (isset($_POST["17an"]) AND !empty($_POST["17an"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "1");
    } elseif (isset($_POST["17aus"]) AND !empty($_POST["17aus"])) {
    file_put_contents("/sys/class/gpio/gpio17/value", "0");
    }

    $GPIO17 = trim(file_get_contents("/sys/class/gpio/gpio17/value"));

    if ($GPIO17 == 0) {
    echo "<b>LED ist Aus</b><br/>";
    } elseif ($GPIO17 == 1) {
    echo "<b>LED ist An</b><br/>";
    }
    ?>

    <form method="POST" name="GPIOform" action="">
    <input type="submit" name="17aus" value="LED aus"/>
    <input type="submit" name="17an" value="LED an"/>
    </form>

    </body>
    </html>
    [/php]


    - > [al=Index]Index[/al] < -

  • [an=AdvancedFirstCode][/an]Fortgeschrittener Code zur 1.Möglichkeit:

    Wenn ihr mehr als nur einen GPIO über eine Webseite schalten / abfragen wollt ist es Sinnvoll eine Funktion zu definieren die ihr dann nur noch mehrfach aufrufen braucht.

    Solche Funktionen könnten wie folgt aussehen:
    [code=php]
    function GetGPIOpinDirection($PIN) {
    $Direction='';
    if (file_exists("/sys/class/gpio/gpio".$PIN."")) {
    $Direction = trim(file_get_contents("/sys/class/gpio/gpio".$PIN."/direction"));
    }
    return $Direction;
    }

    function GetGPIOpinValue($PIN) {
    $Value='';
    if (file_exists("/sys/class/gpio/gpio".$PIN."")) {
    $Value = trim(file_get_contents("/sys/class/gpio/gpio".$PIN."/value"));
    }
    return $Value;
    }
    [/php]
    Die erste Funktion gibt die aktuelle Richtung zurück/aus. Wenn ihr sowieso nur OUT nutzt könnt ihr diese eigentlich auch weg lassen, könnte aber auch zur Sicherheit nicht schaden um zu verifizieren das die Initialisierung erfolgreich/korrekt war.
    Die zweite Funktion gibt den aktuellen Wert aus.

    Eine weitere Möglichkeit (und davon gibt es quasi unendlich viele) wäre zum Beispiel auch:

    Spoiler anzeigen

    [code=php]
    function gpio_wert($PIN) {
    if (file_exists("/sys/class/gpio/gpio".$PIN."/value")) {
    $status = file_get_contents("/sys/class/gpio/gpio".$PIN."/value");
    return $status;
    } else {
    return "GPIO nicht aktiviert.";
    }
    }
    [/php]

    Der Aufruf einer solchen Funktion sieht wie folgt aus:
    [code=php]
    $GPIO17 = GetGPIOpinValue("17");
    [/php]


    Gehen wir mal davon aus, ihr wollt 5 verschiedene GPIOs über die Webseite bedienen können: 17, 27, 22, 23 und 24.

    Nun könntet ihr natürlich für jeden einzelnen GPIO die im [al=FirstCode]Code zur 1.Möglichkeit[/al] Abschnitt genannte Initialisierung einfügen, was aber nicht sehr schick wäre 5x nur leicht veränderte Zeilen da stehen zu haben... Besser wäre hierfür eine Funktion zu verwenden:
    [code=php]
    function init_GPIO($pin, $direction) {
    if (!file_exists("/sys/class/gpio/gpio".$pin."")) {
    file_put_contents("/sys/class/gpio/export", $pin);
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio".$pin."/direction", $direction);
    }
    }
    [/php]Dieser Funktion übergeben wir auch gleich die Richtung die gesetzt werden soll. So brauch man nicht mehrere Funktionen einbauen nur um GPIO's auch als Eingang verwenden zu können.
    Der Aufruf könnte dann wie folgt aussehen:
    [code=php]
    init_GPIO("17", "out");
    init_GPIO("27", "out");
    init_GPIO("22", "in");
    init_GPIO("23", "in");
    init_GPIO("24", "out");
    [/php]

    Wie auch schon in [al=FirstCode]Code zur 1.Möglichkeit[/al] kurz erwähnt, gäbe es hier aber natürlich auch die Möglichkeit das mithilfe von fopen zu regeln:

    Spoiler anzeigen

    [code=php]
    function init_gpio($pin, $direction) {
    $handle = @fopen("/sys/class/gpio/export", 'a');
    @fwrite($handle, $pin);
    @fclose($handle);
    $handle = @fopen("/sys/class/gpio/gpio".$pin."/direction", 'a');
    @fwrite($handle, $direction);
    @fclose($handle);
    }

    function einschalten($pin) {
    $handle = @fopen("/sys/class/gpio/gpio".$pin."/value", 'a');
    @fwrite($handle, "1");
    @fclose($handle);
    }

    function ausschalten($pin) {
    $handle = @fopen("/sys/class/gpio/gpio".$pin."/value", 'a');
    @fwrite($handle, "0");
    @fclose($handle);
    }
    [/php]


    Es wäre allerdings eleganter, auch für spätere Nutzung, gleich am Anfang eine Liste anzulegen mit den GPIO's die man verwenden will. Die einfachste Form wäre:
    [code=php]$GPIO = "17,27,22,23,24";[/php]
    Hier kann man dann das Kommata als Trennzeichen nutzen und in einer 'foreach' Schleife jedes Element bzw Zahl einzeln durchlaufen. Eine Zeichenkette (Liste) anhand eines Trennzeichens aufsplitten bewerkstelligt man mithilfe des Befehls explode(); , denn mit 'foearch' geht man Elemente eines Arrays durch, was explode aus der Liste dann auch erstellt.
    Der optimierte Aufruf zur Initialisierung aller GPIO's sähe hiermit dann wie folgt aus:
    [code=php]
    foreach( explode(",", $GPIO AS $pin) ) {
    init_GPIO($pin, "out");
    }
    [/php]

    ..Wie gesagt gibt es unendlich viele Möglichkeiten etwas umzusetzen oder zu gestalten. Die hier gezeigten sind nicht das non-plus-ultra da ich versuche das ganze auch für Anfänger möglichst verständlich zu beschreiben..
    Man kann das jetzt noch immer weiter Spinnen und ausbauen bzw optimieren - letztlich kommt das aber auf den jeweiligen Einsatzzweck an und was derjenige erreichen möchte, wie derjenige sich die Webseite vorstellt usw etcpp..

    Nur soviel: Es gäbe noch zwei Steigerungen die ich persönlich ebenfalls bevorzuge, da man damit jedem GPIO seine Funktion gleich mit einstellen kann usw. Aber gehen wir das langsam an...

    Die nächste Optimierung wäre nicht eine Liste zu verwenden sondern ein Array. Ein Array sollte immer vorher als solches deklariert werden, es geht zwar auch ohne ist aber im Professionellen Einsatz ein nogo ;)
    Ein eindimensionales Array wird wie folgt Initialisiert:[code=php]$GPIO = array();[/php]
    Eindimensional deshalb weil man nur ein Abschnitt in der Form von $GPIO["pin"] = "dir"; festlegt. Sobald man noch ein Abschnitt mit Eckklammern dazu setzt ist es ein Zweidimensionales Array: $GPIO["pin"]["dir"] = "LED 1";

    Der Vorteil solcher Arrays besteht in der Flexibilität, der Dynamic und dem eigentlich einfacheren Umgang. Bevor man zich verschiedene normale Variablen deklariert mit unterschiedlichen Bedeutungen usw die alle 'hardcoded' sind und man sich im späteren Verlauf einen Ast abbricht das einiger maßen vernünftig einzubinden usw, sollte man lieber zu einem Array greifen ;)

    Die Dimensionen eines Arrays sind unendlich :D
    Ich hab schon welche mit 8 gesehen, was aber schon an Wahnsinn grenzt. Mein Maximum waren bisher 4. Da hörte es dann aber auch schon auf angenehm zu sein, der spätere Umgang wurde dann eher zu einer Qual.
    Stattdessen sollte man dann lieber dazu übergehen und verschiedene Arrays anzulegen, dessen Inhalt aufeinander abgestimmt ist, also zum Beispiel:

    Spoiler anzeigen

    [code=php]
    $PIN["11"] = "17";
    $PIN["13"] = "27";

    $GPIO["17"] = "LED";
    $GPIO["27"] = "RELAIS";
    [/php]
    So könnte man daher gehen und den Wert von $PIN["11"] zu verwenden um auszugeben was an diesen Pin angeschlossen ist: $GPIO[$PIN["11"]] würde dann LED ausgeben.

    Aber das sei nur mal ein Beispiel - auch hier gibt es wie immer verschiedene Möglichkeiten und unendlich viele Herangehensweise....

    Aber um wieder zurück zu dieser Anleitung zu kommen:

    Wie gesagt wäre es einfacher und flexibler ein Array zu verwenden, in dem der GPIO# und dessen Direction festgelegt werden. Auf unser Beispiel hier angewendet sähe das so aus:
    [code=php]
    $GPIO = array();
    $GPIO['17'] = "out";
    $GPIO['27'] = "out";
    $GPIO['22'] = "in";
    $GPIO['23'] = "in";
    $GPIO['24'] = "out";
    [/php]

    Nun nehmen wir uns die von oben genannte foreach Schleife und wandeln diese nur ein bisschen ab:
    [code=php]
    foreach( $GPIO AS $pin => $direction ) {
    init_GPIO($pin, $direction);
    }
    [/php]

    Und schon sind alle von uns benötigten GPIOs mit der benötigten Direction initialisiert. Wollt ihr später noch einen GPIO mehr verwenden braucht ihr nur das $GPIO Array erweitern/verändern aber der restliche Code könnte so bleiben.


    Auf die gleiche Weiße könnte man jetzt auch den aktuellen Wert aller GPIO's auslesen und zur späteren Verwendung vor-merken:
    [code=php]
    $STATUS = array();
    foreach( $GPIO AS $pin => $direction ) {
    $STATUS[$pin] = GetGPIOpinValue($pin);
    }
    [/php]Bei der späteren Verwendung bräuchte man dann nur $STATUS['17'] abrufen, zum Beispiel:
    [code=php]
    if ($STATUS['17'] == 0 AND $STATUS['27'] == 0) {
    echo "Motor steht";
    }
    [/php]


    Nun kommen wir zu den Schaltflächen.
    Wie im [al=FirstCode]Code zur 1.Möglichkeit[/al] Abschnitt schon erklärt, wären 'button' Elemente zwar besser und flexibler, nur leider kann man davon nur ein 'submit' Type pro Formular haben.

    Ich gebe daher zunächst auch ein Beispiel wie es auch ohne Knöpfe (button oder input) funktioniert aber nicht $_POST sondern $_GET verwendet.
    Und zwar kann man auf einfache Weise sog. Hyperlinks hierfür verwenden. Man erstellt sich also einfach für jeden Schaltzustand einen Link und übergibt beim anklicken die dafür erforderlichen $_GET Parameter.
    Beispiel:
    [code=php]
    GPIO 17: <a href="?gpio=17&dir=out&state=1">An</a> / <a href="?gpio=17&dir=out&state=0">Aus</a> <br/>
    [/php]
    Bei dieser Schreibweise wird es an das aktuelle File angehängt.
    Möchte man die Übergabe aber an eine andere Datei übergeben müsste es stattdessen wie folgt aussehen:
    [code=php]
    GPIO 17: <a href="change.php?gpio=17&dir=out&state=1">An</a> / <a href="change.php?gpio=17&dir=out&state=0">Aus</a> <br/>
    [/php]

    Beim klicken auf einer der Links werden also 3 Variablen über GET übermittelt und die URL entsprechend verändert: gpio, dir und state
    Wer aber jetzt denkt die Verarbeitung wär deshalb schwierig den muss ich leider enttäuschen :D

    Wie üblich gibt es mehrere Möglichkeiten :X

    Eine Möglichkeit sieht vor das weiter oben erwähnte $GPIO Array zu verwenden, um den GPIO zu initialisieren falls noch nicht passiert und erst dann entsprechend der Übergabe zu setzen. Diese Möglichkeit ist flexibel und bedarf keinerlei späterer Anpassung.
    [code=php]
    if (!empty($_GET["gpio"])) {
    if (!file_exists("/sys/class/gpio/gpio".$_GET['gpio']."")) { init_GPIO($_GET['gpio'], $GPIO[$_GET['dir']]); }
    file_put_contents("/sys/class/gpio/gpio".$_GET['gpio']."/value", $_GET['state']);
    }
    [/php]Kurz und Knapp, allerdings nicht eindeutig und daher Problematisch falls man auch noch andere $_GET Werte übergeben will... Da müsste man dann schon klarere Differenzierungen vor nehmen um sicher zu stellen, dass auch wirklich "gpio" übergeben wurde, sonst hagelt es Fehlermeldungen.


    Da wir aber mehrere GPIO's verwenden wollen, könnten wir uns nun auch automatisiert für jeden GPIO obige Links generieren lassen, sodass wir später auch hier keinerlei Änderungen am Code mehr vornehmen müssten... Allerdings können wir natürlich nur GPIO's beeinflussen die als Ausgang definiert sind, GPIO's als Eingang können wir nur auslesen.
    [code=php]
    foreach($GPIO AS $pin => $direction) {
    if ($direction == "out") {
    if (!file_exists("/sys/class/gpio/gpio".$pin."")) { init_GPIO($pin, "out"); }
    echo "GPIO ".$pin.": <a href="?gpio=".$pin."&state=1">An</a> / <a href="?gpio=".$pin."&state=0">Aus</a>";
    echo "<br/>\n";
    }
    }
    [/php]Brauchen dann nur noch:
    [code=php]
    if (!empty($_GET["gpio"])) {
    file_put_contents("/sys/class/gpio/gpio".$_GET['gpio']."/value", $_GET['state']);
    }
    [/php]


    Nun wäre es natürlich auch schön wenn wir den aktuellen Status angezeigt bekommen. Da wir an dieser Stelle aber sowieso schon durch das $GPIO Array flitzen können wir auch hier gleich den Status mit ausgeben.. Und besonders schön wäre es sogar mit ein bisschen Farbe ;) Auch das geht recht einfach indem wir uns mithilfe CSS etwas malen lassen.
    [code=php]
    foreach($GPIO AS $pin => $direction) {
    if ($direction == "out") {
    echo "GPIO ".$pin.": <a href='?gpio=".$pin."&state=1'>An</a> / <a href='?gpio=".$pin."&state=0'>Aus</a>";
    if (GetGPIOpinValue($pin) == 1) {
    echo "<div id='gpioOn' style='width: 25px; height: 20px; background-color:Green'>On</div>\n";
    } else {
    echo "<div id='gpioOff' style='width: 25px; height: 20px; background-color:Red'>Off</div>\n";
    }
    } else {
    if (GetGPIOpinValue($pin) == 1) {
    echo "GPIO ".$pin." <div id='gpioOn' style='width: 25px; height: 20px; background-color:Green'>On</div>\n";
    } else {
    echo "GPIO ".$pin." <div id='gpioOff' style='width: 25px; height: 20px; background-color:Red'>Off</div>\n";
    }
    }
    echo "<br/>\n";
    }
    [/php]

    ...Nun habe ich 2 neue Sachen gezeigt, die eigentlich zu den PHP Grundlagen gehören würde.....Randbemerkung - füge ich später in die "PHP Grundlagen" mit ein :fies:
    Und zwar ein mal das \n am Ende eines echo's: Das sorgt für einen Zeilenumbruch, welchen man eigentlich in jedes echo einfügen sollte, damit der Quellcode (Rechtklick auf eine Webseite) auch lesbar aussieht und nicht alles in einer langen Zeile steht..
    Und das andere betrifft das vermischen von " und '. Ein Zeilenumbruch funktioniert nur mit einem echo "bla\n"; aber nicht mit echo 'bla\n'; .. Und das nächste ist dass wenn innerhalb des echos weitere " auftauchen muss man diese entweder escapen (ein \ davor setzen) oder umwandeln in '.

    Damit gewinnt man zwar kein Schönheitswettbewerb aber ist fürs erste Funktional :fies:

    Spoiler anzeigen


    Ein vorläufiger Zwischenstand des Codes inkl. Grundgerüst:

    Spoiler anzeigen

    [code=php]
    <!DOCTYPE html>
    <html>
    <head>
    <title>GPIO test</title>
    </head>
    <body>

    <?php
    $GPIO = array();
    $GPIO['17'] = "out";
    $GPIO['27'] = "out";
    $GPIO['22'] = "in";
    $GPIO['23'] = "in";
    $GPIO['24'] = "out";

    //--------


    if (!empty($_GET["gpio"])) {
    if (!file_exists("/sys/class/gpio/gpio".$_GET['gpio']."")) { init_GPIO($_GET['gpio'], $GPIO[$_GET['gpio']]); }
    file_put_contents("/sys/class/gpio/gpio".$_GET['gpio']."/value", $_GET['state']);
    }

    foreach($GPIO AS $pin => $direction) {
    if ($direction == "out") {
    echo "GPIO ".$pin.": <a href='?gpio=".$pin."&state=1'>An</a> / <a href='?gpio=".$pin."&state=0'>Aus</a>";
    if (GetGPIOpinValue($pin) == 1) {
    echo "<div id='gpioOn' style='width: 25px; height: 20px; background-color:Green'>On</div>\n";
    } else {
    echo "<div id='gpioOff' style='width: 25px; height: 20px; background-color:Red'>Off</div>\n";
    }
    } else {
    if (GetGPIOpinValue($pin) == 1) {
    echo "GPIO ".$pin." <div id='gpioOn' style='width: 25px; height: 20px; background-color:Green'>On</div>\n";
    } else {
    echo "GPIO ".$pin." <div id='gpioOff' style='width: 25px; height: 20px; background-color:Red'>Off</div>\n";
    }
    }
    echo "<br/>\n";
    }


    //-------- Functions

    function init_GPIO($pin, $direction) {
    if (!file_exists("/sys/class/gpio/gpio".$pin."")) {
    file_put_contents("/sys/class/gpio/export", $pin);
    usleep(200000); //Programm-Verzoegerung in Mikrosekunden: 0.2s
    file_put_contents("/sys/class/gpio/gpio".$pin."/direction", $direction);
    }
    }

    function GetGPIOpinDirection($PIN) {
    $Direction='';
    if (file_exists("/sys/class/gpio/gpio".$PIN."")) {
    $Direction = trim(file_get_contents("/sys/class/gpio/gpio".$PIN."/direction"));
    }
    return $Direction;
    }

    function GetGPIOpinValue($PIN) {
    $Value='';
    if (file_exists("/sys/class/gpio/gpio".$PIN."")) {
    $Value = trim(file_get_contents("/sys/class/gpio/gpio".$PIN."/value"));
    }
    return $Value;
    }
    ?>
    </body>
    </html>
    [/php]


    UnderConstruction.jpg
    Under Construction

    ...bevor mir das bisher geschriebene verloren geht, poste ich das lieber mal und schreibe in ein paar Stunden weiter...


    - > [al=Index]Index[/al] < -

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!