Hallo,
Erstmal großes Lob an Dich für Deine Anleitung mit Python sieht sehr elegant aus, um ehrlich zu sein habe ich es mir aber nicht in seinem ganzen Umfang angeschaut, weil ich mich mit Python nicht auskenne.
Ich scheine einer der wenigen zu sein, die mit Python sofort frustriert waren, deshalb wollte ich es in C++ lösen.
Eigentlich brauche ich das Ding wirklich nur zum An- und Ausschalten von GPIOS. Die zu Grunde liegende Idee ist eine Bewässerung, aber noch weit entfernt. Ich komme jetzt aber an einen Punkt, an dem ich das ganze nicht weiterentwickeln möchte, da mir der Umfang reicht. Ich hatte aber viel Spaß dabei und vielleicht möchte irgendwer daran weiterbasteln. Der Ablauf ist prinzipiell wie folgt:
Irgendetwas (Alles was eine HTTP Anfrage senden kann) sendet einen POST Request mit den Werten curstate="XX"&gpio="YY" an "toggle.php". Toggle.php leitet diese Daten weiter in die Pipe, in der sie dann von webListener (der per Poll dauerhaft lauert) auseinandergenommen und entsprechend verwurstet werden.
Bekannte "curstate" Inhalte sind on, off, in, out, rise, fall, both. stop schießt die Endlosschleife ab und das Programm wird beendet.
Als gpio muss die Gpio Nummerierung verwendet werden. Bisher werden 2 und 3 wegen der I2C Schnittstelle unterbunden, alles andere bis 40 wird einfach durchgewunken.
Zusätzlich (wenn auch sehr speziell) realisiert:
C++: Beim umschalten eines GPIO auf Ausgang, wird automatischein evtl. vorhandener Edge Eintrag gelöscht.
php: Beim umschalten wird der neue Wert in eine XML Datei geschrieben, die ich dazu nutze die Werte der GPIOs auf der Website darzustellen.
Ist wahrscheinlich alles nicht professionell, aber ich bin ja eigentlich auch Chemiker
C++ Code:
//#define DEBUG
#include "webL.h"
bool run = true;
struct pollfd fds[1];
//A char array that can read up to 8 commandbytes + a dump char array with one element (not a beauty but effective)
char input [9];
char dump [1];
uint8_t command;
std::ofstream file;
int main(int argc, char **argv)
{
/* This will set up the pollfd struct and start the poll of a pipe that is defined in fifo.h
* Opening with O_RDWR | O_NONBLOCK will only work on Linux
*/
if ( (fds->fd = open ( FIFO, O_RDWR | O_NONBLOCK) ) < 0) return -1;
fds->events = POLLIN;
//run can be set to false by sending stop
while( run == true ){
//fire if poll returns a positive
if( ( poll ( fds, 1, 10000 ) ) > 0 ){
// fill command array with zeros
memset( input, 0, sizeof( input ));
// read pipe buffer into command array
read( fds->fd, input, sizeof( input ) );
// create zero terminated string
input[ strlen( input ) - 1] = '\0';
// dump rest of the pipe buffer
while( (read( fds->fd, dump, sizeof( dump ) )) >= 0 );
//take the input, parse it and toggle
#ifdef DEBUG
int debug;
debug = parse(input);
switch (debug){
case 0: std::cout<<"OK" << std::endl; break;
case -1: std::cout<<"Error: GPIO" << std::endl; break;
case -2: std::cout<<"Error: Command" << std::endl; break;
case -3: std::cout<<"Error: File output" << std::endl;break;
}
#else
parse(input);
#endif
}
}
return 0;
}
// translates the input into an Integer for the value / direction and an Integer for the gpio
int parse(char *toParse){
//unsigned int i, j, k;
std::string input = toParse;
// Integers to store parsing results
int state, gpio;
state = FAIL;
gpio = FAIL;
std::string command;
std::string number;
number = "255";
//split the string into command and number
size_t pos;
if ((pos = input.find_first_of("0123456789"))!=std::string::npos){
command = (input.substr(0, pos));
number = (input.substr(pos));
//identify the command
if (command == "on") state = ON;
else if (command == "off") state = OFF;
else if (command == "in") state = IN;
else if (command == "out") state = OUT;
else if (command == "rise") state = RISE;
else if (command == "fall") state = FALL;
else if (command == "both") state = BOTH;
}else{
if (input == "stop"){
run = false; return 0;
}else state = FAIL;
}
//translate string to gpio (int)
if ((gpio = std::stoi(number))>40) gpio = FAIL;
#ifdef DEBUG
std::cout<<"Command: "<< command <<" - state: " << state << " - Number: " << number << std::endl;
#endif
return toggle(state, gpio);
}
int toggle (int state, int gpio){
std::string command;
std::string path;
//check if the gpio is available: 1 does not exist, 2 and 3 are reserved for I2C
switch (gpio){
case 1: return -1; break;
case 2: return 1; break;
case 3: return 1; break;
case FAIL: return -1; break;
default: break;
}
//check the direction / value of the gpio and translate into command and path string
switch (state){
case OUT:
path = STDPATH + std::to_string(gpio) + "/direction";
command = "out";
toggle(NONE,gpio);
usleep(50000);
break;
case IN:
path = STDPATH + std::to_string(gpio) + "/direction";
command = "in";
break;
case ON:
path = STDPATH + std::to_string(gpio) + "/value";
command = "1";
break;
case OFF:
path = STDPATH + std::to_string(gpio) + "/value";
command = "0";
break;
case RISE:
path = STDPATH + std::to_string(gpio) + "/edge";
command = "rising";
break;
case FALL:
path = STDPATH + std::to_string(gpio) + "/edge";
command = "falling";
break;
case BOTH:
path = STDPATH + std::to_string(gpio) + "/edge";
command = "both";
break;
case NONE:
path = STDPATH + std::to_string(gpio) + "/edge";
command = "none";
break;
case FAIL:
return -2;
}
#ifdef DEBUG
Debugging: std::cout << path << " - " << command << std::endl;
#endif
//put the command into the file
std::ofstream output;
output.open (path);
if ( !output ) return -3;
output << command;
output.close();
return 0;
}
Alles anzeigen
webL-header:
#ifndef _FIFO_H_
#define _FIFO_H_
#include <iostream>
#include <fstream>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <inttypes.h>
#define FIFO "/path/to/pipe"
#define STDPATH "/sys/class/gpio/gpio"
//#define STDPATH "/path/to/play/around/with"
#define STOP 0xEE
#define FAIL 0xFF
#define ON 1
#define OFF 0
#define OUT 0x10
#define IN 0x11
#define RISE 0x02
#define FALL 0x20
#define BOTH 0x22
#define NONE 0x03
int parse( char * );
int toggle( int, int );
#endif
Alles anzeigen
PHP-Code (nur als Beispiel wie ich es nutze):
<?php
$PIPEPATH="/path/to/pipe";
$XMLNAME="setup.xml";
$XMLPATH="/path/to/setup.xml"
$curstate= $_POST['curstate'];
$gpio = $_POST['gpio'];
if ($curstate == "on" or $curstate == "off") $node = "on";
else if ($curstate == "out" or $curstate == "in") $node = "dir";
else if ($curstate == "rise" or $curstate == "fall" or $curstate == "both") $node = "edge";
else $node ="name";
if ($node != "name"){
$out = fopen($PIPEPATH, "a") or die("Unable to open file!");
$ausgabe = $curstate . $gpio . "\n";
fwrite ($out, $ausgabe);
fclose($out);
}
/*The following code is used to modify a setup.xml file that stores the gpios
*Properties.
*The XML file should look like this:
*<setup>
* <gpio>
* <num>4</num>
* <name>Teich</name>
* <dir>out</dir>
* <on>off</on>
* <active>y</active> <= This is currently not in use
* </gpio>
*</setup>
*/
$doc = new DOMDocument();
$doc->load( $XMLNAME );
$gpios = $doc->getElementsByTagName( "gpio" );
foreach ( $gpios as $xmlgpio){
$search = $xmlgpio->getElementsByTagName("num");
if ($search->item(0)->nodeValue == $gpio){
$child = $xmlgpio->getElementsByTagName($node);
$child->item(0)->nodeValue = $curstate;
if ($curstate == "out"){
$child = $xmlgpio->getElementsByTagName("edge");
$child->item(0)->nodeValue = "none";
}
}
}
$doc->save($XMLPATH);
?>
Alles anzeigen