Projektvorstellung

UHR3.1 - Update der dritten Version meiner TEXTUHR auf Arduino-Basis



Um diese Projektbeschreibung zu verstehen, sollte man mit UHR3 vertraut sein.

Das Update:


Nachdem UHR3 zwischendurch nicht mehr lief und ich das DCF77-Modul nicht mehr richtig in Gang bringen konnte, lag das schöne Teil lange Zeit rum. Schließlich hatten der Einzug meines Schatzis und Umbau meiner Wohnung auch erstmal Vorrang.

Inzwischen hab ich wieder etwas Zeit gefunden und eine komplett neue Elektronik für UHR3 gebaut. Die Zeit kommt nicht mehr von DCF77 - die Störanfälligkeit nervte mich doch gewaltig. Statt dem ATMega328 werkelt jetzt ein ESP8266-12E in der Uhr und dessen WLAN-Fähigkeiten nutze ich auch gleich, um die Zeit zu aktualisieren.

Er verbindet sich zu einem lokalen RaspberryPi, den ich für meherere kleine Projekte nutze und ruft eine php-Seite auf, die einfach nur Datum und Zeit in einem festen Format ausgibt.
Der php-Code:

		<?php
		$timestamp = time();
		$datum = date("d-m-y", $timestamp);
		echo ("---DAT ");
		echo $datum;
		$zeit = date("H-i-s", $timestamp);
		echo ("\r\n---UHR ");
		echo $zeit;
		?>
		


Die Ausgabe sieht dann etwa so aus: ---DAT 04-10-17 ---UHR 18-41-48

Ansonsten wurde das Gehäse noch schwarz gestrichen, damit es besser in die Umgebung passt (und Schatzi gefällt).

 

Die neue Elektronik:

Das neue Herz der Uhr ist ein ESP8266-12E, der über die Arduino-IDE programmiert wurde. Die Platine trägt ansonsten nur Spannungsregler und einen 74HCT541, der als Levelshifter fungiert und Stripe und Display mit den nötigen 5V-Signalen versorgt. Ein 74HCT125 hätte es auch getan, aber beim 541er ist mir die Pinbelegung sympathischer ;-)
Ansonsten die Pfostenstecker aus der Version 3, an die Stripe, Helligkeitssensor und Display angeschlossen sind. Auf eine RTC hab ich hier verzichtet, weil die Zeit alle zwei Minuten geholt wird - das sollte reichen.
Die Adventskranz-Erweiterung ist ebenso nicht mehr an Bord - war mir nicht wichtig genug.


Die neue Steuerung: Links oben die Spannungsregler, mittig der ESP, rechts der 74HCT541. Das schwarz unten kommt vom Streichen, obwohl der 7805 schon recht warm wird.



Der Sketch (das "Programm"):


Der Sketch sollte sich wieder von selbst erklären - nachdem er fast komplett kommentiert ist.


#include <ESP8266WiFi.h>                           // Library fürs WLAN des ESP8266
#include <Adafruit_NeoPixel.h>                     // Library für Stripe-Ansteuerung
#include <Time.h>                                  // Library mit Zeit-Funktionen einbinden
#include "UC121902-TNARX-A.h"                  // Library fürs Display einbinden
UC121902_TNARX_A::Display display (12,13,5);             // Display einstellen an Pins 12,13 und 15
#define PIXELPIN 4                                       // Datenpin für Stripe: 4
#define NUMPIXELS 37                                     // Stripe hat 36 LEDs/Pixels
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800);    //Stripe-Daten einstllen
#define hsensor A0                                       // Helligkeitssensor hängt am analogen Eingang (Pin2 des ESP)
int hell;                                                // Helligkeitswert aus dem Sensor
int ledhell=255;                                         // Helligkeit der LEDs
int mehrrot;                                             // Variable für höheren Rot-Wert (abends)
int mehrblau;                                            // Variable für höheren Blau-Wert (morgens)
const char* ssid     = "MEINESSID";            // WLAN-SSID einstellen
const char* password = "MEINPASSWORT"          // WLAN-Key
const char* host = "SERVERNAME";               // URL des Servers, von dem die Zeit geholt wird 
String url = "/zeit.php";                      // Seite, die die Zeit anzeigt
int stunde;                                              // 
int minut;                                               // 
int sekunde;                                             //  Variablen für die geholten Zeit-Daten
int tag;
int monat;
int jahr;
String disp;                                             // Variable für den Display-Inhalt
byte dispcount;                                          // Hilfsvarialbe fürs Umschalten des Displays

unsigned long lastholen;                                 // Wann wurde zuletzt die Zeit geholt?
unsigned long lastzeigen;                                // Wann wurde zuletzt die Zeit angezeigt?
 
