Meine Garmin Watch Faces

Für einige Smartwatches der Firma Garmin ist es möglich ein eigenes ‘Watch Face’ zu erstellen. Dabei handelt es sich um ein – im klassischen Sinne – Ziffernblatt. Das heißt, es ist möglich ein völlig eigenes Ziffernblatt zu programmieren und anschließend auf die Smartwatch zu laden.

Connect-IQ

Sieht man im Internet auf der Garmin Seite Connect-IQ nach, finden sich bereits Tausende von Watch Faces, die man einfach auf seine Garmin Smartwatch laden kann. Normalerweise macht man das aber über ‘Garmin Connect’, das ist die zu jeder Garmin Smart Watch gehörende App. Dort gibt es einen Menüpunkt, mit dem man ein Watch Face komfortabel auswählen und auf der Smartwatch installieren kann. Auf der Smartwatch selbst kann man mehrere, schon installierte Watch Faces über den Menüpunkt ‘Displaydesign’ auswählen und aktivieren.

Eine Zeit lang macht es richtig Spaß die verschiedenen Watch Faces auszuprobieren. Es gibt so viele Verschiedene! Teilweise sind die nicht nur unterschiedlich gestaltet, sondern haben völlig neue und interessante Funktionen. Es gibt zum Beispiel eine, die eine miniaturisierte Weltkarte darstellt und darauf den Sonnenverlauf zeigt. Oder eine, die eine grafische Balkendarstellung der Herzfrequenzzonen zeigt. Und selbstverständlich kann man zwischen unzähligen Analog- und Digital-Watchfaces auswählen.

Wer gerne etwas Persönlicheres haben möchte, kann sich mit Hilfe der ‘Garmin Face It’-App aus einem mit dem Smartphone aufgenommenen Foto ein individuelles Watch Face machen!

Eigene Watch Faces

Wem das alles nicht genügt und etwas programmieren kann, der kann einfach sein eigenes Watchface programmieren. Über die Weihnachtszeit bekam ich Mega-Lust mir mein ganz eigenes Watchface zu erstellen. Selbstverständlich dachte ich, dass meines das Beste sein wird und in der Connect-IQ Liste mit den beliebtesten Watch Faces nach kurzer Zeit ganz oben erscheinen wird!

Mit 240×240 Pixeln haben die Garmin Smartwatches nicht ganz so viele Pixel wie die schärfsten Konkurrenten wie zum Beispiel die Samsung Galaxy Watch mit 360×360 Pixeln oder der Apple Watch 4 mit 312 x 390 (40 mm Display) bis zu 384 x 480 (44 mm Display) Pixeln. Aber aus einem normalen Abstand von ca. 30 cm ist das kaum zu erkennen. Und Garmins transreflektives Chroma Display bleibt halt dauerhaft sichtbar.

Trotz der geringeren Auflösung ist es möglich, nahezu jede denkbare Idee als grafische Darstellung auf dem Display zu realisieren. Mein Grundidee war es, die für mich am Tag am häufigsten aufgerufenen Werte jederzeit, und in einer angenehmen Größe sichtbar, anzuzeigen.

Daraus ergab sich eine Viertelung des Anzeigekreises mit einem fetten Streifen in der Mitte, der zur Anzeige der Zeit dient.

Ein guter Startpunkt für Entwickler ist der Programmer’s Guide von Garmins Seiten für Entwickler. Entwickelt wird am besten mit Eclipse. Von Garmin gibt es dazu ein passendes SDK und programmiert wird in Monkey C. 🙂

Tipps

Theoretisch kann man alles in Monkey-C programmieren. Aber für bestimmte Darstellungen ist das ziemlich aufwendig. Wer sich mit einem Vektor Zeichenprogramm wie Corel Draw oder Inkscape auskennt, der kann statische Anteile des Watchfaces damit zeichnen, als PNG exportieren und anschließend einfach auf dem Display anzeigen!

Mit Inkscape habe ich auch die Icons gezeichnet und exportiert.

Mit BMFont kann man aus Truetype-Zeichensätzen passende Bitmap-Zeichensätze für die Watch Face Programmierung erzeugen. Hier ist ein nettes Tutorial dazu.

Bemerkenswertes

