ralphi: C/C++ Befehl time(NULL) falscher Wert?

Hi all,
weiß jemand, warum ich in C/C++ mit dem Befehl time(NULL) einen falschen Wert bekomme?
ca. 14:07 Unix-timestamp:
in C: 2130385884 sec
php: liefert 1458911223 sec

Ich möchte gerne den Timestamp zu einem Sensormodul (nRF24L01+) senden, zwengs Messwertzuordnungen.
Alles funktioniert wie gewünscht, außer Timestamp (.. und deuten des 1-Wire DS18B20 Messwerts - frag ich weiter unten auch gleich)
Auszug vom Pi2-Code:

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <RF24/RF24.h>
#include <stdio.h>
#include <time.h>
using namespace std;
//g++ -Ofast -mfpu=vfp -mfloat-abi=hard -march=armv7-a -mtune=arm1176jzf-s -Wall -I../ -lrf24-bcm mytest.cpp -o test2
// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 8Mhz
RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);

const uint64_t addresses[] = { 0x65646f4e31,0x65646f4e32};
unsigned long startTime, stopTime, counter, rxTimer=0;

int main(int argc, char** argv){
  radio.begin();                   
      radio.openWritingPipe(addresses[1]);
      radio.openReadingPipe(1,addresses[0]);
      
    uint8_t stamp[4] ;
    * stamp = time(NULL);
    radio.write( &stamp, sizeof(stamp)) ;
    cout << "stamp: " << (unsigned long)&stamp  << endl;
    radio.startListening();

    // hier dann das Messwert Empfangszeug
}

Zwengs 1-Wire Tempsensor DS18B20.
Vielleich weiß das auch Jemand?
Laut Datenblatt :
BYTE 0 TEMPERATURE LSB (50h)
BYTE 1 TEMPERATURE MSB (50h) (85°C)
Byte 1 Byte 0
S S S S S B6 B5 B4 - B3 B2 B1 B0 -1 -2 -3 -4
Ich bekomme MSB 136 LSB 45 (Byte1,0) Zimmertemp: ca. 20,9°C
1000 1000 - 0010 1101
Also: 2,13°C - (könnt natürlich 21,3 sein - dann deut ich aber das DB falsch)

Beim ATTiny84:

..
Sensor.reset();
  Sensor.skip();
  Sensor.write(0xBE,1);   // Read Scratchpad Byte0 LSB byte1 MSB
    for ( int i = 0; i < 2; i++) {       // 9 bytes brauch nur 2 davon laut Datenblatt
      temp[i] = Sensor.read();
    }    
..

Viele Grüße aus LA
ralphi

