Forum: PC-Programmierung Linux/raspberryPi: Daemon für serielle Schnittstelle (Wetterstation) in C?


von Julian W. (julian-w) Benutzerseite


Lesenswert?

Hallo,

ich hab mir eine kleine Wetterstation (mit Extras) gebaut die soweit 
auch funktioniert. Angesteuert wird das ganze über die serielle 
Schnittstelle. Nun bräuchte ich für das RaspberryPI noch einen Dämon, 
der die serielle Kommunikation übernimmt.

Ich müsste periodisch z.B. alle 30 Sekunden eine gewisse Zeichenkette 
senden. Sobald alle Messungen abgeschlossen sind (Dauer ist variabel, 
von 1s bis zu 10s alles möglich) werden die Daten von der Wetterstation 
an das RPi gesendet. Auch kann die Wetterstation von sich aus spontan 
Daten senden.

Der Dämon müsste also alle x Sekunden eine Zeichenkette senden und immer 
lauschen, ob er was empfängt (und das empfangene auswerten und in einer 
Textdatei/MySQL-Datenbank speichern). Wie setzt ich das am besten um?

Ich habe mir schon Python (die Sprache die für das RPi gepusht wird) 
angeschaut komme damit aber nicht wirklich klar... sieht für mich nach 
Spaghetti aus.

In C hab ich immerhin das gefunden:
http://openbook.galileocomputing.de/linux_unix_programmierung/Kap07-011.htm

Ist das so ratsam? Wie würdet ihr das am besten implementieren? Ich 
frage mich nur wie ich das mit dem senden und empfangen sauber hinkriege 
(theoretisch könnte beides gleichzeitig passieren)...

Viele Grüße
Julian

von Rolf Magnus (Gast)


Lesenswert?

Julian W. schrieb:
> Der Dämon müsste also alle x Sekunden eine Zeichenkette senden und immer
> lauschen, ob er was empfängt (und das empfangene auswerten und in einer
> Textdatei/MySQL-Datenbank speichern). Wie setzt ich das am besten um?

Zum Beispiel mit einem Alarm-Timer und einfachem blockierenden Lesen.
Ungefähr so (Pseudocode):
1
volatile sig_atomic_t wecker = 0;
2
3
void handler(int sig)
4
{
5
    if (sig == SIGALRM)
6
        wecker = 1;
7
}
8
9
....
10
11
    // init
12
    signal(SIGALRM, handler);
13
    setitimer(ITIMER_REAL, x Sekunden);
14
15
    // Hauptschleife
16
    for (;;)
17
    {
18
        char buf[BUFSIZE];
19
        int ret = read(port, buf, BUFSIZE); // blockerend lesen
20
        if (ret < 0)                        // konnte nicht lesen
21
        {
22
            if (errno == EAGAIN)            // ein Signal!
23
            {
24
                if (wecker == 1)            // unser Alarm-Timer
25
                {
26
                    wecker = 0;
27
                    senden();
28
                }
29
            }
30
            else
31
            {
32
                // Lesefehler!
33
            }
34
        }
35
        else 
36
        {
37
            verarbeite_angekommene_daten(buf, ret);
38
        }
39
    }

von Karl (Gast)


Lesenswert?

Julian W. schrieb:
> Ich habe mir schon Python (die Sprache die für das RPi gepusht wird)
> angeschaut komme damit aber nicht wirklich klar... sieht für mich nach
> Spaghetti aus.

Wenn der Bauer nicht schwimmen kann, ist die Badehose schuld!

Was dein C-Programm in 20 Zeilen macht macht Python in 2. Wie da 
Spaghettis draus werden ist mir schleierhaft. Außerdem unterstützt 
Python so ziemlich alle Programmierparadigmen.

von imonbln (Gast)


Lesenswert?

Julian W. schrieb:
> Der Dämon müsste also alle x Sekunden eine Zeichenkette senden und immer
> lauschen, ob er was empfängt (und das empfangene auswerten und in einer
> Textdatei/MySQL-Datenbank speichern). Wie setzt ich das am besten um?

Wie man einen Deamon schreibt, da hast du ja schon was gefunden ;)
Ich denke für den Zitierten Teil wird poll dein freund sein.

von Julian W. (julian-w) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Zum Beispiel mit einem Alarm-Timer und einfachem blockierenden Lesen.
> Ungefähr so (Pseudocode):

Danke das sieht schon mal sehr gut aus :D

