ubuntuusers.de

C-Programmierung / frage zu "static volatile int"

Status: Gelöst | Ubuntu-Version: Ubuntu 16.04 (Xenial Xerus)
Antworten |

sveni-lee

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

ich habe ein kleines Skript das auf Impusle an einem GPIO reagiert und einen counter um den Faktor 1 erhöht.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>


// What GPIO input are we using?
//   This is a wiringPi pin number

#define   BUTTON_PIN   9

// globalCounter:
//   Global variable to count interrupts
//   Should be declared volatile to make sure the compiler doesn't cache it.

static volatile int globalCounter = 0 ;


/*
 * myInterrupt:
 *********************************************************************************
 */

void myInterrupt (void)
{
  ++globalCounter ;
}


/*
 *********************************************************************************
 * main
 *********************************************************************************
 */

int main (void)
{
  int myCounter = 0 ;

  if (wiringPiSetup () < 0)
  {
    fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
  {
    fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    return 1 ;
  }


  for (;;)
  {
    printf ("Waiting ... ") ; fflush (stdout) ;

    while (myCounter == globalCounter)
      delay (100) ;

    datei = fopen ("/var/strom/stromzähler1", "w");
    fprintf (datei, "%d\n", globalCounter);
    fclose (datei);
    myCounter = globalCounter ;
  }

  return 0 ;
}

das funktioniert auch super. nun habe ich das "Problem", dass der Counter bei jedem Neustart bei null beginnt was hierraus resultieren dürfte:

1
static volatile int globalCounter = 0

gibt es eine Möglichkeit das ganze so zu gestalten das diese Variable gleich /var/strom/stromzähler1 zu setzten?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

sveni-lee schrieb:

gibt es eine Möglichkeit das ganze so zu gestalten das diese Variable gleich /var/strom/stromzähler1 zu setzten?

Einfach: Du liest einfach den Dateiinhalt am Anfang von main() oder bevor Du die Variable das erste Mal benutzt.

Und: nein, das hat überhaupt nichts mit volatile zu tun.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

sveni-lee schrieb:

gibt es eine Möglichkeit das ganze so zu gestalten das diese Variable gleich /var/strom/stromzähler1 zu setzten?

Du kannst die Datei natürlich durch dein C-Programm auslesen lassen (https://www.tutorialspoint.com/cprogramming/c_file_io.htm) und den Wert parsen oder dem Programm den Start-Wert als Argument übergeben (https://www.tutorialspoint.com/cprogramming/c_command_line_arguments.htm).

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

vielen Dank für die schnellen Antworten...

also müsste ich unter main() einfügen?

1
2
3
4
FILE *fp;
fp = fopen("/var/strom/stromzähler1", "r");
int globalCounter = fp
fclose(fp);

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

sveni-lee schrieb:

also müsste ich unter main() einfügen?

1
2
3
4
FILE *fp;
fp = fopen("/var/strom/stromzähler1", "r");
int globalCounter = fp
fclose(fp);

Statt zu Fragen, ob dein Code funktioniert, probier es einfach aus - dann schau dir die Compiler-Meldungen an (mit -Wall kompilieren), und wenn du ohne Fehler und Warnungen bauen kannst, prüfe, ob das Programm aucht das tut, was du erwartest. Aus den eigenen Fehlern zu lernen ist mit am effektivsten.

Du musst die Datei öffnen, solltest anhand des Rückgabewertes prüfen, ob das geklappt hat, dann die Datei parsen (und sinnvollerweise prüfen, ob du genau einen Integer findest) - das kannst du wie im verlinkten Tutorial gezeigt mit fscanf() machen (die Manpages zu den Funktionen lesen!) - nur dass du einen int statt statt einem Zeichen-Array erhalten willst - und dann die Datei wieder schließen. Die Variable für den File-Descriptor willst du nicht mit dem eingelesenen Wert aus der Datei mischen, denn das eine ist ein FILE Pointer und das andere ein int...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>

int parse_value_from_file(const char *filename, int *value)
{
    FILE *fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "could not open %s\n", filename);
        return 1;
    }
    int count = fscanf(fd, "%d", value);
    fclose(fd);
    if (count != 1) {
        fprintf(stderr, "could not parse %s successfully\n", filename);
        return 2;
    }
    return 0;
}

int main()
{
    const char filename[] = "test.txt";
    int value, error;
    error = parse_value_from_file(filename, &value);
    if (error) {
        return error;
    }
    fprintf(stdout, "value from file was %d\n", value);
    return 0;
}

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

