PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Lighttpd + UART - Wird nichts gesendet



Kampi
08.09.2012, 20:11
Hallo Forum,

ich versuche gerade den UART von meinem Pi mit dem Webserver zu verbinden.
Ziel soll es sein, dass das Raspberry Pi über UART Daten an einen meiner CAN-Knoten sendet bzw. empfängt und diese dann in einem Webinterface wieder gibt.
Auf meiner Website habe ich diesen Code:



#Text senden
if (isset($_REQUEST['Text-senden']))
{
echo "Button gedrückt";
echo "<br>";
$befehl="echo '2' >> /dev/ttyAMA0";
$dummy = shell-exec($befehl);
}


Dieser wird testweise immer ausgeführt wenn ein Button gedrückt wird.
Mein Raspberry Pi ist auf 38400 Baud eingestellt und der User "Dialout" ist in der selben Gruppe wie der Benutzer des Webservers, sprich beide befinden sich in der Gruppe www-data.
Wenn ich jetzt auf den Button drücke wird aber kein Text gesendet. Führe ich den Befehl aber in der Konsole aus, funktioniert es einwandfrei.
Woran liegt das?

Ach und dann noch eine kleine Frage.....
Der UART soll sich bei jedem Neustart von dem Raspberry Pi selber auf 38400 Baud einstellen.
Dafür habe ich in der Date "rc.local" diese Zeile stehen



sudo stty 38400 -F /dev/ttyAMA0


Leider klappt das nicht so ganz recht. Wenn ich das Raspberry Pi neustarte und direkt nach dem Bootvorgang einen Text senden will, wird dieser nicht korrekt gesendet, sprich es kommt Zeichenmüll am Terminal an.
Stelle ich die Baudrate aber via Konsole nochmal händisch nach klappt es.
Weiß auch hier jemand eine Lösung dafür?

Ich bin mir recht sicher, dass es in beiden Fällen wahrscheinlich nur ein Rechteproblem ist, nur ich finde den Fehler nicht.
Danke für die Hilfe!

peterfido
10.09.2012, 19:44
Das mit dem Zeichenmüll hatte ich auch. Wenn die Schnittstelle ständig geöffnet bleibt, dann verschwindet dieser. Ich nutze für die UART nur noch die C-Programme. Ich hatte zwischenzeitlich auch schon alles in einem Programm, da ist aber durch die ständige Schleife die Prozessorlast höher. Bei 3 Programmen liegt diese beim Empfänger gen 0 und beim Überwacher meist auch.

So, wie Du es jetzt probierst, ging es bei mir aber auch. Allerdings habe ich den user www-data noch in die Gruppe dialout hinzugefügt.

Was wird denn in Dummy zurückgegeben?

Kampi
10.09.2012, 20:11
Hey,

Dummy gibt 0 aus.

peterfido
10.09.2012, 20:22
Hm.

Der User www-data darf auch senden?



sudo adduser www-data dialout

Kampi
10.09.2012, 23:00
Ich probiere es morgen mal aus.
Komme erst gleich heim und dann geht es auch direkt ins Bett ;)