void setup() {
  //Serial.begin(115200);
  delay(10);
  pixels.begin();                                         // Initalisieren des Stripes
  delay(30);
  display.begin();                                        // Initalisieren des Displays
 
  /*Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  */
  WiFi.begin(ssid, password);                             // WLAN-Verbindung herstellen
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    //Serial.print(".");
  }
 
  /*Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();
  */
}

void loop(){
  if (millis()-lastholen > 120000 || (lastholen==0 && millis()>5000)){    // 120 Sekunden nach dem letzten Holen oder 5 Sek nach Start
    zeitholen();                                         // Funktion zum Holen der Zeitinfo
    lastholen=millis();                                  // Merken, wann die letzte Zeitinfo geholt wurde
  }
  if (millis()-lastzeigen > 1000){                    // jede Sekunde:
    zeitdisplay();                                       // Zeit am Display anzeigen
    zeitzuled(hour(),minute());                          // Zeit auf dem Stripe anzeigen
    lastzeigen=millis();                                 // Merken, wann zuletzt die Zeit angezeigt wurde
    helligkeit();                                        // Helligkeit neu prüfen
  }
}

void zeitzuled(int h, int m)                              // *** Funktion Stripeausgabe
{
  /* 
    LEDs und Anzeigen
    35->fünf    33->zehn    31->viertel    29->vor   27->nach    25->halb    23->1   21->2   19->3   17->4    15->5    13->6   11->7    9->8   7->9   5->10  3->11   1->12
  */
  pxoff();                                                // Funktion aufrufen, das alle Pixel auf AUS setzt
  mehrblau=0;                                             // Variable für mehr blau auf Null
  mehrrot=0;                                              // Variable für mehr rot auf Null
  if (h<7) mehrblau=20;                                // Wenn Stunde kleiner als 7, Blauwert um 20 erhöhen
  if (h>18) mehrrot=20;                                // Wenn Stunde größer als 18, Rotwert um 20 erhöhen
  if (m>17) h+=1;                                      // ab Minute 18 -> 1 Std größer anzeigen  (13:20 -> zehn vor halb ZWEI)
  if (h>12) h-=12;                                     // 12h-Format, also nach 12 Uhr 12 Std abziehen
  if (h==0) h=12;                                         // 0 uhr = 12
  pxon(25-h*2);                                           // Stunde anzeigen (z.B. Std 6 -> Pixel 13)
  if (m>2 && m<8) { pxon(35); pxon(27); }           // fünf nach
  if (m>7 && m<13) { pxon(33); pxon(27); }          // zehn nach
  if (m>12 && m<18) { pxon(31); pxon(27); }               // viertel nach
  if (m>17 && m<23) { pxon(33); pxon(29); pxon(25); }     // zehn vor halb
  if (m>22 && m<28) { pxon(35); pxon(29); pxon(25); }     // fünf vor halb
  if (m>27 && m<33) { pxon(25); }                         // halb
  if (m>32 && m<38) { pxon(35); pxon(27); pxon(25); }     // fünf nach halb
  if (m>37 && m<43) { pxon(33); pxon(27); pxon(25); }     // zehn nach halb
  if (m>42 && m<48) { pxon(31); pxon(29); }               // viertel vor
  if (m>47 && m<53) { pxon(33); pxon(29); }               // zehn vor
  if (m>52 && m<58) { pxon(35); pxon(29); }               // fünf vor
  if (year()==1970) pxoff();                              // nichts anzeigen, solange Uhr noch nicht gestellt
  pixels.show();                                          // alle Pixel gesetzt, Daten auf Stripe schreiben
}


void zeitholen() {
  display.prog.turnOn();                                  // Das Feld "PROG" auf dem Display einschalten
  display.print("          ");                  // Das Display leeren
  //Serial.print("connecting to "); 
  //Serial.println(host);
  WiFiClient client;                                      // Verbindung mit dem Server
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {                  // Wenn keine Verdindung zustande kommt...
    //Serial.println("connection failed");
    display.chan.turnOn();                                // Das Feld "CHAN" auf dem Display einschalten
    display.prog.turnOff();                               // Das Feld "PROG" auf dem Display ausschalten
    return;                                               // zum loop (CHAN bleibt an als Indikator, dass Update nicht erfolgt)
  }
  //Serial.print("Requesting URL: ");
  //Serial.println(url);
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +   // Request senden
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  // start waiting for the response             
  unsigned long lasttime = millis();                      // Zeit des letzten Updates speichern
  while (!client.available() && millis() - lasttime < 1000) {delay(1);}   // max 1s warten
  while (client.available()) {
    String line = client.readStringUntil('\r');           // Antwort zeilenweise lesen
    if (line.indexOf("---DAT")>=0) {         // Wenn die Zeile "---DAT" enthält...
      display.chan.turnOff();                             // CHAN ausschalten (Indikator, dass Update erfolgt)
      //Serial.println();
      //Serial.print("**Datum: ");
      //Serial.println(line.substring(8, 16));
      tag=line.substring(8,10).toInt();                   // Datumsangaben in Zahlen umwandeln  
      monat=line.substring(11,13).toInt();
      jahr=line.substring(14,16).toInt();
    }
    if (line.indexOf("---UHR")>=0) {         // Wenn die Zeile "---UHR" enthält...   
      Serial.println();
      //Serial.print("**Zeit: ");
      //Serial.println(line.substring(8, 16));
      stunde=line.substring(8,10).toInt();                // Zeitangaben in Zahlen umwandeln  
      minut=line.substring(11,13).toInt();
      sekunde=line.substring(14,16).toInt();
    }

  setTime(stunde,minut,sekunde,tag,monat,jahr);           // Zeit und Datum einstellen
    
     
    //Serial.print(line);
  }

//  Serial.println("closing connection");
  display.prog.turnOff();                                // PROG am Display ausschalten
}