seahawk1986 schrieb: Statt zu Fragen, ob dein Code funktioniert, probier es einfach aus - dann schau dir die Compiler-Meldungen an (mit -Wall kompilieren), und wenn du ohne Fehler und Warnungen bauen kannst, prüfe, ob das Programm aucht das tut, was du erwartest. Aus den eigenen Fehlern zu lernen ist mit am effektivsten.

kann ich leider grad nicht... bin noch auf der Arbeit und wollte schon mal ein wenig vorarbeiten

seahawk1986 schrieb: Du musst die Datei öffnen, solltest anhand des Rückgabewertes prüfen, ob das geklappt hat, dann die Datei parsen (und sinnvollerweise prüfen, ob du genau einen Integer findest) - das kannst du wie im verlinkten Tutorial gezeigt mit fscanf() machen (die Manpages zu den Funktionen lesen!) - nur dass du einen int statt statt einem Zeichen-Array erhalten willst - und dann die Datei wieder schließen. Die Variable für den File-Descriptor willst du nicht mit dem eingelesenen Wert aus der Datei mischen, denn das eine ist ein FILE Pointer und das andere ein int...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>

int parse_value_from_file(const char *filename, int *value)
{
    FILE *fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "could not open %s\n", filename);
        return 1;
    }
    int count = fscanf(fd, "%d", value);
    fclose(fd);
    if (count != 1) {
        fprintf(stderr, "could not parse %s successfully\n", filename);
        return 2;
    }
    return 0;
}

int main()
{
    const char filename[] = "test.txt";
    int value, error;
    error = parse_value_from_file(filename, &value);
    if (error) {
        return error;
    }
    fprintf(stdout, "value from file was %d\n", value);
    return 0;
}

Das ist mein erstes c-skript überhaupt... so wie ich das jetzt sehe, wird damit die datei überprüft so wie zuvor von Dir beschrieben, richtig? Aber auf deinen Rat hin habe ich mir das Skript noch einmal genau angesehen. Der wert der in die Datei geschrieben wird ist globalCount und dieser wird hier gebildet

1
2
3
4
void myInterrupt (void)
{
  ++globalCounter ;
}

also wird bei jedem interrupt am GPIO die Variable globalCount um 1 erhöht und dann in die Datei geschrieben. Da void ja eine Funktion ohne Rückgabewert ist, müsst man dann nicht den Wert für globalCount schon vorher festlegen damit die er nicht bei jedem Neustart von null beginnt oder aber man müsste in die void Funktion schreiben das die Variable die augelesen wurde /ver/log/stromzähler1 um eins erhöt wird?

Ich bin verwirrt....

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

sveni-lee schrieb:

kann ich leider grad nicht... bin noch auf der Arbeit und wollte schon mal ein wenig vorarbeiten

Einen C-Compiler kann man notfalls auch auf einem Andoid-Gerät nutzen: https://termux.com/ (nur mit dem GPIO-Zugriff wie beim Raspberry Pi wird es dann etwas schwierig) 😉

Das ist mein erstes c-skript überhaupt... so wie ich das jetzt sehe, wird damit die datei überprüft so wie zuvor von Dir beschrieben, richtig?

Dein Programm setzt einen Callback für einen Interrupt und wartet dann, bis sich der Wert von globalCounter ändert. Dann schreibt es den Wert ohne Rücksicht auf Verluste in die Datei und das Spielchen beginnt von vorne.

Mein Beispielprogram versucht die Datei Test.txt im Arbeitsverzeichnis zu öffnen und dann einen Integer daraus zu lesen. Wenn es nicht klappt, beendet es sich mit einer Fehlermeldung und einem eindeutigen Exit-Code (mein ZSH-Theme druckt den Exit-Wert im auf den Befehl folgenden Prompt neben einem , wenn er ungleich 0 ist), ansonsten schreibt es eine Meldung mit dem ausgelesenen Wert:

--- projects/c » gcc -Wall parse_num_from_file.c -o parse_num 

--- projects/c » ./parse_num
could not open test.txt
--- projects/c » touch test.txt                          1 ↵
--- projects/c » ./parse_num   
could not parse test.txt successfully
--- projects/c » echo "42" > test.txt                    2 ↵
--- projects/c » ./parse_num         
value from file was 42
--- projects/c » echo "blah 42" > test.txt                            
--- projects/c » ./parse_num
could not parse test.txt successfully
--- projects/c »                                         2 ↵ 

Aber auf deinen Rat hin habe ich mir das Skript noch einmal genau angesehen. Der wert der in die Datei geschrieben wird ist globalCount und dieser wird hier gebildet

1
2
3
4
void myInterrupt (void)
{
  ++globalCounter ;
}