Kampi
11.09.2012, 17:10
Der User ist schon in der Gruppe aber es tut sich trotzdem nichts :(

peterfido
11.09.2012, 17:22
Hm. Was passiert, wenn du die ' weglässt? Oder statt >> nur ein > verwendest. >> bedeutet anhängen und > überschreiben. Wie sieht es aus, wenn ein Bash Script "dazwischen " geschaltet wird, dieses also den echo Befehl ausführt? Oder einfach mal mein C-Programm testen, ob das wenigstens unter www-data sendet.

Edit oder
sudo echo halloWelt >> /dev/ttyAMA0

Kampi
11.09.2012, 19:46
Das klappt leider auch nicht :(

peterfido
11.09.2012, 20:49
Welche Variante meinst Du? Oder hast Du schon alle probiert? Da fällt mir noch der Unterschied auf, dass ich Lighthttp gar nicht genutzt habe, sondern den Apache. Hast Du /tmp auf tmpfs?

Edit:

Man kann in der php.ini shell-exec z.B. verbieten. Kannst du andere Programme aus php ausführen?

Kampi
11.09.2012, 21:06
Alles bis auf das Bash-Skrip habe ich ausprobiert.
Was meinst du mit /tmp?

peterfido
11.09.2012, 21:16
Hm. mein Edit mit der php ini kam bestimmt zu spät. Ich nutze im /tmp Verzeichnis das Tempfilesystem. Als letzte Möglichkeit könnte man da per echo was reinschreiben und ein Programm als root ausgeführt schickt das dann an die ttyAMA0

Kampi
11.09.2012, 21:39
Also Shell_exec funktioniert, da ich über das Webinterface auch meine GPIOs schalten kann.
Dies funktioniert und das habe ich auch bereits getestet.
Nur der Text wird halt nicht gesendet.

peterfido
11.09.2012, 21:51
Wer ist der Empfänger? Ein Terminalprogramm oder schon eon AVR. Falls AVR: hast Du ein CHR(13) angehängt?

Ich arbeite gerade an einem Internet-Radiowecker. Soweit läuft schon alles. Bin aber noch in der Testphase. Im Moment zieht er sich updates.

Da ist noch kein Webserver drauf. Wenn es der Platz zulässt, mache ich morgen mal ein Backup und teste mit lighthttp. Mir fehlt eh noch ein WebIf, um die Einstellungen komfortabel zu machen. Weckzeiten und so geht per AVR problemlos. Die Senderliste soll aber noch per php änderbar werden.

Kampi
11.09.2012, 22:07
Ne erstmal ist der Empfänger nur ein Terminalprogramm.
Das Pi soll aber später über UART mit einen meiner CAN-Knoten reden und dann empfangene Daten über ein Webinterface abrufbar machen.....nur bis dahin dauert es noch ein bischen :)
Ich arbeite die Punkte lieber erstmal Schritt für Schritt ab, damit nicht gleich x Fehler auf einmal auftreten und im Moment soll einfach nur der Text vom Pi an meinen PC gesendet werden wenn ich ein Button in meinem Webinterface betätige.

peterfido
11.09.2012, 22:29
lighthttp ist bei mir jetzt drauf. allerdings sendet er wie bei dir nichts. Morgen gehts weiter, muss ins Bett...

Kampi
11.09.2012, 23:06
Ok :)
Dank dir schon mal für die Mühe!

peterfido
12.09.2012, 19:21
So, ich habe mal in der php.ini die Fehleranzeige eingeschaltet. Da kommt dann bei meinem Senden-Programm, die Meldung, dass er /dev/ttyAMA0 nicht öffnen kann. Über echo kommt keine Fehlermeldung. Leider komm ich grad nicht weiter, da meine 2GB Karte voll ist. Ich muss erstmal das Backup von gestern zurückspielen und dann gibt es Essen. Die Probleme hatte ich alle mit dem Apache nicht...

Kampi
12.09.2012, 19:27
Hey,

danke fuer die Muehe.......wie hast du dir den Fehler anzeigen lassen?
Per PHP ueber den Shell_exec Befehl oder hast du das via C Programm getestet?
Aber die Erkenntnis bestaetigt meine Theorie, dass es sich um ein Rechteproblem handelt.
Nur die Frage ist....wie stelle ich es um.
Was sagt den dein Bauchgefuehl? Hat man mit Apache weniger Probleme als mit Lighttpd?
Weil Performancetechnisch ist das Raspberry Pi durch die Raspian Distribution ja recht gut dabei, sodass ich denke das da Apache nicht so ins Gewicht schlagen sollte.......

peterfido
12.09.2012, 21:46
Der Fehler wird mir auf dem WebIf angezeigt, da ich $output einfach noch mit einblende.
Was mich stört ist, dass ich als user root arbeite. Es wird mir auch root als php-Benutzer angezeigt. Ich deinstalliere gerade php und danach lighthttp um dann (ab morgen) den apache zu testen.

