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

OP Persönliche Seite #3351117
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
Gast #3351201
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
    }
Gast #3351212
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.
Gast #3351223
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.
OP Persönliche Seite #3353931
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?
Gast #3354017
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
}
Gast #3354186
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.
Gast #3354192
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.

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.