also wird bei jedem interrupt am GPIO die Variable globalCount um 1 erhöht und dann in die Datei geschrieben. Da void ja eine Funktion ohne Rückgabewert ist, müsst man dann nicht den Wert für globalCount schon vorher festlegen damit die er nicht bei jedem Neustart von null beginnt oder aber man müsste in die void Funktion schreiben das die Variable die augelesen wurde /ver/log/stromzähler1 um eins erhöt wird?

globalCounter ist eine globale Variable, die in Zeile 17 von deinem Programm mit dem Wert 0 initialisiert wird. Was du jetzt noch machen musst, ist diese Variable mit dem Start-Wert aus der Datei zu versehen, bevor du den Interrupt aktivierst - das könnte in etwa so aussehen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>


// What GPIO input are we using?
//   This is a wiringPi pin number

#define   BUTTON_PIN   9

// globalCounter:
//   Global variable to count interrupts
//   Should be declared volatile to make sure the compiler doesn't cache it.

static volatile int globalCounter = 0 ;


/*
 * myInterrupt:
 *********************************************************************************
 */

void myInterrupt (void)
{
  ++globalCounter ;
}

int parse_value_from_file(const char *filename, int *value)
{
    FILE *fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "could not open %s\n", filename);
        return 1;
    }
    int count = fscanf(fd, "%d", value);
    fclose(fd);
    if (count != 1) {
        fprintf(stderr, "could not parse %s successfully\n", filename);
        return 2;
    }
    return 0;
}


/*
 *********************************************************************************
 * main
 *********************************************************************************
 */

