Internetzeit & Datum

Ich hole mir mit dem ESP Zeit & Datum Datum aus dem Internet:

#include <ESP8266WiFi.h>

const char* ssid = "<yourWiFiSSID>";
const char* password = "<yourWiFiPswd>";

void setup() {
  Serial.begin(115200); 
  Serial.println(); 
  initWifi();
}

void loop() {
  Serial.println(getTime());
  delay(5000);
}
  
String getTime() {
  WiFiClient client;
  while (!!!client.connect("google.com", 80)) {
    Serial.println("connection failed, retrying...");
  }

  client.print("HEAD / HTTP/1.1\r\n\r\n");
 
  while(!!!client.available()) {
     yield();
  }

  while(client.available()){
    if (client.read() == '\n') {    
      if (client.read() == 'D') {    
        if (client.read() == 'a') {    
          if (client.read() == 't') {    
            if (client.read() == 'e') {    
              if (client.read() == ':') {    
                client.read();
                String theDate = client.readStringUntil('\r');
                client.stop();
                return theDate;
              }
            }
          }
        }
      }
    }
  }
}

void initWifi() {
   Serial.print("Connecting to ");
   Serial.print(ssid);
   if (strcmp (WiFi.SSID(),ssid) != 0) {
      WiFi.begin(ssid, password);
   }

   while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
   }
  Serial.print("\nWiFi connected, IP address: ");
  Serial.println(WiFi.localIP());
}

Klappt gut. Nur wird dort der Monat nicht in Zahlen sondern z.B. Nov angegeben.

Date: Tue, 10 Nov 2015 18:52:14 GMT

Ich brauch es für die Sommer- Winterzeiteinstellung aber in Zahlen.

Gibt es noch bessere Möglichkeiten als eine 12er if Abfrage?

https://de.wikipedia.org/wiki/Network_Time_Protocol

Das hab ich schon mal gelesen aber nicht verstanden.

Hat jemand einen Anfängerfreundlichen Beispiel-Sketch?

Oder wenn du dir wieder was zusammenhacken willst. Gibt dir den Monat von 1-12 zurück oder 0 wenn er nicht gefunden wurde.

const char months[] PROGMEM = "JanFebMarAprMayJunJulAugSepOctNovDec";

void setup()
{
  Serial.begin(9600);

  String theDate = "Tue, 10 blah 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Mon, 10 Jan 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Fri, 10 Feb 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Sat, 10 Mar 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Sun, 10 Apr 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 May 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Jun 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Jul 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Aug 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Sep 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Oct 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Nov 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));

  theDate = "Tue, 10 Dec 2015 18:52:14 GMT";
  Serial.println(getMonth(theDate));
}

void loop()
{
}

byte getMonth(const String& date)
{
  const char* date_c = date.c_str();      //Zeiger auf internes Array

  for (int i = 0; i < 12; i++)            //über Monate iterieren
  {
    const char* date_ptr = date_c;          //nochmal Zeiger auf Datum, da man jedesmal den Anfang braucht
    const char* compare = &months[i * 3];   //Zeiger auf Monat im Vergleichs-String
    byte match = 0;     //Anzahl der Übereinstimmungen

    while (*date_ptr)   //wenn String nicht zu Ende
    {
      if (*date_ptr == pgm_read_byte(compare + match))    //Zeichen im Datum mit nächstem Zeichen im Vergleichs-String vergleichen
      {
        match++;

        if (match == 3)   //wenn 3 Zeichen übereinstimmen ist der Monat enthalten
          return i + 1;
      }
      else
        match = 0;

      date_ptr++;   //nächstes Zeichen
    }
  }

  return 0;
}

Kann man natürlich auch mit Standard C String Funktionen lösen

EDIT:
Wobei es etwas logischer (und vielleicht kürzer) wäre einmal durch dein eingelesenen String zu gehen und dann jedes Zeichen darin immer wieder mit den Monaten zu vergleichen. Aber es geht so oder so.

Mist - du hast mich durchschaut: Zusammenhacken ist meine Spezialität! :grin:

Hast wohl auch für jeden Fall den richtigen Sketch in der Hosentasche!?

Funktioniert prima.

Na dann kann ich ja weiterhacken... :D

stoni99: Hast wohl auch für jeden Fall den richtigen Sketch in der Hosentasche!?

Nee, er kocht immer frisch :)

Eigentlich brauchst du aber noch den Tag und das Jahr oder? Und die Zeit auch noch. Das kann man dann besser alles in einem Durchgang parsen.

Krümelkack: Es gibt keine "Winterzeit", nur die Sommerzeit und ide ganz normale mitteleuropäische Zeit (MEZ).

Tag & Jahr habe ich mir dann auch noch wie ganz oben beschrieben geholt.