peterfido
13.09.2012, 17:38
So, Apache hat auch nicht geholfen.

Ich nutze jetzt das senden Programm, welches per sudo aufgerufen wird. Dazu muss noch die Passwortabfrage deaktiviert werden.

Also Befehl
visudo in die Konsole eingeben

Dort dann die Zeile

www-data ALL=(ALL) NOPASSWD: /var/scripte/senden

am Ende einfügen.

Jetzt wird für das eine Senden Programm kein Kennwort bei sudo verlangt.

Meine Webseite zum Testen:


<html>
<head>
<title>Raspberry Pi PHP- Server - UART Test</title>
</head>
<body>
<h1>Raspberry Pi PHP- Server - UART Test</h1>
<div id="nav"><a href="index.php?safemode=1">Safemode?</a> <a href="index.php">Neu laden</a></div>
<div id="main"><h2>RasPi-Sendetest<h2><br>
<?php
header("Cache-Control: no-cache, must-revalidate");
echo "<br>";
echo "Befehl eingeben";
echo "<br>";

if (isset($_GET["safemode"])) {
if( ini_get('safe_mode') ){
echo "<br>Safemode an<br>";
}else{
echo "<br>Safemode aus<br>";
}
};
if (isset($_POST["befehl"])) {
if ($_POST["befehl"]!=""){
$befehl="sudo /var/scripte/senden ".$_POST["befehl"];
$output = shell_exec($befehl);
}
};

echo "<form method='post' action='index.php'>";
echo "<input type='text' maxlength='40' size='40' id='1' name='befehl' value='HalloWelt' />";
echo "<br>";
echo ".$output"
?>

<input type="submit" value="Senden"> </form>
</div>
</body>

Mein Senden Programm:


// 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)
{
int res=write(fd, &c, 1);
if (res<0) printf("Fehler beim Senden\n");
return res;
}