Man kann nicht nur Watch Faces programmieren. Sondern richtige kleine Anwendungen. Die nennen sich dann Widgets oder Device Apps. Aber soweit wollte ich nicht einsteigen. Ich habe genug anderes zu tun. Es gibt aber eine Sache, die meiner Meinung nach noch unbedingt erwähnenswert ist. Dies ist die Aktualisierungsrate des Displays, sie wirkt sich nämlich wirklich entscheidend auf den Stromverbrauch aus.

Mehr oder weniger zufällig hatte ich beim Ausprobieren von Watch Faces immer welche gewählt, die aufwendige Funktionen besaßen oder zumindest eine sekündliche Aktualisierung hatten. Das führte dazu, dass ich die Smartwatch spätestens alle 4 bis 5 Tage aufladen musste. Mein eigenes Watch Face (und natürlich viele andere auch) unterstützt keine sekündliche Aktualisierung und läuft darum wesentlich länger. Ich habe es nicht genau gemessen, aber manchmal scheinen über den Tag nur knapp 10 % Energie verbraucht werden!

Meine Watch Faces!

Quadrant

Ein Anwender meines Quadrant-Watch Faces fand es gut, wollte aber eine dunklere Darstellung. Darum entwickelte ich noch eine ähnliche Designvariante mit deutlich dunkleren Farben und nannte sie Quadrant Dark.

Quadrant Dark

Leider haben es meine Watch faces bis jetzt nur ins Mittelfeld der beliebtesten Watch Faces geschafft. Aber sie werden verwendet und das macht mich ein ganz kleines bisschen stolz! 🙂

Noch mehr coole Einsteiger-Motorräder

In meinem Artikel ‘Das beste Motorrad für Anfänger und Reisende‘ habe ich Euch die Honda Transalp vorgestellt. Heute bin ich zufällig an einer anderen Honda vorbei gefahren, die vermutlich ähnlich gut geeignet sein dürfte und mich an noch ein anderes Motorrad erinnert hat. Beide Motorräder haben den Motor von der Honda Transalp und zusätzlich noch eine Kardanwelle als weiteres technisches Schmankerl.

Es sind die Honda NTC 650 Revere und die Honda Deauville.

Beide sind mit brauchbaren Kilometerleistungen für um die 2000 Euro zu bekommen. Allerdings müssen A2-Besitzer sie vermutlich in den meisten Fällen drosseln lassen.

Hier gibts Infos zur Honda NTC 650 Revere und hier zur Honda Deauville.

 

ESP32 als Siebenschläfer

Ein Bericht über meine Versuche mit einem ESP32 und der Arduino IDE einen autarken Email-Versand von Sensordaten aus dem Garten zu realisieren. Das dafür am geeignetsten erscheinende Board ist eines von Banggood, welches zusätzlich zum ESP32 auch noch ein OLED-Display und eine Akkuhalterung für 18650er Akkus integriert hat.

Wenn man sich, ohne wirklich Ahnung zu haben, so wie ich, in die Arduino Welt eingearbeitet hat und erste ‘Real life’-Anwendungen entwickeln möchte, stößt man auf vorher nicht wahrgenommene Probleme. So ging es mir mit der schon tausendfach von anderen realisierten Idee einen Outdoor-Wettersensor zu entwickeln. Eigentlich ist die Idee super simpel. Ein ESP32-Board fragt regelmäßig einen DHT22-Sensor ab und sendet die Werte als Email an meinen Email-Account. Nix Besonderes.

Über Bugs und Troubleshooting

Aber schon mit dem DHT22 fangen die Probleme an. Einmal hatte ich einen, der keine vernünftigen Luftfeuchtigkeitswerte zur Verfügung stellte, ein anderes Mal einen, der nach einigen Tagen gar nicht mehr funktionierte und zur Stimulierung einmal kurz von der Stromversorgung getrennt werden wollte.