Da ich mir die Daten per:

int Tag = (getTime().substring(5, 7)).toInt();

hole, wird es wohl Probleme bei einstelligen Werten geben. Wird sich am Dienstag zeigen. Bin noch am knobeln wie ich das löse...

Evtl. kann ich da ja auch noch was zusammenhacken. :grin:

sieh dir mal strtok an. das macht genau das was du willst

Du meinst hiermit den String nach den Leerzeichen als Trennzeichen zerstückeln?

so in etwa. du kannst entweder mehrere delimiter (derdelimiter muss nicht in einer Variablen sein)gleichzeitig angeben, oder eben gezielt, z.B. nur Leerstelle, dann bleibt z.b. die Zeit komplett erhalten. Eben je nach Anwendungsfall und Datenstrukturen.

Ja, das strtok funktioniert!

Hab es mal für den Arduino-Sketch angepasst:

void setup() {

Serial.begin(9600);

char string[] = "Kurt Kanns 555678 DE";
char delimiter[] = " ";  //delimiter (Begrenzungszeichen, Separator) 
char *ptr;

// initialisieren und ersten Abschnitt erstellen
ptr = strtok(string, delimiter);

while(ptr != NULL) {
  Serial.print("Abschnitt gefunden: ");
  Serial.println(ptr);
  // naechsten Abschnitt erstellen
  ptr = strtok(NULL, delimiter);
}

}

void loop() {
  // put your main code here, to run repeatedly:

}

Mit "delimiter" gebe ich an welches als Trennzeichen verwendet werden soll. Also in meinem Fall " " Leerzeichen. Geht aber auch z.B. Komma und/oder Semikolon also ",;".

Muss ich jetzt noch in meinen Sketch einarbeiten - ähh, einhacken... ;D

Ist etwas schwierig, da ich ja einem String einen bestimmten Abschnitt zuweisen muss.

Hab es mal mit Case probiert - bin mit mir da noch nicht so recht zufrieden :confused: :

void setup() {

Serial.begin(9600);

char string[] = "Date: Tue, 10 Nov 2015 18:52:14 GMT";
char delimiter[] = " ";  //delimiter (Begrenzungszeichen, Separator) 
char *ptr;

String Tag;
String Monat;
String Jahr;


// initialisieren und ersten Abschnitt erstellen
ptr = strtok(string, delimiter);

int a=1;
while(ptr != NULL) {
  switch (a) {
    case 1:
      Serial.print("Abschnitt1 gefunden: ");
      Serial.println("wird ignoriert");
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
    break;
    case 2:
      Serial.print("Abschnitt2 gefunden: ");
      Serial.println("wird ignoriert");
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 3:
      Serial.print("Abschnitt3 gefunden Tag: ");
      Serial.println(ptr);
      Tag = (ptr);
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 4:
      Serial.print("Abschnitt3 gefunden Monat: ");
      Serial.println(ptr);
      Monat = (ptr);
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 5:
      Serial.print("Abschnitt3 gefunden Monat: ");
      Serial.println(ptr);
      Jahr = (ptr);
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
  }
    a++;
}
Serial.println(Tag);
Serial.println(Monat);
Serial.println(Jahr);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Ist etwas schwierig

Rein rhetorisch (Und falls das jemand nachmachen will): Warum willst du dir partout nicht die (bzw. eine) richtige Internet Zeit holen.

ntp ist nicht wirklich kompliziert.

Auch wenn du ein Android Handy hast, musst du nicht alles bei Google kaufen. ;)

Die String Klasse macht das alles nur unnötig kompliziert. Schon rein vom programmieren her. Es fehlen da einfach elementare Funktionen. Und man kommt zwar mit c_str() inzwischen an das interne Array, aber das ist nur zum Lesen. strtok() verändert allerdings den String.
Dazu kommt dann noch die Speicherverschwendung.

Mit strtok() und Standard C String Funktionen ist sowas trivial:

struct time_data
{
  byte day;
  byte month;
  unsigned int year;
  byte hour;
  byte minute;
  byte second;
};

void parseTime(const String& str, time_data& time);
void printTime(const time_data& time);

void setup()
{
  Serial.begin(9600);

  time_data time;

  String theDate = "Tue, 2 Jan 2014 8:02:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Wed, 3 Feb 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Thu, 4 Mar 2015 1:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Fri, 5 Apr 2015 18:05:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Sat, 6 May 2015 07:52:4 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Sun, 7 Jun 2015 18:52:4 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 8 Jul 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 9 Aug 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 10 Sep 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 11 Oct 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 12 Nov 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);

  theDate = "Tue, 13 Dec 2015 18:52:14 GMT";
  parseTime(theDate, time);
  printTime(time);
}