void zeitdisplay(){                                      // *** Funktion Displayausgabe
  disp=" ";                                    // Stringvariable für Displayinhalt leeren
  if (dispcount<6){                                   // wenn wengier als 6 Sek vergangen...
    disp += " ";                               // Leerzeichen hinzufügen
    if (hour()<10) disp += "0";             // falls Stunde einstellig, Null einfügen
    disp += hour();                                      // Stunde einfügen
    disp += " ";                               // Leerzeichen einfügen
    if (minute()<10) disp += "0";           // falls Miunte einstellig, Null einfügen
    disp += minute();                                    // Minute einfügen
    disp += " ";                               // Leerzeichen einfügen
    if (second()<10) disp += "0";           // falls Sekunde einstellig, Null einfügen
    disp += second();                                    // Sekunde einfügen
  }
  else{                                                  // ... sonst (6 oder mehr Sek vergangen)
    if (day()<10) disp += "0";              // falls Tag einstellig, Null einfügen
    disp += day();                                       // Tag einfügen
    disp += " ";                               // Leerzeichen einfügen
    if (month()<10) disp += "0";            // falls Monat einstellig, Null einfügen
    disp += month();                                     // Monat einfügen
    disp += " ";                               // Leerzeichen einfügen
    disp += year();                                      // Jahr einfügen (ist vierstellig)
  }
  if (year()==1970) disp="  - SEt -  ";
  display.print(disp);                                   // generierten String auf Display schreiben
  

  dispcount++;                                           // Zähler um eins erhöhen 
  if (dispcount==10) dispcount=0;                        // bei 10 auf Null setzen (nach 10 Sekunden)
}

void zeitseriell(){                                      // Kontoll-Funktion aus der Beta-Zeit
/*  Serial.println();
  if(day()<10) Serial.print("0");
  Serial.print(day());
  Serial.print(".");
  if(month()<10) Serial.print("0");
  Serial.print(month());
  Serial.print(".");
  Serial.print(year());
  Serial.print("  ");
  if(hour()<10) Serial.print("0");
  Serial.print(hour());
  Serial.print(":");
  if(minute()<10) Serial.print("0");
  Serial.print(minute());
  Serial.print(":");
  if(second()<10) Serial.print("0");
  Serial.print(second());
  */
}

void pxon(int px){                                        // *** Funktion für Pixelansteuerung
  pixels.setPixelColor(px,pixels.Color(ledhell+mehrrot,ledhell,ledhell+mehrblau));    
  pixels.setPixelColor(px+1,pixels.Color(ledhell+mehrrot,ledhell,ledhell+mehrblau));  
}
void pxoff(){                                             // *** Funktion für alle Pixel aus
  for (int i=0;i<NUMPIXELS;i++){                       // für jedes Pixel von 0 bis NUMPIXELS (Anzahl der Pixel)...
    pixels.setPixelColor(i,pixels.Color(0,0,0));          // ... setze alle 3 Farben (RGB) auf Null
  }
}

void helligkeit(){
  hell=analogRead(hsensor);                              // Helligkeitswert einlesen
  ledhell =20+ hell/5;                                   // ... und umrechnen 
}


Ausblick / ToDo / Dankesworte:

 

Falls jemand Vorschläge hat oder Fehler findet, darf er/sie sich gerne melden!
An dieser Stelle herzlichen Dank für die vielen Rückmeldungen auf die erste Version. Es war immer schön, mit anderen Uhrenbastlern aus ganz Europa zu fachsimpeln und Erfahrungen auszutauschen!
Ein besonderer Gruß geht an phi, Frank und Matthias! ;)


Alle in erwähnten Marken- und Warenzeichen oder Produktnamen sind Eigentum ihrer jeweiligen Inhaber.
Für die Inhalte externer Links sind die jeweiligen Seitenbetreiber verantwortlich.

Keine Haftung für eventuelle Schäden.