int main (void)
{
  const char filename[] = "/var/strom/stromzähler1";

  int error = parse_value_from_file(filename, &globalCounter);

  if (error)
      fprintf(stderr, "unable to read initial value from %s, starting at %d", filename, globalCounter);

  int myCounter = globalCounter;

  if (wiringPiSetup () < 0)
  {
    fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
  {
    fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    return 1 ;
  }


  for (;;)
  {
    printf ("Waiting ... ") ; fflush (stdout) ;

    while (myCounter == globalCounter)
      delay (100) ;

    datei = fopen (filename, "w");
    fprintf (datei, "%d\n", globalCounter);
    fclose (datei);
    myCounter = globalCounter ;
  }

  return 0 ;
}

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

hmm... das kompilieren klappt so nicht

strompi@strompi:~/WiringOP-Zero/examples$ gcc -Wall stromzaehler1.c -o test -lpthread -lwiringPi
stromzaehler1.c: In function ‘main’:
stromzaehler1.c:57:47: warning: passing argument 2 of ‘parse_value_from_file’ discards ‘volatile’ qualifier from pointer target type [-Wdiscarded-qualifiers]
   int error = parse_value_from_file(filename, &globalCounter);
                                               ^
stromzaehler1.c:30:5: note: expected ‘int *’ but argument is of type ‘volatile int *’
 int parse_value_from_file(const char *filename, int *value)
     ^
stromzaehler1.c:84:5: error: ‘datei’ undeclared (first use in this function)
     datei = fopen (filename, "w");
     ^
stromzaehler1.c:84:5: note: each undeclared identifier is reported only once for each function it appears in

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

Ah stimmt, da ist es einfacher den int myCounter aus der main() Funktion zu nehmen und damit die als volatile markierte globale Variable zu befüllen.

Der zweite Fehler stammt aus deinem Code aus dem ersten Post, da muss man die Variable datei ordentlich deklarieren - ich habe gerade keine Raspberry Pi zur Hand, aber so sollte es hoffentlich gehen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wiringPi.h>


// What GPIO input are we using?
//   This is a wiringPi pin number

#define   BUTTON_PIN   9

// globalCounter:
//   Global variable to count interrupts
//   Should be declared volatile to make sure the compiler doesn't cache it.

static volatile int globalCounter = 0 ;


/*
 * myInterrupt:
 *********************************************************************************
 */

void myInterrupt (void)
{
  ++globalCounter ;
}

int parse_value_from_file(const char *filename, int *value)
{
    FILE *fd = fopen(filename, "r");
    if (fd == NULL) {
        fprintf(stderr, "could not open %s\n", filename);
        return 1;
    }
    int count = fscanf(fd, "%d", value);
    fclose(fd);
    if (count != 1) {
        fprintf(stderr, "could not parse %s successfully\n", filename);
        return 2;
    }
    return 0;
}


/*
 *********************************************************************************
 * main
 *********************************************************************************
 */

int main (void)
{
  const char filename[] = "/var/strom/stromzähler1";
  int myCounter;
  int error = parse_value_from_file(filename, &myCounter);

  if (error) {
      myCounter = 0;
      fprintf(stderr, "unable to read initial value from %s, starting at %d", filename, myCounter);
  }

  globalCounter = myCounter;

  if (wiringPiSetup () < 0)
  {
    fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
  {
    fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    return 1 ;
  }


  for (;;)
  {
    printf ("Waiting ... ") ; fflush (stdout) ;

    while (myCounter == globalCounter)
      delay (100) ;

    FILE * datei = fopen (filename, "w");
    fprintf (datei, "%d\n", globalCounter);
    fclose (datei);
    myCounter = globalCounter ;
  }

  return 0 ;
}

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

jetzt hat das kompilieren ohne Fehler geklappt aber leider passiert gar nichts... es wird nichts in die Datei geschrien und das Programm wird sofort beendet.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

Tja, dann beginnt jetzt das fröhliche Debuggen... - welchen Exit-Code liefert das Programm? Hat der Nutzer, der das Programm ausführt, Schreibrechte für die Datei /var/strom/stromzähler1?

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

also den Programmstart habe ich hinbekommen, war mein Fehler beim Programmaufruf. Schreibrechtesind nicht das Problem da ich das Programm mit Sudo starte...

Das Programm wird auf jeden fall schon mall ausgeführt aber es reagiert sehr "nervös"

original program:

tail: /var/strom/stromcounter1: file truncated
1
tail: /var/strom/stromcounter1: file truncated
2
tail: /var/strom/stromcounter1: file truncated
3
tail: /var/strom/stromcounter1: file truncated
4
tail: /var/strom/stromcounter1: file truncated
5
tail: /var/strom/stromcounter1: file truncated
6

nach start des neuen Programm

tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
7
tail: /var/strom/stromcounter1: file truncated
.
.
.

auch wenn sich der Wert nicht ändert, wird die Datei neu geschrieben...

UPDATE: mein Fehler hatte was auskommentiert, warum auch immer. läuft jetzt

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

und jetzt die Königsdisziplin ☺

könnte man jetzt auch zusätzlich zum schreiben der Datei auch noch einen curl Befehl absetzten?

1
curl http://192.168.178.38:8082/set/javascript.0.Stromzähler.Normalstrom.Zählerstand_input?value={$globalCounter}

Hintergrund, damit könnte ich den Wert direkt per SimpleApi and iobroker senden...

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

Das kannst du mit der libcurl machen. curl selbst kennt ein Startargument, mit dem es dir C-Beispielcode für einen HTTP-Request erzeugen kann: https://ec.haxx.se/libcurl--libcurl.html

Dann musst du nur noch mit sprintf einen char-Array mit dem gewünschten Zählerwert zusammenbauen.

sveni-lee

(Themenstarter)

Anmeldungsdatum:
28. Mai 2013

Beiträge: 258

Ich hab da mal was versucht aus den diversen infos aus dem Netzt zusammenzubauen...

Das ganze funktioniert auch soweit!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int main (void)
{
  const char filename[] = "/var/strom/stromcounter1";
  int myCounter;
  int error = parse_value_from_file(filename, &myCounter);
  char simpleapi[50];
  char adresse[] = "http://192.168.178.38:8082/set/javascript.0.Stromzähler.Normalstrom.Zählerstand_input?value=";

  if (error) {
      myCounter = 0;
      fprintf(stderr, "unable to read initial value from %s, starting at %d", filename, myCounter);
  }

  globalCounter = myCounter;

  if (wiringPiSetup () < 0)
  {
    fprintf (stderr, "Unable to setup wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiISR (BUTTON_PIN, INT_EDGE_FALLING, &myInterrupt) < 0)
  {
    fprintf (stderr, "Unable to setup ISR: %s\n", strerror (errno)) ;
    return 1 ;
  }


  for (;;)
  {
    while (myCounter == globalCounter)
      delay (100) ;

    FILE * datei = fopen (filename, "w");
    fprintf (datei, "%d\n", globalCounter);
    fclose (datei);
    myCounter = globalCounter ;

    sprintf(simpleapi, "%s%d",adresse,globalCounter);
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, simpleapi);
    curl_easy_perform(curl);
  }

  return 0 ;
}

allerdings gibt es eine Rückmeldung vom vom API_server. die abgefangen werden müsste...

1
2
3
strompi@strompi:~/WiringOP-Zero/examples$ curl http://192.168.1.142:8082/set/javascript.0.Stromzaehler.Normalstrom.Zaehlerstand_input?value=3528
{"id":"javascript.0.Stromzaehler.Normalstrom.Zaehlerstand_input","value":3528,"val":3528}
strompi@strompi:~/WiringOP-Zero/examples$

diese müsste umgeleitet werden: /dev/null 2>&1

Antworten |