-
Danke für die Tipps.
Ich schaue es mir mal an.
Wie funktioniert das mit dem C Programm?
Ist das ein Programm was nur 1x ausgeführt wird wenn man auf nen Button drückt oder wie muss ich das verstehen?
Bzw. kannst du mir ein bischen näher erklären wie ich die Interaktionen in einem Webinterface mit einem C-Programm koppeln kann, so das über das Webinterface Daten an ein C-Programm gegeben werden?
-
Das hier vorgestellte sendet das, was als Parameter übergeben wurde, an die UART und wird dann gleich wieder beendet. Wird es nur so, also ohne Parameter aufgerufen, sendet es die Uhrzeit mit einem U voran, sowie eine Sekunde später das Datum mit einem D voran. Ansonsten werden Umlaute noch angepasst und als letztes ein CR gesendet.
Das funktioniert bei mir sehr zuverlässig. Einige Strings müssen in Häkchen gesetzt werden, da sonst bestimmt Zeichen einen Strich durch die Rechnung machen. Aufgefallen sind mir da die Klammern und das Kaufmannsund.
Darüber hinaus prüft es noch, ob es bereits läuft und macht pro bereits laufende Instanz eine Sekunde Pause. So wird verhindert, dass mehrere Strings durcheinander gemischt werden und nur Murks ankommt.
Ich habe mein Protokoll darauf hin aufgebaut, dass das erste Zeichen angibt, was da jetzt kommt. U für Uhrzeit, D für Datum R für Radiosender I für Interpret, T für Titel, S für Anzahl der Titel in der Senderliste, C für aktuelle Position in der Liste und V für aktuelle Lautstärke. Alles bis auf U und D übernimmt aber ein anderes Programm, welches über das "senden" Programm Informationen zum AVR schickt.
Der Rückkanal AVR>>Raspi erfolgt über ein anderes Programm, welches ständig im Hintergrund läuft. Immer, wenn Daten über UART eintreffen, werden diese dann von diesem Programm ausgewertet und entsprechend gehandelt. Da musste ich allerdings das Protokoll ertwas erweitern: Seit ich eine definiertes Zeichen als Indiz für einen neuen Datensatz nutze, läuft es fehlerfrei durch. Ohne dieses Zeichen musste ich hin und wieder einen Befehl doppelt senden.
Meine Experimente, wo nur ein Programm in beide Richtungen agiert, waren zwar auch erfolgreich, jedoch stieg durch die ständige Loop die Prozessorlast teilweise auf über 30% nur für das Programm an, was ich nicht wollte. Mit den 3 Programmen bin ich so gut wie immer im einstelligen Prozentbereich für diese. Das 3. Programm überwacht alle 2 Sekunden den Status des MPD und schläft dazwischen immer. Nur bei Änderungen werden Informationen über "senden" an den AVR gemeldet. Ansonsten prüft es auch noch per pidof, ob das Empfänger Programm noch läuft und startet es, wenn es dieses nicht findet. Umgekehrt prüft das Empfänger Programm auch den Status des Überwachers, wenn eine Zeichenkette eingetroffen ist (Also nach dem chr(13)). Das gegenseitige Überwachen kommt aber im nächsten Step raus und wird per Cronjob minütlich durch ein Programm namens Wachhund übernommen. Das ist auch schon fertig und überwacht dann zusätzlich noch den MPD.
Ich kann auch über den AVR alle aktuellen Werte anfordern, was in der Testphase von Vorteil ist, wenn der AVR neu startet, der Raspi aber durchläuft. Dazu sendet er ein bestimmtes Zeichen (CHR(8)). Das erkennt der Empfänger und beendet den MPD Überwacher und startet ihn darauf neu, was zur Folge hat, dass alle aktuellen Werte zum AVR geschickt werden.
Um eine Kommunikation mit einem Programm per php zu realisieren, gibt es mehrere Möglichkeiten. Ich würde ein Programm mit Parametern aufrufen und für den Rückkanahl die Ausgabe auswerten. Dieses müsste dann im Beispiel im $output ankommen. Wie es bei Leerzeichen und anderen Zeichen, wie die oben erwähnten Klammern, sowie & reagiert, habe ich noch nicht getestet. Kann ich aber mal eben schnell probieren. Dazu melde ich mich später nochmal.
-
Danke für die Erklärung, nur leider hilft das erstmal nicht weiter.
Welcher Teil in deinem C Programm ist den dafür zuständig die Parameter zu empfangen und sie dann an den UART weiterzugeben?
Das Senden eines Strings per UART ist kein Problem für mich nur ich möchte verstehen wie bei deinem Beispiel der Text der im Webinterface steht und zwangsläufig durch den Webserver bearbeitet wird, aus dem Webserver raus kommt und in den UART rein ;)
Stecke da leider noch nicht so tief drin von daher sorry falls ich mich etwas unklar ausdrücken sollte aber ich würde gerne verstehen wie die Kommunikation zwischen verschiedenen Komponenten (hier Webserver + eigenes C-Programm) funktioniert. :)
Ich nehme mal an auf dem selben Weg kann man das dann auch für den SPI bzw. I²C machen?
-
Normal schon, ich habe allerdings noch keinerlei Erfahrungen auf dem Raspi mit SPI oder I²C.
Anbei mal eine Testseite (index1.php)
Code:
<html>
<head>
<title>Raspberry Pi Server - Testseite</title>
</head>
<body>
<h1>Raspberry Pi Webserver - Testseite</h1>
<div id="nav"><a href="index.php?safemode=1">Safemode?</a><a href="index.php">Hauptseite</a> <a href="index1.php">Neu laden</a></div>
<div id="main"><h2>Raspi Kommunikationstest<h2><br>
<?php
header("Cache-Control: no-cache, must-revalidate"); //cache abschalten, so dass die Seite neu geladen wird
if (isset($_GET["safemode"])) { //Ermögliche, den Safemodus des php abzufragen.
if( ini_get('safe_mode') ){
echo "<br>Safemode an<br>";
}else{
echo "<br>Safemode aus<br>";
}
};
if (isset($_POST["befehl"])) { //Wurde die Variable "befehl" gesetzt?
if ($_POST["befehl"]!=""){ //Ist befehl nicht leer dann
$befehl="/var/scripte/kommunikation ".$_POST["befehl"]; //Programm aufruf und die Eingabe, welche in den Postdaten der befehl-Variable steckt als Parameter anhängen
$output = shell_exec($befehl); //Durchführen des Programmaufrufes und die Antwort in output speichern
}
};
echo "<form method='post' action='index1.php'>"; //Formula einleiten, nach Absenden des Textes die Seite neu laden. Dabei wird der Text dann an das Programm übergeben
echo "<input type='text' maxlength='40' size='40' id='1' name='befehl' value='Ich' />"; //Textfeld max 40 Zeichen, name befehl
echo "<input type='submit' value='Senden' />"; //SendenButton
echo "<br>"; //Neue Zeile
echo "Antwort: $output </form>"; //Antwort des Programmes ausgeben, sowie Form abschließen
?>
</div>
</body>
Diese nimmt den eingegebenen Text und schickt diesen als Parameter an /var/scripte/kommunikation
Das muss nicht per sudo geschehen, da nichts auf die UART ausgegeben werden soll.
/var/scripte/kommunikation.c
Code:
// Compile with: gcc /var/scripte/kommunikation.c -o /var/scripte/kommunikation
//#include <iostream>
//using namespace std;
#include <sys/types.h> //nicht benötigtes scheint keinen Platz zu verschwenden...
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc >= 1) { //arg[0] ist der Programmaufruf an sich. Also mehr als ein Argument (Parameter), dann ist Text eingetroffen
if (strcmp(argv[1],"Ich")==0){ //Vergleich auf das Wort Ich
printf("Du\n"); //Bei "Ich" "Du" antworten
}else{ //Kein "Ich" gefunden
if (strcmp(argv[1],"Du")==0){ //Evtl. ein "Du"?
printf("Ich\n"); //Ja - mit "Ich" antworten.
}else{ //Weder ein Ich noch ein Du erkannt
printf("Keiner\n"); //"Keiner" Antworten
}
}
}
return 0;
}
Also ein einfacher Kommunikationstest. Wichtig ist, dass die Seite als index1.php gespeichert (und dann auch aufgerufen wird). Die Rechte nicht vergessen.
Das Senden Programm weiter oben sendet die Zeichen einzeln in der
Code:
unsigned char send(char c)
{
int res=write(fd, &c, 1);
if (res<0) printf("Fehler beim Senden\n");
return res;
}
Init öffnet die Schnittstelle und parametriert diese.
In der Main werden die Parameter abgefragt und evtl. Sonderzeichen angepasst. Dann wird das char Array pBuf, welches den zu sendenden Text enthält Zeichenweise per send an der UART ausgegeben.
Hier nochmal das Senden Programm mit Kommentaren:
Code:
// Compile with: gcc /var/scripte/senden.c -o /var/scripte/senden
//#include <iostream>
//using namespace std;
#include <sys/types.h> //nicht benötigtes scheint keinen Platz zu verschwenden...
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#define BAUDRATE B19200
char MODEMDEVICE[]= "/dev/ttyAMA0"; // !!!
int fd; // File descriptor
struct termios newtio={};
unsigned char send(char c) //gibt ein einzelnes Zeichen über UART (als fd geöffnet) aus.
{
int res=write(fd, &c, 1); //Zeichen ausgeben
if (res<0) printf("Fehler beim Senden\n"); //Fehler aufgetreten
return res;
}
int init() //Schnittstelle öffnen und parametrieren
{
/*** Init ***/
//O_RDONLY, O_WRONLY or O_RDWR -
//O_NDELAY (geht weiter, wenn keine Daten da sind und gibt "-1" zurueck)
// man 2 open fuer mehr Infos - see "man 2 open" for more info
// O_NOCTTY No ControllTeleType
fd = open(MODEMDEVICE, O_WRONLY | O_NOCTTY);
if (fd < 0){
printf("Fehler beim oeffnen von %s\n", MODEMDEVICE);
exit(-1);
}
memset(&newtio, 0, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD; //setzt die neuen Porteinstellungen
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0; /* set input mode (non-canonical, no echo, ...) */
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 1 chars received */
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);
return fd;
}
int einmal() //Prüfen, ob das Programm schon läuft und die Instanzen zählen
{
int i=0;
int anzahl=0;
int laenge=0;
char *ptr;
FILE *in;
extern FILE *popen();
char buff[30];
char rueckgabe[30]="";
if(!(in = popen("pidof senden", "r"))){ //PID abfragen
exit(1);
}
fgets(buff, sizeof(buff), in);
pclose(in);
laenge=strlen(buff);
for(i=0;i<laenge;i++)
{
if(buff[i]==' ') anzahl++; //Zwischen den PIDs sind Leerzeichen. Einmal diese Instanz und pro weitere Instanz ein Leerzeichen
}
return anzahl;
}
int main(int argc, char** argv) //Programmstart
{
char c; //in c passt ein einzelnes Zeichen zum versenden
char vBuf[100]="",*pBuf; //Evtl. Buffer vergrößern, sonst Schutzverletzung
char buffer [100]=""; //""
char buffer1 [100]=""; //""
int i=0;
int anzahl;
anzahl=einmal(); //Anzahl der Instanzen abfragen
if (anzahl>0){ //Bei mehreren pro Instanz eine Sekunde Pause
anzahl++;
sleep(anzahl);
}
init(); //Schnittstelle öffnen und parametrieren
if (argc == 1) { //Wenn nichts angegeben, dann Uhrzeit und Datum senden
time_t rawtime; //Zeit auslesen und formatieren
struct tm * timeinfo; //Zeit auslesen und formatieren
time ( &rawtime ); //Zeit auslesen und formatieren
timeinfo = localtime ( &rawtime ); //Zeit auslesen und formatieren
strftime (buffer,80,"U%H:%M:%S",timeinfo); //Erwartetes Uhrzeitformat: Uhh:mm:ss
pBuf=buffer; //Zeit in buffer schieben
strcat(pBuf,"\r"); //CR anhängen
while(pBuf && *pBuf) //Zeichen einzeln senden
send(*pBuf++);
strftime (buffer,80,"D%d.%m.%y",timeinfo); //Erwartetes Datumsformat: Dtt.mm.yy
pBuf=buffer; //Datum in buffer schieben
strcat(pBuf,"\r"); //CR anhängen
sleep(1); //1Sekunde warten
while(pBuf && *pBuf) //Zeichen einzeln senden
send(*pBuf++);
}else{ //Sonst Parameter einlesen und senden
if(strlen(argv[1])>75){ //Puffer im AVR ist auf 80 gestellt. Bei mehr als 75 Zeichen wird nur der erste Parameter abgefragt
strncpy(buffer,argv[1],76); //Maximal 76 Zeichen zulassen
buffer[76]='\0';
}else{ //Weniger als 76 Zeichen im 1. Parameter, also weitere Parameter einlesen und aneinanderhängen
strcpy(buffer,argv[1]); //Erste Paameter einlesen und in buffer ablegen
if (argc >2){ //gibt es noch mehr Parameter?
for (i=2;i<argc;i++){ //alle nacheinander einlesen und anhängen
if(strlen(buffer)+strlen(argv[i])>75){ //Buffer plus Parameter x länge>75???
strcat(buffer," "); //Leerzeichen zwischen den Wörtern einfügen
strncat(buffer,argv[i],(76-strlen(buffer))); //Ist der Text zu lang, dann bei 76 Zeichen abschneiden
buffer[76]='\0'; //Textende Zeichen ist chr(0)
break; //exit for in c...
}else{
strcat(buffer," "); //Weniger als 76 Zeichen?
strcat(buffer,argv[i]); //Parameter weiter anhängen
}
}
}
}
int o=0;
for(i=0;i<strlen(buffer);i++){ //Umlaute von UTF-8 in ASCii Codes des AVR wandeln. Dabei den Text in buffer1 ablegen
buffer1[o]=buffer[i];
if (buffer1[o]==164){ //ä
buffer1[o]= 228;
}
if (buffer1[o]==188){ //ü
buffer1[o]= 252;
}
if (buffer1[o]==182){ //ö
buffer1[o]= 246;
}
if (buffer1[o]==132){ //Ä
buffer1[o]= 196;
}
if (buffer1[o]==156){ //Ü
buffer1[o]= 220;
}
if (buffer1[o]==150){ //Ö
buffer1[o]= 214;
}
if (buffer1[o]==159){ //ß
buffer1[o]= 223;
}
if (buffer1[o]==138){ //&
buffer1[o]= 38;
}
if (buffer1[o] != 195){ //Initialisierung Umlaut weglassen
o++;
}
}
pBuf=buffer1; //pBuf nach buffer1 umbiegen
strcat(pBuf,"\r"); //CHR(13) anfügen, damit der AVR auswertet
while(pBuf && *pBuf){ //Zeichen einzeln senden
send(*pBuf++);
}
}
close (fd);
return 0;
}
-
Wo lege ich den fest das die Seite index1.php heißen muss?
Und Parameter einer PHP Seite kann ich ganz einfach mittels $_POST an jede beliebige Datei (in dem Fall das kompilierte C-Programm) übergeben?
Und das einlesen des Textes geschieht dann mit der Variable
extern File *popen();
?
-
Zu 1:
Code:
echo "<form method='post' action='index1.php'>"; //Formula einleiten
Heißt die php anders, muss dies hier bei action angepasst werden.
Zu 2: Die Formularcontrols haben alle einen Namen. Beim Senden mittels Click auf den Submit werden die Werte mit den jeweiligen Namen übergeben. Das Textfeld hier heißt befehl. Der darin übergebene Text lässt sich mit $_POST["befehl"] auslesen. Es können auch mehrere Textfelder und auch andere Controls mit vorhanden sein, diese lassen sich dann alle mit $_POST["jeweiligerName"] auslesen.
Zu 3: Das einlesen des Textes in dem Programm erfolgt mit argv. in argc steht die Anzahl der Parameter. Dieser ist mindestens 1, da der Programmaufruf selbst der erste Parameter[0] ist. Alle weiteren Parameter stehen dann in argv[1] bis argv[x]. Diese werden an den Leerzeichen jeweils getrennt. "Außer der Text wird komplett in Häkchen übergen." Dann steht alles in argv[1].
popen öffnet eine Pipe, worin das aufgerufene Programm (pidof) die Ausgabe hin umleitet. So steht dann die Ausgabe von "pidof senden" direkt in der Variable buff. Dieses wird in diesem Fall aber nur genutzt, um die Anzahl der laufenden Instanzen des Senden-Programmes anzufragen. Ist also nicht für das eigentliche Senden nötig.
-
Super :)
Dank dir für die Erklärung. So langsam dämmert es mir.
Jetzt hatte ich auch nen Ansatz zum suchen. Ich habe einfach mal nach argc gegooglet und da kam dieser Link bei raus:
http://crasseux.com/books/ctutorial/argc-and-argv.html
Das habe ich mir mal durchgelesen und wenn ich es richtig verstanden habe, funktioniert dies so:
Durch einen Klick im WebIf schickst du einen Befehl ab. Diesen nenne ich jetzt einfach mal "Start".
Der Programmaufruf der durch den shell_exec ausgeführt wird lautet dementsprechend "/var/scripte/senden.Start".
Als Parameter steht dann ja in argv eine 1 drin, weil die [0] ist der Name des Programmes, sprich "/var/scripte/senden" und die [1] dann der übergebene Text, sprich "Start" als Zeichenkette.
Und diese Zeichenkette zerlegst du dann und sendest sie einzelnt.
Ist das so korrekt?
Dann eine zweite Frage....in dem untersten Beispiel rufen sie das Programm so auf
./fubar a b c
also mit Leerzeichen. Du verwendest einen Punkt. Aus welchem Grund? Ginge das mit Leerzeichen auch oder hat der Punkt was damit zu tun, weil das bei dir ein Aufruf über PHP ist und nicht über Konsole wie im Beispiel?
-
Da fehlt ein Leerzeichen: /var/scripte/senden Start
Der Punkt leitet die Variable ein. Würde dahinter nochwas kommen, wäre ein weiterer Punkt notwendig. $befehl="/var/scripte/kommunikation ".$_POST["befehl"]." nächsterParameter";
Allerdings bin auch ich, wie Du wohl auch, recht neu, was php betrifft.
-
Ist der Punkt da nur drin damit dein Programm den Befehl erkennen kann oder ist es so von C gefordert?
Ansonsten dank dir schonmal für deine Hilfe :)
Es ist mir nun einige klarer geworden und mal schauen ob ich es auch hinbekomme :D
-
Der Punkt ist dafür, dass php die Variable erkennen kann. Der Punkt wird nicht mitgesendet.