int init()
{
/*** 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()
{
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"))){
exit(1);
}

fgets(buff, sizeof(buff), in);
pclose(in);
laenge=strlen(buff);
for(i=0;i<laenge;i++)
{
if(buff[i]==' ') anzahl++;
}
return anzahl;
}


int main(int argc, char** argv)
{
char c;
char vBuf[100]="",*pBuf;
char buffer [100]="";
char buffer1 [100]="";
int i=0;
int anzahl;
anzahl=einmal();
if (anzahl>0){
anzahl++;
sleep(anzahl);
}
init();
if (argc == 1) { //Wenn nichts angegeben, dann Uhrzeit und Datum senden
time_t rawtime;

struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );

strftime (buffer,80,"U%H:%M:%S",timeinfo); //Uhh:mm:ss
pBuf=buffer;
strcat(pBuf,"\r");
while(pBuf && *pBuf) //Zeichen einzeln senden
send(*pBuf++);

strftime (buffer,80,"D%d.%m.%y",timeinfo); //Dtt.mm.yy
pBuf=buffer;
strcat(pBuf,"\r");
sleep(1);
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
strncpy(buffer,argv[1],76);
buffer[76]='\0';
}else{
strcpy(buffer,argv[1]);
if (argc >2){
for (i=2;i<argc;i++){
if(strlen(buffer)+strlen(argv[i])>75){
strcat(buffer," ");
strncat(buffer,argv[i],(76-strlen(buffer)));
buffer[76]='\0';
break; //exit for in c...
}else{
strcat(buffer," ");
strcat(buffer,argv[i]);
}
}
}
}
int o=0;
for(i=0;i<strlen(buffer);i++){
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
o++;
}
}
pBuf=buffer1;
strcat(pBuf,"\r"); //CHR(13) anfügen, damit der AVR auswertet
while(pBuf && *pBuf){ //Zeichen einzeln senden
send(*pBuf++);
}
}
close (fd);
return 0;
}

Kampi
14.09.2012, 19:20
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?

peterfido
14.09.2012, 20:20
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.

Kampi
14.09.2012, 20:31
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?

peterfido
14.09.2012, 22:32
Normal schon, ich habe allerdings noch keinerlei Erfahrungen auf dem Raspi mit SPI oder I²C.

Anbei mal eine Testseite (index1.php)


<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


// 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


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:


// 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;
}

Kampi
14.09.2012, 23:02
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();

?

peterfido
14.09.2012, 23:22
Zu 1:
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.

Kampi
14.09.2012, 23:45
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?

peterfido
14.09.2012, 23:54
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.

Kampi
14.09.2012, 23:59
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

peterfido
15.09.2012, 00:23
Der Punkt ist dafür, dass php die Variable erkennen kann. Der Punkt wird nicht mitgesendet.

Kampi
15.09.2012, 09:34
Super :)
Dank dir für die Erklärung. Ich probiere das mit der Argumentübergabe nachher mal selber an meinem Raspberry aus.
Wenn das klappt bastel ich mir eine UART-Kommunikation per C-Programm zusammen und gucke das ich von dem Zugriff von lighttpd auf den AMA0 wegkomme.
Vielleicht stolpere ich ja mal zufällig über eine Lösung wie ich das hätte machen können.

Kampi
15.09.2012, 15:08
Ich wollte dein Programm nun mal selber ausprobieren aber es tut sich nichts.
Den PHP Code habe ich kopiert und ebenso den C-Code. Wenn ich den C-Code mit ./kommunikation "Ich" ausführen möchte, klappt es problemlos.
Allerdings passiert nichts über das WebIf.
Was muss ich da noch wie an den Rechten ändern?

peterfido
15.09.2012, 15:49
Ich habe es gerade mal auf meinem 3. Raspi getestet. Einfach das Programm mit dem in der ersten Zeile angegebenen Befehl compilieren und die php ins /var/www packen.
Das Programm bekommt automatisch beim Kompilieren die Rechte 755
die php bekommt manuell 644.

Der erste Test lief gleich erfolgreich. Als Webserver läuft lighttpd mit php5. Liegen die Programme bei Dir wie bei mir unter /var/scripte ?
Ich bin auf allen meinen Raspis immer als root unterwegs. Der lighttpd läuft als www-data.

Kampi
15.09.2012, 15:57
Ok es funktioniert in der Originalversion.
Dann habe ich wohl unbeabsichtigt was falsch gemacht als ich ein paar Sachen angepasst habe.

Edit: Banaler Fehler.....hab im Dateipfad von der PHP Datei das /www vergessen.....

peterfido
15.09.2012, 16:05
Schön :o, dann kann es ja jetzt weiter gehen..

Kampi
15.09.2012, 17:25
Ja mal schauen was sich damit machen lässt :)
Wenn ich was über den UART senden möchte brauch ich root-Rechte?
Wenn mir die Datei dann im FileZilla mit root und www-data als Besitzer angezeigt wird sollte das ja dann klappen wenn ich root-Rechte benötige oder?

peterfido
15.09.2012, 17:58
Mit sudo klappt das senden per UART. Wichtig ist halt, die Kennwortabfrage per visudo für das Senden Programm abzuschalten, wie weiter oben beschrieben.
Auf meinem ersten Raspi ging es auch ohne sudo. Allerdings habe ich da vorher viel hin und her (de)installiert und probiert...

Kampi
15.09.2012, 18:49
Juhu es klappt :)
Hab dein Programm bischen umgemodelt und nun wird jedesmal ein Text gesendet wenn ich im WebIf den Button drücke.
Waren zwar ein paar ein paar Fallstricke dabei (z.B. if (argc >= 1) habe ich auf if (argc == 1) stehen gelassen und mich gewundert warum es nicht klappt ;) )
Ist dieser Teil hier:



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 öffnen 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;
}


Immer derselbe für jedes Programm wo man den UART verwenden will?
Im Moment lasse ich die Baudrate noch bei 115200, weil das die Defaulteinstellung für die Baudrate ist und ich noch kein Teil im Startscript habe um die Baudrate umzustellen.
Morgen wollte ich dann auf meiner Homepage noch eine Anleitung schreiben wie man den UART per Web verwendet und wollte deinen Namen da erwähnen, weil du einen nicht zu vernachlässigen Anteil an der Problemlösung (und dem Verständnis) beigetragen hast, wenn du nichts dagegen hast :)

Edit:
Mittlerweile habe ich es auch geschafft meine eigene Sendenroutine zu implementieren.
Bei dem Befehl:

res=write(fd, &c[Counter], 1);

Dort wo jetzt &c[Counter] steht, wird ein Pointer erwartet und deswegen wird dort der Inhalt der Speicherzelle, die für &c[Counter] steht, übergeben?
Sehe ich das so richtig?
Und dann noch eine Frage zu der Variable "fd". Diese steht ja für den Filedescriptor und wird bei der Write Funktion an der Stelle erwartet.
Und diese Variable nimmt den Wert 0 an wenn der UART erfolgreich geöffnet wurde?

Klebwax
15.09.2012, 20:23
Mittlerweile habe ich es auch geschafft meine eigene Sendenroutine zu implementieren.
Bei dem Befehl:
einfach
res=write(fd, &c[Counter], 1);

Dort wo jetzt &c[Counter] steht, wird ein Pointer erwartet und deswegen wird dort der Inhalt der Speicherzelle, die für &c[Counter] steht, übergeben?
Sehe ich das so richtig?
Und dann noch eine Frage zu der Variable "fd". Diese steht ja für den Filedescriptor und wird bei der Write Funktion an der Stelle erwartet.
Und diese Variable nimmt den Wert 0 an wenn der UART erfolgreich geöffnet wurde?

Nein, es ist ein Index für den offenen File. Jeder offene File hat eine eigene Nummer. Ohne die wüßte write ja nicht, wohin es schreiben soll. Hat auch mit UART direkt nichts zu tun, das ist auch nur ein File.

Versuchs doch mal mit "man write", und wenn du die man-pages nicht auf deinem System hast, einfach in Google.

Wenn man etwas menschenlesbares ausgeben will, ist häufig fprintf() einfacher zu benutzen.

MfG Klebwax

peterfido
15.09.2012, 20:29
In der Init() wird die Schnittstelle zum Schreiben geöffnet. Möchtest Du lesen und schreiben, dann wird entweder

fd = open(MODEMDEVICE, O_WRONLY | O_NOCTTY);
.
.
.
newtio.c_cc[VMIN] = 1; /* blocking read until 1 chars received */

gegen

fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
.
.
.
.
newtio.c_cc[VMIN] = 0; /* blocking read until 1 chars received */


getauscht oder du öffnest die Schnittstelle ein zweites Mal nur zum lesen. Die zweite Änderung (newtio.c_cc[VMIN] = 0; ) braucht es dann aber trotzdem, damit
das Programm nicht beim Read blockiert wird, wenn nichts kommt. Dafür steigt dann durch das ständige Einlesen der UART die Prozessorlast.

Die Init(); Sub habe ich im Internet gefunden und da sie funktionierte, habe ich sie so belassen. Möglich, dass diese noch optimiert werden kann. Bei C werden überwiegend nur
die Adressen statt der Inhalte der Variablen übergeben. Es wird also die Schnittstelle geöffnet und die Adresse in fd zurückgegeben. Da schreibt dann die send(); immer nur ein Zeichen
rein und dieses wird dann unmittelbar über UART ausgegeben. Mehrere Zeichen "auf einmal" senden habe ich nicht probiert. Möglich, dass der Output Buffer irgendwann überläuft.

Tritt beim Senden kein Fehler auf, wird als Ergebnis (res Abkürzung für result) 0 zurückgegeben.
int res=write(fd, &c, 1);

Diese Schleife


while(pBuf && *pBuf){ //Zeichen einzeln senden
send(*pBuf++);

habe ich auch im Netz gefunden. Man könnte auch die Zeichen gleich in der Schleife, wo die Umlaute angepasst werden, senden. Das hatte ich zwischendurch auch im kombinierten Senden/Empfangen Programm gemacht. Habe diese Idee dann aber, wie gesagt wegen der Prozessorlast, wieder verworfen.

Die Geschwindigkeit hatte ich Anfangs auf 38400 Baud. Später habe ich mich dann bei 19200 eingependelt. 115200 würde mein AVR nicht schaffen (die Abweichung wäre zu hoch), da ich kein Baudratenquarz nutze.
19200 ist für meine Anwendung noch ausreichend schnell und ich habe noch vor, den TX-Pin des Raspi mit 2 parallel geschalteten RX zu verbinden, da ich noch eine externe Uhr ansteuern möchte. Da ist das Kabel dann knapp 1m lang. Da ist langsamer besser.

Ansonsten ist C noch recht neu für mich..

Kampi
15.09.2012, 20:34
Ah ok.
Sprich mit der Zeile

fd = open(MODEMDEVICE, O_WRONLY | O_NOCTTY);

wird ein File geöffnet welches durch Modemdevice deklariert wurde. Dieses File ist in dem Programm ttyAMA0. Anschließend wird gesagt, das man dort nur reinschreiben will.
Und die Zahl die dem File dann zugewiesen wird, wird unter "fd" gespeichert?
Und ich vergesse immer wieder, dass bei dieser Geschichte alles über Dateien abläuft ;)
Bin noch bisl zu sehr an µC gewöhnt wo man direkt den UART anspricht. Das das unter dem Linux über eine Datei abläuft und nicht direkt über den UART vergesse ich gerne ;)
Ist halt doch ein bischen was anderes als eine 8-Bit Maschine.....gerade was solche Sachen angeht.
Ich wusste z.B. auch nicht, dass man mit argc und argv Daten in eine Mainloop übergeben kann. Ich kenne C bisher nur von 8-Bit Controllern und dort wird sowas (meines Wissens nach) nicht benutzt.
Aber schön das man durch sowas banales wie den UART benutzen noch so eine ganze Menge lernen kann ^.^