--
"Nicht alles was einfach ist, ist genial, aber alles was genial ist, ist einfach" - Albert E.
  1. Hallo,

    weiß jemand, warum ich in C/C++ mit dem Befehl time(NULL) einen falschen Wert bekomme?

    nein, aber hast du das mal isoliert getestet?

    ca. 14:07 Unix-timestamp:
    in C: 2130385884 sec
    php: liefert 1458911223 sec

    Der Wert 145... erscheint plausibel (46 Jahre mal 365 Tage/Jahr mal 86400 Sekunden/Tag).

    Alles funktioniert wie gewünscht, außer Timestamp (.. und deuten des 1-Wire DS18B20 Messwerts - frag ich weiter unten auch gleich)

    Könnte es ein Problem der Byte-Reihenfolge sein? Erwartet bzw. sendet dein Sensor vielleicht das MSB zuerst (Motorola-Format)? Das wäre jedenfalls das erste, was mir dazu einfiele ...

    So long,
     Martin

    1. Hi Martin,

      nun – den Timestamp hol / generier ich mir auf dem Raspi (Debian Server) mit Echtzeit.
      Und gebe Ihn zur Kontrolle auch auf dem Raspi aus, nicht gesendet / empfangen.

      cout << "stamp: " << (unsigned long)&stamp  << endl;
      

      Der µC hat keine Echtzeit (nur ‘dumme’ Timmer). Der bekommt dann den Timestamp.
      Mit:
      my_zeitstamp = stamp - (millis()/1000); // stamp = gesendet
      bekomme ich dann jedesmal mit
      my_zeitstamp + (millis()/1000) den Unix-Timestamp auch später im µC
      millis() ist die Uptime des µC
      Viele Grüße aus LA
      ralphi

      --
      "Nicht alles was einfach ist, ist genial, aber alles was genial ist, ist einfach" - Albert E.
  2. time() möchte als Argument einen Pointer. Möglicherweise kannst Du den Pointer mit NULL initialisieren aber NULL ist kein Pointer.

    1. Hallo,

      time() möchte als Argument einen Pointer.

      ja, der aber ausdrücklich auch NULL sein darf, in diesem Fall wird der Timestamp nur als Funktionsergebnis geliefert, und nicht zusätzlich irgendwohin geschrieben.

      Möglicherweise kannst Du den Pointer mit NULL initialisieren aber NULL ist kein Pointer.

      Hä?

      So long,
       Martin

  3. weiß jemand, warum ich in C/C++ mit dem Befehl time(NULL) einen falschen Wert bekomme?
    ca. 14:07 Unix-timestamp:
    in C: 2130385884 sec
    php: liefert 1458911223 sec

        uint8_t stamp[4] ;
        * stamp = time(NULL);
    

    Ohne C zu können: time() liefert einen Wert vom Typ time_t, kein uint8_t (was hardwareseitig identisch sein mag), vor allem aber keinen Zeiger auf uint8_t (oder time_t) – entferne erstmal das Sternchen vor stamp.

    Ich möchte gerne den Timestamp zu einem Sensormodul (nRF24L01+) senden, zwengs Messwertzuordnungen.

    Bei aller Liebe: Was meinst du denn bitte mit "zwengs"? Doch nicht etwa "zwecks", vom Zweck?

    1-Wire Tempsensor DS18B20.
    Vielleich weiß das auch Jemand?
    Laut Datenblatt :
    BYTE 0 TEMPERATURE LSB (50h)
    BYTE 1 TEMPERATURE MSB (50h) (85°C)
    Byte 1 Byte 0
    S S S S S B6 B5 B4 - B3 B2 B1 B0 -1 -2 -3 -4
    Ich bekomme MSB 136 LSB 45 (Byte1,0) Zimmertemp: ca. 20,9°C
    1000 1000 - 0010 1101

    Dieser Wert kann nicht vom Sensor stammen. Die S-Bits zeigen das Vorzeichen an und sind entweder alle 1 oder alle 0, nicht, wie bei dir, ein paar 1 und ein paar 0.

    1. Hallo,

          uint8_t stamp[4] ;
          * stamp = time(NULL);
      

      Ohne C zu können: time() liefert einen Wert vom Typ time_t, kein uint8_t (was hardwareseitig identisch sein mag), vor allem aber keinen Zeiger auf uint8_t (oder time_t) – entferne erstmal das Sternchen vor stamp.

      ohne C zu können, hast du vermutlich genau den Knackpunkt gefunden, aber falsch erklärt bzw. die falschen Schlüsse daraus gezogen.

      Also: tatsächlich liefert time() einen Wert vom Typ time_t, und das ist ein uint32 (also im üblichen Sprachgebrauch ein DWORD). In ralphis Beispiel ist stamp deklariert als Array von vier uint8 (also BYTE). Die Zuweisung

      * stamp = time(NULL);
      

      ist de facto identisch mit

      stamp[0] = time(NULL);
      

      weil die Nennung eines Arrays ohne Index-Klammern gleichwertig mit einem Zeiger auf das erste Array-Element ist, und weist daher dem ersten Byte von stamp das Ergebnis von time() zu. Dabei findet implizit ein Typecast von DWORD auf BYTE statt, bei dem Information verlorengeht (vom Compiler hätte ich an der Stelle eigentlich eine Warnung erwartet), während stamp[1] bis stamp[3] uninitialisiert bleiben und zufällige Werte enthalten.

      Aber ralphi will ja den von time() gelieferten DWORD-Wert in den vier Bytes stamp[0] bis stamp[3] haben. Am einfachsten wäre das wohl mit:

      time((time_t*) stamp);
      

      Ich möchte gerne den Timestamp zu einem Sensormodul (nRF24L01+) senden, zwengs Messwertzuordnungen.

      Bei aller Liebe: Was meinst du denn bitte mit "zwengs"? Doch nicht etwa "zwecks", vom Zweck?

      Das fiel mir auch auf, aber ich habe einfach drüber hinweggelesen. :-)

      So long,
       Martin

    2. Hi,

      Bei aller Liebe: Was meinst du denn bitte mit "zwengs"? Doch nicht etwa "zwecks", vom Zweck?

      Sorry - mein Dialekt.
      'zwengs (am)' heißt: 'und wegen des' ;-)
      Viele Grüße aus LA
      ralphi

      --
      "Nicht alles was einfach ist, ist genial, aber alles was genial ist, ist einfach" - Albert E.
  4. Moin,

    #include <stdio.h>
    #include <time.h>
    

    Wenn du in C++ programmierst, dann nimm doch besser deren Header für die Standardbibliothek:

    // …
    #include <cstdio>
    #include <ctime>
    

    Warum eigentlich iostream und cstdio? Das kann unter Umständen Konflikte bei der I/O geben. In deinem Codebeispiel wird cstdio auch gar nicht verwendet.

    using namespace std;
    
    unsigned long startTime, stopTime;
    

    Was für Werte sollen in diesen Variablen gespeichert werden? Die Funktion std::time liefert einen Wert vom Typ std::time_t zurück. Das muss nicht zwingend ein long sein.

    int main(int argc, char** argv){
        // …
    
        uint8_t stamp[4] ;
        * stamp = time(NULL);
    

    Hier unterstellst du, dass std::time einen 32-Bit-Wert zurückgibt. Es können aber auch 64 Bit sein (um das Jahr-2038-Problem zu umgehen).

    Wenn mich nicht alles täuscht, dann steht der time_t in stamp auch unter Umständen in einer anderen Byte-Reihenfolge als vielleicht gewünscht. Das könnte hier zum Problem werden:

        radio.write( &stamp, sizeof(stamp)) ;
    

    Ansonsten könnte der Aufruf von write auch geändert werden:

    std::time_t stamp = std::time(NULL);
    
    radio.write(&stamp, sizeof(stamp));
    

    Viele Grüße Robert

  5. uint8_t stamp[4] ;
    

    stamp ist hier deklariert als ein Pointer auf einen uint8_t-Wert.

    * stamp = time(NULL);
    

    Diese Zeile erzeugt links einen uint8_t-Wert und weist diesem eine größere Zahl zu. Was davon nicht in 8 Bit passt, wird abgeschnitten.

    cout << "stamp: " << (unsigned long)&stamp  << ends;
    

    Diese Zeile gibt nicht den Wert von stamp aus, sondern die Adresse an der der Pointer stamp gespeichert ist.

    Korrekt wäre:

    uint8_t stamp[4];
    *(unsigned long*)stamp = time(NULL);
    cout << "stamp: " << *(unsigned long*)stamp << endl;
    

    Oder ganz einfach:

    time_t stamp = time(NULL);
    cout << "stamp: " << stamp << endl;
    
    1. Hi all,
      Danke erst mal an Alle:
      Also der Befehl radio.write() erwartet void* als Daten.

      Wenn ich jetzt:

      time_t stamp2 = time(NULL);
      
          uint8_t stamp[4];
          stamp[0] = (stamp2 >> 0)& 0xFF;
          stamp[1] = (stamp2 >> 8)& 0xFF;
          stamp[2] = (stamp2 >> 16)& 0xFF;
          stamp[3] = (stamp2 >> 24)& 0xFF;
      
      

      Dann passen die Bytes :-)

      //Allerdings:
          cout << "stamp: " << (unsigned long) *stamp << endl; // nur das erste Byte
          cout << "stamp: " << (unsigned int) stamp << endl; // nur Adresse
      //Jetzt bekomm ich:
          radio.write( &stamp, 4); //sizeof(stamp)) ; // Also Adresse von stamp
          // nix gscheids
      

      Kann aber auch ein Fehler im µC-Prog sein!?

      Bei:

      uint8_t stamp[4];
      *(unsigned long*)stamp = time(NULL);
      cout << "stamp: " << *(unsigned long*)stamp << endl;
      

      warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

      So richtig, wie ich an den Speicherbereich bei Linux ran muss, weiß ich noch nicht?
      Beim µC hab ich:

      uint8_t data[32];
      //..
      // füll es
      //..
      // VCC
          data[x] = 0x01; x++;
          data[x] = analogValue & 0xFF; x++;
          data[x] = (analogValue >> 8) & 0xFF; x++;
      //.. usw
      // SENSDEREI
          radio.powerUp(); 
          radio.write( data, sizeof(data) );
          radio.powerDown();
      

      das klappt ohne Probleme!?

      Viele Grüße aus LA
      ralphi

      --
      "Nicht alles was einfach ist, ist genial, aber alles was genial ist, ist einfach" - Albert E.
      1. Moin,

        Also der Befehl radio.write() erwartet void* als Daten.

        und das bedeutet jetzt was genau - so zusammenhanglos?

        time_t stamp2 = time(NULL);
        
            uint8_t stamp[4];
            stamp[0] = (stamp2 >> 0)& 0xFF;
            stamp[1] = (stamp2 >> 8)& 0xFF;
            stamp[2] = (stamp2 >> 16)& 0xFF;
            stamp[3] = (stamp2 >> 24)& 0xFF;
        

        Okay, das geht natürlich. Ist quasi die Holzhammer-Narkose. Einen eleganteren Weg hatte ich dir schon vorgeschlagen; ein anderer wäre eine union-Deklaration, so dass ein time_t und vier Bytes dieselben Adressen belegen:

        union
         { time_t t;
           uint8  b[4];
         } stamp;
        

        So kannst du denselben Speicherplatz einmal mit stamp.t als time-Wert ansprechen, und einmal mit stamp.b als Byte-Array.

        //Allerdings:
            cout << "stamp: " << (unsigned long) *stamp << endl; // nur das erste Byte
            cout << "stamp: " << (unsigned int) stamp << endl; // nur Adresse
        //Jetzt bekomm ich:
            radio.write( &stamp, 4); //sizeof(stamp)) ; // Also Adresse von stamp
            // nix gscheids
        

        Nochmal: Der Ausdruck (unsigned long) *stamp nimmt die Basisadresse des Arrays als Zeiger auf uint8, dereferenziert diesen Zeiger und liest das erste Array-Element, und castet das dann nach unsigned long. Das ist dasselbe wie (unsigned long) stamp[0].
        Der Ausdruck (unsigned int) stamp nimmt die Adresse des Arrays und castet die nach unsigned int. (Warum nimmst du hier nicht auch long?)
        Und &stamp ist wieder nur die Adresse des Arrays, wobei der Adress-Operator & hier überflüssig ist (aber auch nicht schädlich).

        uint8_t stamp[4];
        *(unsigned long*)stamp = time(NULL);
        cout << "stamp: " << *(unsigned long*)stamp << endl;
        

        warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

        Prima: Der Compiler erkennt, dass du hier verschiedene Zeiger-Typen ineinander überführen willst und warnt, weil das meistens ein Fehler ist. Hier ist es dagegen Absicht.

        So richtig, wie ich an den Speicherbereich bei Linux ran muss, weiß ich noch nicht?

        Es gibt keinen Unterschied zwischen Linux und beispielsweise Windows in diesen Beispielen.

            data[x] = 0x01; x++;
            data[x] = analogValue & 0xFF; x++;
            data[x] = (analogValue >> 8) & 0xFF; x++;
        

        Das geht noch etwas schöner, wenn man die Zuweisung und das Increment zusammenfasst:

            data[x++] = 0x01;
            data[x++] = analogValue & 0xFF;
            data[x++] = (analogValue >> 8) & 0xFF;
        

        ERGÄNZUNG:
        Wieso hantierst du eigentlich so umständlich mit einem time-Wert und dem Byte-Array herum? Wozu brauchst du das Array? Genügt nicht einfach sowas:

        time_t stamp = time(NULL);
        // weiterer Code ...
        // weiterer Code ...
        radio.write(&stamp, sizeof(stamp));
        

        Denn eingangs hattest du noch darauf hingewiesen, dass radio.write() mit void* als Parameter deklariert ist, und der ist ja zu allen anderen Zeigertypen kompatibel.

        Schöne Ostern noch,
         Martin

        1. Hi all,
          ich muss mich erst mal entschuldigen – ein Problem zu schildern, dass es so nicht gibt.
          Meiner Schlamperei ist zu verdanken, dass der ATTiny die gesendeten Werte falsch darstellt. Und der DS18(B)20 falsch gedeutet wird.
          Natürlich funktioniert:

          ERGÄNZUNG:
          Wieso hantierst du eigentlich so umständlich mit einem time-Wert und dem Byte-Array herum? Wozu brauchst du das Array? Genügt nicht einfach sowas:

          time_t stamp = time(NULL);
          // weiterer Code ...
          // weiterer Code ...
          radio.write(&stamp, sizeof(stamp));
          

          Ich hab mehr als 3 Sachen gleichzeitig implementiert und Murx im Speicher gemacht.
          Auch der DS18(B)20 funktioniert, wenn man in der Box nicht DS18B20 und DS18S20 mischt - grr

          ein anderer wäre eine union-Deklaration, so dass ein time_t und vier Bytes dieselben Adressen belegen:

          union
           { time_t t;
             uint8  b[4];
           } stamp;
          

          So kannst du denselben Speicherplatz einmal mit stamp.t als time-Wert ansprechen, und einmal mit stamp.b als Byte-Array.

          Dachte das geht einfacher!?
          Zitat:

          Es ist allerdings genauso gut möglich eine Zeigervariable zu übergeben oder direkt die Speicherposition des Feldes:
          char str[] = {'h','a','l','l','o','\0'};
          char* pStr1 = str;
          char* pStr2 = &str[0];
          pStr1 und pStr2 zeigen auf den selben Speicherbereich

          Sorry - AN ALLE :-0 Viele Grüße aus LA
          ralphi

          --
          "Nicht alles was einfach ist, ist genial, aber alles was genial ist, ist einfach" - Albert E.