Ich habe das jetzt so beiläufig niedergeschrieben, aber in der Praxis bedeuteten diese Dinge jede Menge Aufwand. Anfangs weiß ich ja nicht, warum meine Anwendung nicht richtig funktioniert. Oft denke ich, dass es bestimmt mein Fehler sein muss und ich versuche stundenlang ihn zu finden. Bei dem Problem mit den falschen Werten dachte ich lange an einen Fehler von mir, hatte aber keine Zeit mich darum zu kümmern. Als ich mich dann nach einigen Wochen wieder damit beschäftigte, wusste ich nicht mehr genau, was und wie ich alles installiert hatte. Es handelte sich dabei allerdings um eine Raspberry Pi Anwendung mit Python und C-Bibliotheken und einer HTML/JavaScript-Anzeige. Ich fand den Fehler nicht und lies es erst einmal darauf beruhen. Das System lieferte daraufhin jahrelang falsche Werte für die Luftfeuchtigkeit (Zum Glück war es ja nur ein Experimentier-Projekt) in meine Datenbank!

Im Zusammenhang mit meinem ESP32 Experiment kaufte ich kürzlich einen zweiten Sensor, dachte, den könnte ich ja zum Testen einfach einmal an den Raspberry stecken und sofort wurden richtige Werte geliefert! Das ist wirklich frustrierend.

Doch genau mit diesem Sensor traten nach einigen Tagen wieder Probleme auf, die dazu führten, dass wieder keine Werte in der Datenbank ankamen. Auch diesmal verbrachte ich mehrere Stunden mit der Fehlersuche, nur um irgendwo im Internet zu lesen, dass der DHT22 tatsächlich manchmal einfach ausfällt. Man braucht ihm dann nur kurz den Strom weg zu nehmen und schon läuft er wieder!

Ich habe mich jetzt nicht weiter darum gekümmert, aber für die, die das Problem irgendwie automatisiert in den Griff bekommen wollen, ist eine mögliche Lösung, den positiven Anschluss des Sensors nicht einfach mit Plus zu verbinden, sondern mit einem auf High gesetzten GPIO-Ausgang, der im Fehlerfall kurz auf Low gesetzt wird.

Mobile Stromversorgung

Denkt man an eine mobile Stromversorgung, denkt man heutzutage an eine Powerbank. Und in der Tat funktioniert das mit den meisten ESP32 Boards und der dabei verbauten USB-Buchse scheinbar wunderbar. Diese Ansicht ändert sich aber schnell, wenn man das Board richtig lange betreiben will:

Der größte Nachteil einer Powerbank ist die damit einhergehende Elektronik, die aus dem oder den in der Powerbank eingesetzten Akkus die 5 Volt für die USB-Buchsen erzeugt. Egal wie effizient die Elektronik ihren Job erledigt, es kostet immer etwas Energie. Legt man den ESP32 in den Tiefschlaf, so ist es sogar ein Vielfaches der Energie, die der ESP32 benötigt! Aber es wird noch schlimmer: Der ESP32 verträgt gar keine 5 Volt! Das heißt eine weitere Schaltung – diesmal auf dem Board selbst – muss die 5 Volt von der Powerbank wieder auf 3.3 Volt herunter konvertieren und die benötigt auch wieder Energie. Der Einsatz einer Powerbank ist also doppelt unsinnig und sollte bei mobilen Anwendungen möglichst vermieden werden.

Der zweite Nachteil mancher Powerbanks ist die Eigenschaft sich einfach abzuschalten, wenn der Strom eine bestimmte Menge unterschreitet. Letzteres kann man allerdings softwaregestützt vermeiden, wenn man innerhalb des Zeitraums, indem die Powerbank auf mehr Strom wartet, einfach mal kurz am Strom zieht. 🙂 Dazu kann man an einen GPIO-Eingang einen passenden Widerstand anbringen oder man aktiviert einfach einmal kurz das Wifi!

Beide Nachteile sind denkbar schlechte Voraussetzungen für einen geruhsamen Schlaf des ESP32.

ESP32 mit OLED und 18650 Akku

Vor kurzem entdeckte ich, dass es die ESP32 mit OLED Display auch noch zusätzlich mit einem Fach für einen 18650er Akku gibt. Sogar eine Ladeelektronik ist vorhanden. Und das Board kann gleichzeitig betrieben und geladen werden. Bei der Recherche zu diesem Artikel habe ich gelesen, dass die Spannung eines 18650er Akku, die je nach Ladezustand 3.7 bis 4.2 Volt beträgt, zu hoch für den ESP32 (2.3 bis 3.6 Volt) sein soll und sie darum noch einmal herunter konvertiert werden muss. Bei meinen Versuchen mit dem Board konnte ich aber einen Strom im ‘Deep Sleep’ dieses Boards von unter 1 Milliampere messen und ich kann mir nicht vorstellen, dass ein DC-DC Wandler so wenig Strom verbraucht. Aber wer weiß.