Eine Frage hab ich noch:
Erzeuge ich damit nicht eine CPU-Last von 100%? Weil die for-Schleife 
iwrd ja nicht "gebremst" und die CPU würde damit immer auf max. 
Taktfrequenz laufen oder täusche ich mich da?

von imonbln (Gast)


Lesenswert?

Julian W. schrieb:
> Erzeuge ich damit nicht eine CPU-Last von 100%? Weil die for-Schleife
> iwrd ja nicht "gebremst" und die CPU würde damit immer auf max.
> Taktfrequenz laufen oder täusche ich mich da?

der read ist blockierend, also wird der scheduler hier denn Prozess,
weg schieben bis der read Erfolg hat oder der Timeout zuschlägt.
Merke jeder Systemcall ist ein  "schedulling point".

ausserdem wie schon gesagt, ich würde dir statt es Pseudocode der selber
ein timeout bastelt denn POSIX systemcall poll ans Herz legen.

ungefähr so (Achtung auch pysdocode)
1
#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(x[0]))
2
3
struct pollfd  pfd[] = {
4
    { .fd = port , .events = POLLIN, }
5
};
6
7
while (1) {
8
  ret = poll(pfd, ARRAY_SIZE(pfd), timeout);
9
  if ( ret == 0)  {
10
    /* timeout */
11
    continue;
12
  } else if ( ret < 0 ) {
13
    fprintf( stderr "poll failes with %d=%s\n",
14
       errno, strerror(errno));
15
    exit(1);
16
  }
17
18
  for (i= 0; i < ARRAY_SIZE(pfd); i++) {
19
    if (!pfd[i].revents & POLLIN) {
20
      /* no event for fd*/
21
      continue;
22
    }
23
    ret = read(pfd[i].fd, buf, BUFSIZE);
24
    [...]
25
  }
26
}

von Rolf Magnus (Gast)


Lesenswert?

imonbln schrieb:
> ausserdem wie schon gesagt, ich würde dir statt es Pseudocode der selber
> ein timeout bastelt denn POSIX systemcall poll ans Herz legen.

Dabei hat man allerdings immer abhängig von der Laufzeit des restlichen 
Code Verzögerungen, die sich aufaddieren.
Deshalb habe ich da auch den Timer genommen, nicht etwa um einen Timeout 
selbst zu basteln, sondern um eine präzisere Zeitsteuerung zu haben. Mag 
sein, daß das im konkreten Fall nix ausmacht, aber viel komplizierter 
ist der Code ja nun auch nicht.
Übrigens empfielt es sich, bei der poll-Methode, den fd auf non-blocking 
umzustellen, damit read() nicht doch mal (dann eben ohne Timeout) 
blockiert, was laut man-Page sonst manchmal passieren kann.

von Rolf Magnus (Gast)


Lesenswert?

Ich habe gerade gesehen, daß mein Pseudocode noch einen Fehler hat:

Rolf Magnus schrieb:
> if (errno == EAGAIN)            // ein Signal!

Das müßte eigentlich heißen:
1
if (errno == EINTR)            // ein Signal!

EINTR bedeutet "interrupted system call", kommt also immer dann, wenn 
ein Systemcall wie z.B. read() durch ein Signal unterbrochen wurde.

von Julian W. (julian-w) Benutzerseite


Lesenswert?

Noch eine Frage zur seriellen Schnittstelle:

wie spreche ich die serielle Schnittelle mit C unter Linux (konkret 
Debian) am besten an, was ist also "best practice"?

Einfach /dev/tty*** öffnen? Eine Bibliothek wie die libserial benutzen? 
Ich muss nur ein paar Daten hin und her schieben bei 9600 Baud.

von foobar (Gast)


Lesenswert?

libserial ist aber C++.
Einfach nur Device öffnen geht auch, vorausgesetzt die Schnittstelle ist 
bereits richtig konfiguriert (Baudrate, Start-,Stop- und Datenbits)

von Julian W. (julian-w) Benutzerseite


Lesenswert?

Wie sieht es eigentlich mit dem Beispiel von dieser Seite aus:

http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

Ist das noch Stand der Technik? Oder sollte man es besser anders machen?

von Frank W. (wesoft) Benutzerseite


Lesenswert?

Julian W. schrieb:
> Wie sieht es eigentlich mit dem Beispiel von dieser Seite aus:
>
> http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html
>
> Ist das noch Stand der Technik? Oder sollte man es besser anders machen?

Klar ist das noch Stand der Technik. So verwende ich jedenfalls die 
serielle Schnittstelle immer.

Frank

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.