Edit: Da war der Peter etwas fixer :)
Ja die Baudrate senke ich denke ich mal auch noch, da auch ich kein Baudratenquarz benutze.
Aber gut....dann verwende ich die "Init"-Routine einfach so weiter. Wollte nur sicher gehen, nicht das es in anderen Programmen dann nicht funktioniert und ich suche mich doof :D

peterfido
15.09.2012, 20:40
argc und argv haben sich so eingebürgert. Du könntest diese auch anders benennen. Wichtig ist der Typ. Es wird halt zuerst übergeben, wieviele Parameter kommen und anschließend dann das Parameter Array.

Bei Linux ist der Hintergedanke, dass alles irgendwie nur ein File ist. Ansonsten stimmt das wohl soweit. Wobei die Zahl dann eine Adresse/Pointer ist.

Kampi
15.09.2012, 20:47
Bei Linux ist der Hintergedanke, dass alles irgendwie nur ein File ist.

Und das, finde ich ist die Krux die man immer im Hinterkopf halten muss, wenn man von 8-Bit Controllern auf Embedded Linux umsteigt ;)
Aber wenigstens klappt das Senden nun problemlos und das ganze drumherum sitzt auch....dann kann ich mich ja jetzt mit dem Empfangen beschäftigen... ;)

Klebwax
15.09.2012, 21:22
Ich wusste z.B. auch nicht, dass man mit argc und argv Daten in eine Mainloop übergeben kann. Ich kenne C bisher nur von 8-Bit Controllern und dort wird sowas (meines Wissens nach) nicht benutzt.