Ich habe nachgemessen und bei einer Akkuspannung von 4 Volt lagen bei VCC nur etwa 3.3 Volt an. Also scheint es einen Gleichspannungswandler zu geben. Das hat mich dazu gebracht noch einmal genauer nachzuforschen. Auf dem Board ist ein ‘Low Dropout’-Regler mit der Bezeichnung AMS1117 verbaut. Vermutlich wird der sowohl für die 5 Volt vom USB-Anschluss als auch für den 18650er verwendet.

Mit meinem Solarpanel wird das Board geladen, jedenfalls leuchtet die entsprechende LED auf dem Board auf, wenn genügend Sonnenlicht auf das Panel fällt. Langzeittests stehen allerdings noch aus. Aber schon jetzt scheint mir das Board zusammen mit einem 18650er eine sehr vielversprechende Lösung für eine mobile Computeranwendung zu sein. Denn bei ersten Versuchen scheint es länger durchzuhalten als meine Soshine E3S-Powerbank, sogar wenn ich zwei 18650er Akkus eingelegt habe!

Zeitprobleme

Wenn wir heutzutage einen Computer einschalten, so ist es ganz selbstverständlich, dass der weiß wie viel Uhr es gerade ist. Er kann dass, weil er irgendwo in seinem Innern einen kleinen Akku besitzt, der dafür sorgt, dass ein kleiner Zähler immer weiter zählen kann, auch wenn wir denken, das er eigentlich stromlos ist. Der ESP32 hat auch so einen Zähler, RTC (Real Time Clock) genannt, allerdings hat er keinen eingebauten Akku. Für die richtige Stromversorgung müssen wir Entwickler sorgen.

Wird unsere mobile Anwendung zum ersten Mal eingeschaltet, so fängt der Zähler bei 0 an zu laufen. Der ESP32 hat in diesem Moment keine Ahnung, ob er in der Vergangenheit, in der Zukunft oder in einem Paralleluniversum aufwacht. Soll er seine Daten mit einem korrekten Datum versenden, müssen wir es ihm mitteilen. Das ginge mit:

  • Mit einem angelötetem Taster und einem mehr oder weniger trickreichen Protokoll, wie ich es zum Beispiel von einer Honda CB1000R her kenne.

  • Per DCF77-Antenne, da holt er sich die unglaublich genaue Zeit einer Atomuhr aus den Funkwellen um uns herum.

  • Aus einem extern angeschlossenen RTC-Modul. Scheint ein bisschen Overkill, da der ESP32 ja schon ein RTC-Modul eingebaut hat.

  • Oder, und so mache ich es, von einem Zeitserver über WiFi aus dem Internet.

Um die Email zu versenden wird ja sowieso gelegentlich eine Internet-Verbindung benötigt. Da kann man auch gleich die Zeit holen. Alle anderen Lösungen benötigen deutlich mehr Aufwand. Leider ist der Zähler nicht sonderlich genau, darum muss er von Zeit zu Zeit synchronisiert werden. Das ist auch der Grund, warum manche Leute eine wesentlich genauere externe RTC anschließen. Es ist aber auch möglich zu ermitteln, um wie viele Sekunden die Uhr pro Tag falsch geht und sie einmal am Tag um diesen Wert zu korrigieren. Damit sollte man einige Monate auskommen.

Deep Sleep