void loop()
{
}


void parseTime(const String& str, time_data& time)
{
  const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  char str_c[35];
  str.toCharArray(str_c, sizeof(str_c));

  strtok(str_c, " ");                 //Anfang des Strings
  time.day = atoi(strtok(NULL, " ")); //Tag
  char* token = strtok(NULL, " ");    //Monat
  char* ptr = strstr(months, token);  //Zeiger auf den Monat in months String
  if (*ptr)
  {
    unsigned int distance = ptr - months;   //Abstand zwischen Teil-String und Anfang
    time.month = (distance / 3) + 1;
  }

  time.year = atoi(strtok(NULL, " "));    //Jahr
  time.hour = atoi(strtok(NULL, ":"));    //Uhrzeit
  time.minute = atoi(strtok(NULL, ":"));
  time.second = atoi(strtok(NULL, ":"));
}

void printTime(const time_data& time)
{
  time.hour < 10 ? Serial.print('0') : Serial.print("");
  Serial.print(time.hour);
  Serial.print(':');
  time.minute < 10 ? Serial.print('0') : Serial.print("");
  Serial.print(time.minute);
  Serial.print(':');
  time.second < 10 ? Serial.print('0') : Serial.print("");
  Serial.print(time.second);

  Serial.print(F(" -- "));
  Serial.print(time.day); Serial.print('.'); Serial.print(time.month); Serial.print('.'); Serial.println(time.year);
}

Enthält Datum und Uhrzeit. Und auch das Konvertieren des Monats geht mit strtr() ganz einfach über Zeiger-Arithmetik. Der Monats-String steht allerdings nicht im Flash

Aber NTP wäre hier der richtige Weg. Für das normale Ethernet Shield gibt es da auch fertigen Code von jurs. Das sollte sich doch anpassen lassen.

Na wenn ich dann das hier lese bleibe ich spätestens bei "if ((pktLen = udp.parsePacket()) == 48) break;" hängen und drücke verwirrt auf "schliessen".

Wäre das verständlicher ?

  for (byte i=0; i<maxPoll; i++) {
    pktLen = udp.parsePacket();
    if (pktlen == 48)  break;  // ok komplett empfangen
    delay(pollIntv);
  }

oder was ist dein Problem ?

Zuweisungen geben den zugewiesenen Wert zurück. Deshalb kann kann man nach einer Zuweisung direkt einen Vergleich machen.

Es gibt da aber auch anderen Code der vielleicht leichter verständlich ist: https://forum.arduino.cc/index.php?topic=206695.0 Ist auch generell etwas besser. Braucht die Time Library, aber die Umrechnung der Unix-Zeit könnte man auch per Hand machen.

Den link hatte ich mir auch schon mal reingezogen.

Hab da noch so meine Probleme mit dem NTP-code. Muss ich wohl mal ein paar Nächte drüber schlafen (oder grübeln).

Warum geht denn eigentlich in meinem unnötig komplizierten Anfänger-Test-Sketch das hier nicht:

Tag = (ptr.toInt());

ptr lässt sich nicht in Tag (integer) umwandeln.

void setup() {

Serial.begin(9600);

char string[] = "Date: Tue, 10 Nov 2015 18:52:14 GMT";
char delimiter[] = " :";  //delimiter (Begrenzungszeichen, Separator) 
char *ptr;

int Tag;
String Monat;
String Jahr;
String Stunde;
String Minute;
String Sekunde;


// initialisieren und ersten Abschnitt erstellen
ptr = strtok(string, delimiter);

int a=1;
while(ptr != NULL) {
  switch (a) {
    case 1:                           //wird ignoriert - Zeichen nicht benötigt
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
    break;
    case 2:                           //wird ignoriert - Zeichen nicht benötigt
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 3:
      Tag = (ptr.toInt());                      //akt. Tag
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 4:
      Monat = (ptr);                    //akt. Monat
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 5:
      Jahr = (ptr);                     //akt. Jahr
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
     case 6:
      Stunde = (ptr);                   //akt. Stunde
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
    break;
    case 7:
      Minute = (ptr);                   //akt. Minute
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 8:
      Sekunde = (ptr);                  //akt. Sekunde
      // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
    case 9:                             //wird ignoriert - Zeichen nicht benötigt
       // naechsten Abschnitt erstellen
      ptr = strtok(NULL, delimiter);
     break;
  }
    a++;
}
Serial.println(Tag);
Serial.print("Tag integer:");
Serial.println(Tag.toInt()+1);
Serial.println(Monat);
Serial.println(Jahr);
Serial.println(Stunde);
Serial.println(Minute);
Serial.println(Sekunde);
}

void loop() {
  // put your main code here, to run repeatedly:

}