Das liegt daran, daß die "Mainloop" des Prozessors Linux ist. Das main() eines Userprogramms ist normalerweise (aber nicht immer) keine Loop, sondern endet. Ein Programm ist einfach eine Funktion, die von Linux aufgerufen wird, und die kann natürlich Parameter haben.

MfG Klebwax

Kampi
15.09.2012, 21:35
Dürfen die Parameter die man übergibt nur eine bestimmte Länge haben?
Weil wenn ich z.B. eintippe

"IchbineinHund"

kommt im Terminal:

IchbineinHundˆ@
[ 3720.868847] Unhandled fault: alignment exception (0x011) at 0x00000001

Bei dem Text "Ich" kommt das nicht, egal wie oft ich das sende.

peterfido
15.09.2012, 21:49
Das sieht nach einer zu klein dimensionierten Variable aus. Ist die char groß genug dimensioert, dann fehlt das String-Abschlußzeichen "\0"


Wie ist denn der Quellcode?

Edit:
Ich nutze bis 76 Zeichen in einem Rutsch. Es können auch mehr sein, jedoch breche ich dann halt ab.

Kampi
15.09.2012, 22:04
Der Quellcode lautet:




// Compile with: gcc /var/scripte/senden.c -o /var/scripte/senden

//#include <iostream>
//using namespace std;
#include <sys/types.h>
#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 B115200 // Baudrate