Ein ESP32 benötigt ohne WiFi und ohne Zusatzbeschaltung so ungefähr 20 – 40 mA. Bei direktem Einsatz des ESP32 mit einem 18650er Akku und bei 3.3 Volt sollte der Akku ungefähr 115 Stunden (3500 mAh / 30 mAh) halten, also knapp 5 Tage. Das ist gar nicht schlecht, aber weit von autark entfernt. Nun könnte man ein Solarpanel anschließen und bei etwas Sonne an jedem Tag, sollte die Autarkie gewährleistet sein. Aber schon eine längere Regenzeit zerstört unser Vorstellungen. Darum gilt es das Board so stromsparend wie nur irgend möglich zu machen. Zum Glück bietet der ESP32 dazu eine tolle Möglichkeit an: Er kann in eine Art Tiefschlaf versetzt werden, in denen er nur wenige Mikroampere benötigt! Im Detail kann man darüber hier nachlesen. Denn der ESP32 hat nicht nur einen Dual-Core Prozessor, sondern auch noch einen extrem stromsparenden Co-Prozessor! Das ermöglicht ihm den leistungsfähigen Dual-Core Mikroprozessor komplett abzuschalten und trotzdem weiter zu ‘existieren’. Dabei wird aber auch das RAM abgeschaltet, das heißt keine Variablen und keine Daten mehr. Jedes Mal wenn der ESP32 ‘aufwacht’, ist es für ihn wie beim Booten eines Computers. Seine Speicher waren stromlos und enthalten keinerlei Informationen mehr. Doch die Entwickler des ESP32 haben mit gedacht und dem Co-Prozessor einen kleinen Speicher mit 8 KByte mitgegeben, der auch im Deep Sleep mit minimal Strom versorgt wird. Wenn man ihn nutzen möchte, schreibt man einfach ‘RTC_DATA_ATTR’ vor die Deklaration einer Variablen.

Programmierung

Normalerweise läuft bei einem Arduino Programm die Setup-Funktion genau einmal und anschließend nur noch die Loop-Funktion. Bei einem ‘Deep Sleep’ Einsatz ist das anders. Zwar könnte man auch die Loop-Funktion verwenden, da aber nach einem ‘Deep Sleep’ auf jeden Fall wieder die Setup-Funktion aufgerufen wird, bietet es sich an, den ganzen Code dort unter zu bringen und die Loop-Funktion leer zu lassen.

Man könnte das ganze Programm jetzt in folgende Einzelschritte auflösen:

  • Programm wacht auf.

  • Verbindet sich mit dem Internet.

  • Holt sich die Zeit aus dem Internet.

  • Ermittelt die Sensor-Werte.

  • Sendet eine Email mit den Werten.

  • Beendet die Internetverbindung.

  • Geht in den ‘Deep Sleep’.

Ich möchte aber das Programm auch als Rumpf für andere Aufgaben verwenden und darum soll es deutlich häufiger wach werden, als es Emails versenden muss. Die Zeit soll nur geholt werden, wenn sie auch erforderlich ist. Also nur am Anfang und später dann nur noch, wenn sie für meinen Geschmack zu weit von der tatsächlichen Zeit abgewichen ist. Darum ist es wichtig zu wissen, ob der ESP32 das erste Mal eingeschaltet wurde, oder ob er aus einem ‘Deep Sleep’ zurück kommt. Dafür habe ich mir eine Variable ‘timeIsHere’ deklariert:

RTC_DATA_ATTR boolean timeIsHere = false;

Verschlüsseln

Im Code gibt es zwei Stellen, an denen man den User-Namen und das Passwort als Base64 verschlüsseln muss. Das geht ganz einfach mit dieser Seite. Ich kann Euch aber nicht garantieren, das die eingegebenen Daten nicht noch anderweitig verwendet werden. Falls ihr es lieber selbst kodieren möchtet, verweise ich Euch hiermit an die nötigen Informationen in der Wikipedia.

Solarpanel

Das von mir verwendete Solarpanel lag schon länger bei mir herum und hat mit etwa 12 Volt eine viel zu hohe Spannung für das Board. Darum habe ich mir noch bei Banggood diesen Konverter gekauft. Er stabilisiert die 12 Volt mit angeblich 95%iger Effizienz auf 5 Volt herunter. Wenn zu wenig Licht vorhanden ist, schaltet er ab und ermöglicht so bei wiederkehrender Sonne einen Restart des Ladevorgangs.

Nachgemessen

Während eines Zugriffes auf das Internet über das WiFi verbraucht meine Anwendung für wenige Sekunden 140 mA. Ist dass WiFi aus, sind es nur 40 mA. Und wenn der ESP32 im Tiefschlaf ist, sind es weniger als 1 mA! Mir ist bekannt, dass der reine ESP32 ohne externe Beschaltung mit einigen wenigen µA auskommt. Aber so, mit dem DHT22 und dem Regler für die Spannung finde ich 1 mA schon ziemlich gut.

Code

#include <WiFi.h>
#include <TimeLib.h>
#include <SimpleDHT.h>
#include <SSD1306.h>

#include "soc/rtc_cntl_reg.h"

#define TTY_SPEED 115200        /* Geschwindigkeit der seriellen Schnittstelle */
#define uS_TO_S_FACTOR 1000000  /* Multiplikator für die Umrechnung von Sekunden zu Mikrosekunden */
#define TIME_TO_SLEEP  60       /* Wie viele Sekunden soll der ESP32 schlafen? */

RTC_DATA_ATTR long bootCount = 0;
RTC_DATA_ATTR boolean timeIsHere = false;
RTC_DATA_ATTR boolean timeIsSynchronized = false;

int pinLED = 16;
int pinDHT22 = 17;
const char* SSID = "SSID";
const char* PASS = "PASSPHRASE";
char server[] = "smtp.1und1.de";

esp_sleep_wakeup_cause_t wakeup_reason;

SSD1306 display(0x3c, 5, 4);
SimpleDHT22 dht22(pinDHT22);
WiFiClient client;

String dayTime;
float temperature = 0;
float humidity = 0;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

  Serial.begin(TTY_SPEED);
  Serial.print(bootCount);
  Serial.println(" Mal");

  display.init();
  display.setFont(ArialMT_Plain_10);
  display.flipScreenVertically();
  display.setContrast(255);

  print_wakeup_reason();

  // Ermittlung der aktuellen Zeit in Sekunden.
  struct timeval tv;
  gettimeofday(&tv, NULL);
  long curTime = tv.tv_sec;
  Serial.print(curTime);
  Serial.println(" Sekunden");

  // Ist ein Tag vergangen, muss die Zeit neu vom Zeitserver abgefragt werden.
  if (curTime % 86400 <= TIME_TO_SLEEP) {
    timeIsSynchronized = false;
  }

  // Wenn seit dem letzten Einschalten noch nie die Zeit abgefragt worden ist oder
  // wenn sie neu synchronisiert werden soll, dann versuchen die Zeit neu abzufragen
  // und neu in der ESP32 RTC setzen.
  if (timeIsHere == false || timeIsSynchronized == false) {
    tv = setRealTime();
    tv.tv_sec = tv.tv_sec + 3600;
    tv.tv_usec = 0;
    settimeofday(&tv, NULL);
  }

  // Zeit ausgeben
  char buffer[30];
  strftime(buffer, sizeof(buffer), "%d.%m.%Y  %T", localtime(&tv.tv_sec));
  dayTime = buffer;
  Serial.println(dayTime);
  display.clear();
  display.drawString(0, 0, dayTime);

  // Wenn eine gültige Zeit vorhanden ist...
  if (timeIsHere == true) {
    // ... Sensordaten ermitteln und auf dem Display ausgeben.
    int err = SimpleDHTErrSuccess;
    if ((err = dht22.read2(&temperature, &humidity, NULL)) == SimpleDHTErrSuccess) {
      display.drawString(0, 10, (String) temperature + " C");
      display.drawString(0, 20, (String) humidity + " %");
      display.display();
    } else {
      Serial.print("Read DHT22 failed, err=");
      Serial.println(err);
    }
    // ...nachsehen ob eine Viertelstunde vergangen
    // ist und falls ja, die Daten als Email versenden.
    if (curTime % 900 <= TIME_TO_SLEEP) {
      if (connectWiFi()) {
        byte ret = sendEmail();
      }
      WiFi.disconnect();
    }
  }

  bootCount = bootCount + 1;
  delay(5000);
  
  // Display löschen. Spart bei einem OLED-Display Energie!
  display.clear();
  display.display();

  // Den ESP32 in den 'Deep Sleep' bringen.
  Serial.println("Schlafen gehen...");
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  esp_deep_sleep_start();
  delay (100);
}

void loop() {
}

struct timeval setRealTime () {
  display.clear();
  display.drawString(0, 0, "Try to get act time ...");
  display.display();

  if (connectWiFi()) {
    configTime(0, 0, "ptbtime1.ptb.de", "ptbtime2.ptb.de", "ptbtime3.ptb.de");
    struct tm timeinfo;
    if (getLocalTime(&timeinfo)) {
      timeIsHere = true;
      timeIsSynchronized = true;
      display.drawString(0, 10, "Success!");
      display.display();
    } else {
      display.drawString(0, 10, "I try next boot again...");
      display.display();
    }
    delay (2000);
  }
  WiFi.disconnect();