char MODEMDEVICE[]= "/dev/ttyAMA0"; // Device zum Senden
struct termios newtio={}; // Variable für "termios.h"
volatile int fd; // File descriptor


unsigned char Send_UART(char c[]) // Gibt ein einzelnes Zeichen über UART (als fd geöffnet) aus.
{
int res;
char Counter;
char lenght = 0x00;

lenght = strlen(c); // Länge des Strings feststellen

while(Counter < lenght)
{
res = write(fd, &c[Counter], 1);
Counter++;
}

Counter = 0x00; // Counter reseten
res = write(fd, &"\r", 1); // CR senden
res = write(fd, &"\n", 1); // LF senden
}


int UART_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 öffnen 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 main(int argc, char** argv) //Programmstart
{
// Variablen für das Hauptprogramm
char buffer[] = "";
int Laenge = 0x00;

UART_Init(); // Schnittstelle öffnen und parametrieren
Laenge = strlen(argv[1]);
strncpy(buffer, argv[1], Laenge); // Kopiert den zweiten Parameter der übergeben wurde in den String "Buffer

if (argc >= 1)
{
printf("Text empfangen\n"); // Empfangsbestätigung senden
Send_UART(buffer);
}

close (fd);
return 0;
}



Die Länge für strncpy ist variabel und richtet sich nach der Länge des ersten Argumentes.

Klebwax
15.09.2012, 23:25
Zu deinem Code läßt sich eine Menge sagen, hier nur einiges.

"volatile" hat in einem Anwenderprogramm nichts zu suchen.

Zeichen sind chars, alles andere, Zähler etc sind ints. Und was soll die Hex-Notation?

char lenght = 0x00;

Also:
int length = 0;

nicht:
unsigned char Send_UART(char c[])

sondern
int Send_UART(char *c)

Und wenn man einer Funktion einen Returnwert verpasst, sollte man ihn auch liefern.

Und statt

length = strlen(c); // Länge des Strings feststellen

while(Counter < lenght)
{
res = write(fd, &c[Counter], 1);
Counter++;
}

einfach
res = write(fd, c, strlen(c));

Dann auch noch res bearbeiten und feststellen, ob auch alles geschrieben wurde.

Und in main()

char buffer[] = "";

Das ist ein array mit der Länge 1, das mit einem Null-Byte gefüllt ist. Ist es das, was du willst?

UART_init() liefert den Filedescriptor als Rückgabewert, keiner sieht ihn an. Das funktioniert, weil fd global ist. Wenn man das will, warum dann als Returnwert?

Laenge = strlen(argv[1]);
strncpy(buffer, argv[1], Laenge);

Warum kopierst du eigentlich argv[1] in einen Buffer und sendest es nicht gleich?

char LineEnd[] = "\r\n";