  struct timeval tv;
  gettimeofday(&tv, NULL);
  return tv;
}

bool connectWiFi () {
  bool connectState = false;
  int tryCounter = 10;
  delay(10);
  Serial.println("");
  Serial.println("");
  Serial.print("Connecting to ");
  Serial.println(SSID);
  WiFi.begin(SSID, PASS);
  while ((WiFi.status() != WL_CONNECTED) && (tryCounter > 0)) {
    delay(500);
    Serial.print(".");
    tryCounter = tryCounter - 1;
  }
  Serial.println("");
  if (tryCounter > 0) {
    Serial.println("WiFi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    connectState = true;
  } else {
    Serial.println("WiFi connect failed");
  }
  return connectState;
}

byte sendEmail() {
  byte thisByte = 0;
  byte respCode;

  if (client.connect(server, 587) == 1) {
    Serial.println(F("connected"));
  } else {
    Serial.println(F("connection failed"));
    return 0;
  }
  if (!eRcv()) return 0;

  Serial.println(F("Sending EHLO"));
  client.println("EHLO smtp.1und1.de");
  if (!eRcv()) return 0;
  Serial.println(F("Sending auth login"));
  client.println("auth login");
  if (!eRcv()) return 0;
  Serial.println(F("Sending User"));
  // Change to your base64, ASCII encoded user
  client.println("XXXXXXXXXX"); //<--------- User
  if (!eRcv()) return 0;
  Serial.println(F("Sending Password"));
  // change to your base64, ASCII encoded password
  client.println("XXXXXXXXXX"); //<--------- Password
  if (!eRcv()) return 0;
  Serial.println(F("Sending From"));
  // change to your email address (sender)
  client.println(F("MAIL From: dummy@compusurf.de"));
  if (!eRcv()) return 0;
  // change to recipient address
  Serial.println(F("Sending To"));
  client.println(F("RCPT To: dummy@compusurf.de"));
  if (!eRcv()) return 0;
  Serial.println(F("Sending DATA"));
  client.println(F("DATA"));
  if (!eRcv()) return 0;
  Serial.println(F("Sending email"));
  // change to recipient address
  client.println(F("To:  dummy@compusurf.de"));
  // change to your address
  client.println(F("From: dummy@compusurf.de"));
  client.println(F("Subject: Dummys Wetter\r\n"));
  client.print(F("Temperatur: "));
  client.print((String) temperature);
  client.println(F(" C"));
  client.println(F("\n"));
  client.print(F("Luftfeuchtigkeit: "));
  client.print((String) humidity);
  client.println(F(" %"));
  client.println(F("\n"));
  client.print(F("Sendezeit: "));
  client.println(dayTime);
  client.println(F("\n"));

  client.println(F("."));
  if (!eRcv()) return 0;
  Serial.println(F("Sending QUIT"));
  client.println(F("QUIT"));
  if (!eRcv()) return 0;
  client.stop();
  Serial.println(F("disconnected"));
  return 1;
}

byte eRcv() {
  byte respCode;
  byte thisByte;
  int loopCount = 0;
  while (!client.available()) {
    delay(1);
    loopCount++;
    // if nothing received for 10 seconds, timeout
    if (loopCount > 10000) {
      client.stop();
      Serial.println(F("\r\nTimeout"));
      return 0;
    }
  }
  respCode = client.peek();
  while (client.available()) {
    thisByte = client.read();
    Serial.write(thisByte);
  }
  if (respCode >= '4') {
    efail();
    return 0;
  }
  return 1;
}

void efail() {
  byte thisByte = 0;
  int loopCount = 0;
  client.println(F("QUIT"));
  while (!client.available()) {
    delay(1);
    loopCount++;
    // if nothing received for 10 seconds, timeout
    if (loopCount > 10000) {
      client.stop();
      Serial.println(F("efail \r\nTimeout"));
      return;
    }
  }
}

void print_wakeup_reason() {
  wakeup_reason = esp_sleep_get_wakeup_cause();
  switch (wakeup_reason) {
    case 1  : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2  : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3  : Serial.println("Wakeup caused by timer"); break;
    case 4  : Serial.println("Wakeup caused by touchpad"); break;
    case 5  : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}