write(fd, argv[1], strlen(argv[1]);
write(fd, LineEnd, 2);

Ich hoffe, das ist nicht allzu schlimm ausgefallen

MfG Klebwax

[/CODE]

Kampi
15.09.2012, 23:54
Huu danke für die ganzen Anregungen.
Zu dem Thema "Warum kopierst du eigentlich argv[1] in einen Buffer und sendest es nicht gleich?"
Ich habe es probiert aber es wollte nie klappen. Ich probiere dann mal das was du vorgeschlagen hast.
Das mit dem Hex habe ich mir so angewöhnt ;)

Bin halt recht neu in C und ich programmiere damit nicht ganz so viel von daher weiß ich nicht wie man was vereinfachen kann :)
Aber danke für die Tipps. Ich setze sie direkt mal um!

Warum hat volatile nichts in einem Anwenderprogramm zu suchen?

Edit: Habe es jetzt mal gemacht. Jetzt erscheint auch der Fehler nicht mehr wenn ich einen längeren Text sende.
Im Endeffekt hat das Programm ja nun zwei Möglichkeiten zu senden oder? Einmal mit

write(fd, argv[1], strlen(argv[1]));
write(fd, LineEnd, 2);

und einmal mit

int Send_UART(char *c)
{
int res;
char LineEnd[] = "\r\n";

res = write(fd, c, strlen(c));
write(fd, LineEnd, 2);
}

Klebwax
16.09.2012, 00:51
Warum hat volatile nichts in einem Anwenderprogramm zu suchen?


volatile bedeutet, daß eine Variable auch von außerhalb eines Programmkontextes geändert werden kann, ohne daß das Programm etwas dazu tut. Dies soll der Compiler berücksichtigen. Das kann nicht vorkommen, alle Variablen eines Userprogramms gehören nur diesem Programm.

Ja, mit den Angewohnheiten ist das so eine Sache. Die Hexnotation heißt, das ist nicht als Zahl gemeint, sondern als Bitmuster. Darauf wendet man typischerweise Bitoperationen an.

Es geht nicht so sehr um vereinfachen, sondern darum, klar und gradlinig Anweisungen an den Rechner zu formulieren, die man selbst (auch noch in ein paar Wochen) und der Rechner verstehen.

Du willst die Parameter für /dev/ttyx setzen und argv[1] mitangehängtem CR/LF dahin schreiben. Das ist nicht wirklich viel, alles in allem ein 10-Zeiler. Funktionen sind da eigentlich nicht nötig, sie stören nur den Lesefluß. Je mehr Zeilen, desto mehr potentielle Fehler.

Du müßtest auch einige Warnings beim Übersetzen bekommen haben. Nimm sie ernst und stell sie ab. 0 Warnings und 0 Errors ist das Ziel.

MfG Klebwax

Kampi
16.09.2012, 00:58
Du müßtest auch einige Warnings beim Übersetzen bekommen haben. Nimm sie ernst und stell sie ab. 0 Warnings und 0 Errors ist das Ziel.



Ja bei den Zeilen die du geschrieben hast war eine Warnung dabei.
Mein altes Programm hat beim kompilieren keine Warnung und keinen Fehler ausgegeben. Bei Warnings versuche ich die Quelle raus zu finden und manchmal gelingt es mir auch :)
Also in dem Punkt bin ich schon recht streng mit mir selber ^.^
Das Hexzahlen typischerweise nur bei Bitoperationen genutzt werden wusste ich gar nicht. Wieder was gelernt :)

Klebwax
16.09.2012, 01:07
Das Hexzahlen typischerweise nur bei Bitoperationen genutzt werden wusste ich gar nicht. Wieder was gelernt :)

Nicht Hexzahlen, sonder die hexadezimale Notation. Kein Mensch zählt Hex, gezählt wird dezimal. Deswegen versteht, da von Menschen gemacht, jeder Assembler und jeder Compiler dezimale Zahlen. In der CPU selbst ist es immer binär.

MfG Klebwax