Wortuhr Sketch ändern Wortausgabe

Hallo
Meine erste selbst gebaute Wortuhr habe ich leider nicht zum laufen gebracht.
Die Vorstellung der Uhr lag schon Jahre zurück, es gab keine Unterstützung mehr, ich bin nicht damit zurecht gekommen.
Ich habe nach langem suchen im Netz eine neue gefunden die meinem alten Modell ähnelt.
Über den Bau der Uhr und die Inbetriebnahme gibt es auf YouTube mehrere Videos.
So dass ich als Anfänger besser zurecht gekommen bin.
Ich habe meine Uhr entsprechend umgebaut, die Uhr habe ich fertig gestellt und sie läuft einwandfrei.
Die Uhr stammt von SPS&Technik wo auch der Sketch herkommt.
Es gibt ein Video wo der Code erklärt wird, für Leute die Änderungen in bestimmten Bereichen vornehmen wollen, was ich gerne machen würde.
Der Ersteller des Codes wird so mit Fragen bombardiert das er nicht alle beantworten kann.
Meine Bitte wäre, ob mir jemand helfen könnte den Code dahin zu ändern das bestimmte Uhrzeiten anders dargestellt werden, Ich bin da überfordert.
Es geht um die zwei Varianten Viertel nach und Viertel Vor.
als Beispiel 11.15Uhr ist Viertel nach Elf gewünscht wäre Viertel Zwölf
11.45Uhr ist Viertel vor Zwölf gewünscht wäre Dreiviertel Zwölf

Buchstaben Layout und den Sketch habe ich angehängt

Gruß Peter
Wordclock LED-Adressen 114 LEDs.pdf (133,1 KB)


/*
 * Projekt: Wordclock
 * Version: 1.02.01
 * Datum: 29.12.2021
 * Autor: SPSundTechnik
 * 
 * 
 * Changelog:
 * - Bugfixes
 * - Fehler Stundenzahl erhöhen ab HALB
 * - Fehler Dimmwert für BLAU berechnen
 * - Fehler Sonderfall EIN/EINS
 * 
*/


//Externe Bibliotheken
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <EEPROM.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include "Parameter.h"
#include "HTML.h"


//******************************************************** PARAMETER ********************************************************


//********************************************************** WS2812B ********************************************************

//Pin des ESP8266
const uint16_t Pin = D6;

//Bitte auswählen, welcher WordClock-Typ verwendet wird
// TYPE  0  => für eine WordClock ohne Minutenanzeige
// TYPE  1  => für eine WordClock mit Minutenanzeige
#define TYPE  1

//WordClock ohne Minutenanzeige
#if TYPE == 0
  bool WordClockType = LOW;
  //Anzahl der LED's
  const uint16_t Numbers = 110;
#endif

//WordClock mit Minutenanzeige
#if TYPE == 1
  bool WordClockType = HIGH;
  //Anzahl der LED's
  const uint16_t Numbers = 114;
#endif

//***************************************************************************************************************************


//*********************************************** ACCESS POINT & DNS SERVER *************************************************

//Zugangsdaten Access Point
//SSID
const char SSID_AccessPoint[] = "WordClock";
//Passwort
const char Password_AccessPoint[] = "WordClock";

//URL DNS Server
//www.wordclock.local
const char WebserverURL[] = "www.wordclock.local";

//***************************************************************************************************************************


//******************************************* FARBWERTE UND HELLIGKEIT DER LED's ********************************************

//Beispiel: RGB-Farbschema Rot = 50; Grün = 50; Blau = 50 => ergibt einen weißen Farbton.
//Tipp: Der blaue Anteil muss bei den WS2812B-LED's etwas reduziert werden, da dieser dominanter ausfällt.
//Helligkeit der LED's kann mit Werten zwischen 0 - 255 eingestellt werden.
//Beispiel 1: Rot = 255;  Grün = 255; Blau = 255 => weißer Farbton mit maximaler Helligkeit aller LED's
//Beispiel 2: Rot = 0;    Grün = 0;   Blau = 255 => blauer Farbton mir maximaler Helligkeit der blauen LED
//Beispiel 3: Rot = 127;  Grün = 127; Blau = 127 => weißer Farbton mit 50% der Helligkeit aller LED's

//Farbwert ROT
const unsigned int RGBValueRed = 80;
//Farbwert GRÜN
const unsigned int RGBValueGreen = 80;
//Farbwert BLAU
const unsigned int RGBValueBlue = 75;

//***************************************************************************************************************************


//******************************************************** NACHTMODUS *******************************************************

//Display kann in einem definiertem Zeitfenster (24h-Format) ausgeschalten werden.
//Enable Display On/Off => Freigabe der Funktion
//Display Off => Uhrzeit wann das Display wieder ausgeschaltet werden soll
//Display On => Uhrzeit wann das Display wieder eingeschaltet werden soll

//Freigabe der Funktion
unsigned int EnableNightMode = 0;
//Uhrzeit Display ausschalten
unsigned int TimeDisplayOff = 0;
//Uhrzeit Display einschalten
unsigned int TimeDisplayOn = 0;

//***************************************************************************************************************************


//***************************************************** LOKALE UHRZEIT ******************************************************

//Wird die Uhr ohne eine WLAN-Verbindung gestartet, kann mit den folgenden Parametern eine Uhrzeit eingestellt werden.
//Die Uhrzeit wird als UTC-Zeit eingestellt. Sommerzeit (UTC+2) oder Winterzeit (UTC+1) können eingestellt werden.

//Stundenzahl in UTC (24h-Format)
const unsigned int localutcaktstunde = 11;
//Minutenzahl in UTC
const unsigned int localutcaktminute = 0;
//Sekundenzahl in UTC
const unsigned int localutcaktsekunde = 0;
//Einstellung Sommer-/Winterzeit
const unsigned int localutcx = 1;

//***************************************************************************************************************************


//******************************************************** DIAGNOSE *********************************************************

//Serielle Schnittstelle für die Diagnose öffnen
const bool debug = HIGH;

//***************************************************************************************************************************



//******************************************************* KONSTANTEN ********************************************************


//Portnummern
const unsigned int localPort = 2390;
const unsigned int DNSPort = 53;
const unsigned int HTTPPort = 80;


//Adresse des NTP-Serverpools
const char* NtpServerName = "de.pool.ntp.org";


//HTTP Status Codes
const int HTTPRequest = 200;
const int HTTPPost = 303;
const int HTTPNotFound = 404;


//Größe des NTP-Datenpaket
const unsigned int NtpPaketSize = 48;
byte NtpPaketBuffer[NtpPaketSize];


//Größe des UserInterface-Datenpaket
const unsigned int UserInterfacePaketSize = 12;
byte UserInterfacePaketBuffer[UserInterfacePaketSize];


//ID-Nummern
const int UdpIdHandshakeRequest = 100;
const int UdpIdHandshakeAnswer = 101;
const int UdpIdRgb = 102;
const int UdpIdTime = 103;
const int UdpIdDate = 104;
const int UdpIdDisplay = 105;


//Zeitintervalle in Millisekunden
const unsigned long secondperday = 86400;
const unsigned long secondperhour = 3600;
const unsigned long secondperminute = 60;
const unsigned long minuteperhour = 60;
const unsigned long millisecondpersecond = 1000;
const unsigned long IntervallUdp = 20;
const unsigned long IntervallDebug = 10000;
const unsigned long IntervallFade = 3;


//Anzahl der Tage im Monat
unsigned int tageImMonat[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


//Adresse der ersten und letzten LED
const int ersteLED = 0;
const int letzteLED = Numbers - 1;


//Faktor zur Berechnung der Stromaufnahme
const float currentPerRgbValue = 0.0795;


//Größe der Adressbereiche
const int NumberOfHours = 12;
const int NumberOfMinute = 4;
const int NumberOfAddMinutes = 4;
const int NumberOfSpecialChar = 2;
  

//Adressen der Stunden
const int HourAddr[NumberOfHours][2] = { {49, 53},      //Zwölf
                                         {60, 63},      //Eins
                                         {62, 65},      //Zwei
                                         {67, 70},      //Drei
                                         {77, 80},      //Vier
                                         {73, 76},      //Fünf
                                         {104, 108},    //Sechs
                                         {55, 60},      //Sieben
                                         {89, 92},      //Acht
                                         {81, 84},      //Neun
                                         {93, 96},      //Zehn
                                         {85, 87} };    //Elf

//Adressen der Minuten
const int MinuteAddr[NumberOfMinute][2] = { {7, 10},       //Fünf
                                            {18, 21},      //Zehn
                                            {26, 32},      //Viertel
                                            {11, 17} };    //Zwanzig

//Adressen Vor/Nach
const int ToPastAddr[NumberOfSpecialChar][2] = { {38, 41},     //Nach
                                                 {35, 37} };   //Vor

//Adressen Halb/Uhr
const int HalfFullHourAddr[NumberOfSpecialChar][2] = { {44, 47},      //Halb
                                                       {99, 101} };  //Uhr

//Adressen Es/Ist
const int EsIstAddr[NumberOfSpecialChar][2] = { {0, 1},     //Es
                                                {3, 5} };   //Ist

//Adressen der Minutenanzeige
const int AddMinutesAddr[NumberOfAddMinutes][2] = { {110, 110},    //Minute +1
                                                    {111, 111},    //Minute +2
                                                    {112, 112},    //Minute +3
                                                    {113, 113} };  //Minute +4

//Zeittabelle mit Takt +2/-2 Minuten
#if TYPE == 0
  int TimeTable[60][4] = { {0,0,0,2},{1,0,0,2},{2,0,0,2},{3,1,1,0},{4,1,1,0},{5,1,1,0},{6,1,1,0},{7,1,1,0},{8,2,1,0},{9,2,1,0},
                         {10,2,1,0},{11,2,1,0},{12,2,1,0},{13,3,1,0},{14,3,1,0},{15,3,1,0},{16,3,1,0},{17,3,1,0},{18,4,1,0},{19,4,1,0},
                         {20,4,1,0},{21,4,1,0},{22,4,1,0},{23,1,2,1},{24,1,2,1},{25,1,2,1},{26,1,2,1},{27,1,2,1},{28,0,0,1},{29,0,0,1},
                         {30,0,0,1},{31,0,0,1},{32,0,0,1},{33,1,1,1},{34,1,1,1},{35,1,1,1},{36,1,1,1},{37,1,1,1},{38,4,2,0},{39,4,2,0},
                         {40,4,2,0},{41,4,2,0},{42,4,2,0},{43,3,2,0},{44,3,2,0},{45,3,2,0},{46,3,2,0},{47,3,2,0},{48,2,2,0},{49,2,2,0},
                         {50,2,2,0},{51,2,2,0},{52,2,2,0},{53,1,2,0},{54,1,2,0},{55,1,2,0},{56,1,2,0},{57,1,2,0},{58,0,0,2},{59,0,0,2} };
#endif

//Zeittabelle mit Takt volle 5 Minuten
#if TYPE == 1
  int TimeTable[60][4] = { {0,0,0,2},{1,0,0,2},{2,0,0,2},{3,0,0,2},{4,0,0,2},{5,1,1,0},{6,1,1,0},{7,1,1,0},{8,1,1,0},{9,1,1,0},
                         {10,2,1,0},{11,2,1,0},{12,2,1,0},{13,2,1,0},{14,2,1,0},{15,3,1,0},{16,3,1,0},{17,3,1,0},{18,3,1,0},{19,3,1,0},
                         {20,4,1,0},{21,4,1,0},{22,4,1,0},{23,4,1,0},{24,4,1,0},{25,1,2,1},{26,1,2,1},{27,1,2,1},{28,1,2,1},{29,1,2,1},
                         {30,0,0,1},{31,0,0,1},{32,0,0,1},{33,0,0,1},{34,0,0,1},{35,1,1,1},{36,1,1,1},{37,1,1,1},{38,1,1,1},{39,1,1,1},
                         {40,4,2,0},{41,4,2,0},{42,4,2,0},{43,4,2,0},{44,4,2,0},{45,3,2,0},{46,3,2,0},{47,3,2,0},{48,3,2,0},{49,3,2,0},
                         {50,2,2,0},{51,2,2,0},{52,2,2,0},{53,2,2,0},{54,2,2,0},{55,1,2,0},{56,1,2,0},{57,1,2,0},{58,1,2,0},{59,1,2,0} };
#endif

//***************************************************************************************************************************



//******************************************************** VARIABLEN ********************************************************

int i = 0;
int z = 0;
int x = 0;
int y = 0;

unsigned long newsecsSince1900 = 0;
unsigned long oldsecsSince1900 = 0;

unsigned int oldstunde = 99;
unsigned int oldmezstunde = 0;
unsigned int oldminute = 99;

unsigned int utcaktstunde = 0;
unsigned int utcaktminute = 0;
unsigned int utcaktsekunde = 0;

unsigned int mezaktstunde = 0;
unsigned int mezaktstunde24 = 0;
unsigned int mezaktminute = 0;
unsigned int mezaktsekunde = 0;

unsigned long newtageSeit1900 = 0;
unsigned long oldtageSeit1900 = 0;
unsigned int newschaltTage = 0;

unsigned int wochenTag = 0;

unsigned int datumTag = 0;
unsigned int datumMonat = 0;
unsigned int datumJahr = 0;

unsigned int tagNummer = 0;

unsigned int utcx = 0;

unsigned int oldschaltTage = 0;
bool schaltJahr = LOW;
bool sonderFall366 = LOW;

unsigned long lastmillisDebug = 0;
unsigned long lastmillisSecond = 0;
unsigned long lastmillisdifferenz = 0;
unsigned long lastmillisFade = 0;
unsigned long LastMillisUdp = 0;

unsigned int UdpId = 0;

unsigned int UdpHour = 0;
unsigned int UdpMinute = 0;
unsigned int UdpSecond = 0;

unsigned int UdpDay = 0;
unsigned int UdpMonth = 0;
unsigned int UdpYear = 0;

unsigned int red = 0;
unsigned int green = 0;
unsigned int blue = 0;

float newredminus = 0.0;
float newgreenminus = 0.0;
float newblueminus = 0.0;

float newredplus = 0.0;
float newgreenplus = 0.0;
float newblueplus = 0.0;

float fadevaluered = 1.0;
float fadevaluegreen = 1.0;
float fadevalueblue = 1.0;

unsigned int currentPerLed = 0;

unsigned int pointer = 0;

unsigned int newletter[Numbers];
unsigned int showletter[Numbers];
unsigned int changeletter[Numbers];

bool newdisplay = LOW;
bool setDisplayOff = LOW;

bool NoWifi = LOW;

//***************************************************************************************************************************



//IP-Adressen
IPAddress TimeServerIP;
IPAddress UserInterfaceIP;
//IP Adressen Access Point
IPAddress IPAdress_AccessPoint(192,168,10,2);
IPAddress Gateway_AccessPoint(192,168,10,0);
IPAddress Subnetmask_AccessPoint(255,255,255,0);

//Wifi UDP
WiFiUDP UDP;

//Neopixel
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(Numbers, Pin, NEO_GRB + NEO_KHZ800);

//Webserver
ESP8266WebServer Webserver(HTTPPort);

//DNS Server
DNSServer DnsServer;

//Parameter
Parameter_t Parameter;



//SETUP
void setup() {


  //Array initialisieren
  for(i = ersteLED; i <= letzteLED; i++) {
    newletter[i] = 0;   
  }

  //Array initialisieren
  for(i = ersteLED; i <= letzteLED; i++) {
    showletter[i] = 0;   
  }

  //Array initialisieren
  for(i = ersteLED; i <= letzteLED; i++) {
    changeletter[i] = 2;   
  }


  //Serielle Schnittstelle für Debuging öffen
  if(debug == HIGH) {
    Serial.begin(115200);
  }
  

  //Leds initialisieren
  pixels.begin();


  //Paramater aus dem EEPROM lesen
  LoadParameter();

  //Startwerte laden, falls ein Fehler im EEPROM vorliegt
  if(Parameter.ConfigSaved != 1){
    
    //Startwerte als Parameter speichern
    Parameter.RGBValueRed = RGBValueRed;
    Parameter.RGBValueGreen = RGBValueGreen;
    Parameter.RGBValueBlue = RGBValueBlue;
    Parameter.EnableNightMode = EnableNightMode;
    Parameter.TimeDisplayOff = TimeDisplayOff;
    Parameter.TimeDisplayOn = TimeDisplayOn;
    Parameter.ConfigSaved = 1;
    
    //Parameter im EEPROM speichern
    SaveParameter();
    
  }


  //Start: Aufbau der WLAN-Verbindung

  //Wifi Verbindung aufbauen
  WiFi.mode(WIFI_STA);
  WiFi.begin(Parameter.Ssid, Parameter.Password);

  //Wartezeit damit die Verbindung aufgebaut wird
  delay(3000);

  //Kontrolle ob die Verbindung aufgebaut wurde
  do{

    //Kein WLAN vorhanden
    if(x >= 2){
      NoWifi = HIGH;
      break;      
    }  
    
    //Start: Test alle LEDs bei Neustart der Uhr

    //Testdurchlauf => alle LEDs nacheinander einschalten
    for(i = ersteLED; i <= letzteLED; i++) {   
      pixels.setPixelColor(i, pixels.Color(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue));
      pixels.show();
      delay(50);
    }

    delay(1000);
    
    //Testdurchlauf => alle LEDs umgekehrt ausschalten
    for(i = letzteLED; i >= ersteLED; i--) {
      pixels.setPixelColor(i, pixels.Color(0,0,0));
      pixels.show();
      delay(50);
    }

    //Ende: Test alle LEDs bei Neustart der Uhr
    
    //Anzahl der Durchläufe zählen
    x++;
    
  }while(WiFi.status() != WL_CONNECTED);

  //Ende: Aufbau der WLAN-Verbindung



  //Kein WLAN gefunden
  //Access Point & Webserver starten
  if(NoWifi == HIGH){

    //Access Point aufbauen
    WiFi.mode(WIFI_AP);
    WiFi.softAPConfig(IPAdress_AccessPoint, Gateway_AccessPoint, Subnetmask_AccessPoint);
    WiFi.softAP(SSID_AccessPoint, Password_AccessPoint);

    //DNS Server starten
    DnsServer.setTTL(300);
    DnsServer.start(DNSPort, WebserverURL, IPAdress_AccessPoint);

    //Webserver starten
    Webserver.on("/", HandlerWebpage);
    Webserver.on("/refresh", HTTP_POST, HandlerRefresh);
    Webserver.on("/save", HTTP_POST, HandlerSave);
    Webserver.onNotFound(HandlerNotFound); 
    Webserver.begin();    

    //Lokale Uhrzeit als Startzeit setzen
    utcaktstunde = localutcaktstunde;
    utcaktminute = localutcaktminute;
    utcaktsekunde = localutcaktsekunde;
    utcx = localutcx;

    //UDP-Protokoll
    UDP.begin(localPort);
    
  }


  //WLAN-Verbindung aufgebaut
  else{
    
    //UDP-Protokoll
    UDP.begin(localPort);

    //Start: NTP-Anfrage und NTP-Antwort

    //Zeitwerte vom NTP-Server abrufen
    do{
      SendNtpRequest();
      UdpDataReceive();
    }while(newsecsSince1900 == 0);
  
    //Ende: NTP-Anfrage und NTP-Antwort
    
    //Systemzeit speichern für den nächsten Vergleich
    lastmillisSecond = millis();

    //Berechnung der Jahreszahl
    datumJahr = datumJahr_calc(newsecsSince1900);

    //Berechnung der Anzahl der Schalttage seit dem 1. Januar 1900
    for(i = 1900; i < datumJahr; i++){
    
      //Aufruf der Funktion zur Erkennung ob ein Schaltjahr vorliegt
      schaltJahr = schaltJahrJN(i);
        
      if(schaltJahr == HIGH){
        newschaltTage++;
      }
    }

    newschaltTage = newschaltTage - 1;
    
  }


  //Start: Farbwerte zum Dimmen der LED's berechnen

  fadevaluered = FadeValueRed(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
  fadevaluegreen = FadeValueGreen(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
  fadevalueblue = FadeValueBlue(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
   
  //Farbwerte um x Nachkommastellen verschieben
  for(i = 0; i <= 10; i++) {

    if(fadevaluered < 1.0 && fadevaluegreen < 1.0 && fadevalueblue < 1.0){
      break;
    }

    fadevaluered = fadevaluered / 10.0;
    fadevaluegreen = fadevaluegreen / 10.0;
    fadevalueblue = fadevalueblue / 10.0;

  }
  
  //Ende: Farbwerte zum Dimmen der LED's berechnen
  
  
  //Farbwerte übernehmen
  newredminus = Parameter.RGBValueRed;
  newgreenminus = Parameter.RGBValueGreen;
  newblueminus = Parameter.RGBValueBlue;


  //Wort "ES" anzeigen
  WriteArray(0, EsIstAddr, newletter, 1);
  //Wort "IST" anzeigen
  WriteArray(1, EsIstAddr, newletter, 1);
  
}


//LOOP
void loop() {
  

  //Start: Webserver
  
  //Webserver Client
  if(NoWifi == HIGH && newdisplay == LOW){
    Webserver.handleClient();
    DnsServer.processNextRequest();
  }

  //Ende: Webserver

  

  //Start: UDP-Datenpakete empfangen
  
  //UDP-Daten in einem festen Zeitintervall abfragen
  if(millis() - LastMillisUdp > IntervallUdp && newdisplay == LOW){
    
    //Systemzeit speichern für den nächsten Vergleich
    LastMillisUdp = millis();

    UdpDataReceive();
    
  }

  //Ende: UDP-Datenpakete empfangen

  

  //Start: NTP-Anfrage
  
  //Zeitwerte bei jedem Stundenwechsel aktualisieren
  if (mezaktstunde != oldmezstunde && newdisplay == LOW && NoWifi == LOW) {

    //Zeit für den nächsten Vergleich speichern
    oldmezstunde = mezaktstunde;

    //Zeitwerte vom NTP-Server anfragen
    SendNtpRequest();
      
  }

  //Ende: NTP-Anfrage
  


  //Start: Datum und Uhrzeit ermitteln
  
  //Kontrolle ob eine Änderung bei dem Zeitstempel stattgefunden hat
  if(newsecsSince1900 > oldsecsSince1900) {
    
    //Änderung abspeichern für den nächsten Vergleich
    oldsecsSince1900 = newsecsSince1900;
    
    //Tage seit dem 1. Januar 1900; 00:00:00 Uhr
    newtageSeit1900 = newsecsSince1900 / secondperday;
    

    //Start: Datum ermitteln

    //Kontrolle ob eine Änderung bei den Tagen seit dem 1. Januar 1900 stattgefunden hat
    if(newtageSeit1900 > oldtageSeit1900) {

      //Änderung abspeichern für den nächsten Vergleich
      oldtageSeit1900 = newtageSeit1900;


      //Aufruf der Funktion zur Berechnung des Wochentages
      wochenTag = wochenTag_calc(newtageSeit1900);


      //Berechnug der Jahreszahl
      datumJahr = ((newtageSeit1900 - newschaltTage) / 365.002f) + 1900;
      

      //Aufruf der Funktion zur Erkennung ob ein Schaltjahr vorliegt
      schaltJahr = schaltJahrJN(datumJahr);
      

      //Funktion fürs Schaltjahr
      if(schaltJahr == HIGH) {

        tageImMonat[2] = 29;


        //Anzahl der Schalttage seit dem 1. Januar 1900 !ERST! am Ende des jeweiligen Schaltjahres hochzählen
        //Sonderfall: Zur Berechnung des 366. Tages wird der "alte" Wert der Schalttage benötigt, die Berechnung
        //            der Jahreszahl am 366. Tag muss aber mit dem neuen Wert durchgeführt werden

        //Berechnung der Anzahl der Schalttage seit dem 1. Januar 1900
        if(datumMonat == 12 && datumTag == 30) {

          oldschaltTage = newschaltTage;

          newschaltTage = newschaltTage + 1;
        
          sonderFall366 = HIGH;
        }
      }

      else {
        tageImMonat[2] = 28;
      }


      //Berechnung der wieviele Tag im Jahr 
      //Sonderfall: Die Berechnung des 366. Tages im Schaltjahr muss mit dem "alten" Wert der Schalttage (seit 1900) berechnet werden
      if(sonderFall366 == HIGH) {

        tagNummer = (newtageSeit1900 - ((datumJahr - 1900) * 365) - oldschaltTage);

        sonderFall366 = LOW;
      }

      //Ansonsten muss mit dem aktuellen Wert der Schalttage (seit 1900) gerechnet werden
      else {      
        tagNummer = (newtageSeit1900 - ((datumJahr - 1900) * 365) - newschaltTage);
      }

      //Aufruf der Funktion zur Berechnung des Monates
      datumMonat = welcherMonat(tagNummer, schaltJahr);

      datumTag = 0;

      //Berechnung der bereits vergangenen Tagen im Jahr
      for(i = 0; i < datumMonat; i++) {      
        datumTag = datumTag + tageImMonat[i];
      }

      //Tag-Nummer minus bereits vergangener Tage im Jahr = aktueller Tag
      datumTag = tagNummer - datumTag;
      
    }

    //Ende: Datum ermitteln

    

    //Start: Sommer-/ Winterzeit einstellen
    
    //am letzten Sonntag im März (03) wird von UTC+1 auf UTC+2 umgestellt; 02:00 -> 03:00
    //am letzten Sonntag im Oktober (10) wird von UTC+2 auf UTC+1 umgestellt; 03:00 -> 02:00

    //Aufruf jeden Sonntag im März
    if(datumMonat == 3 && wochenTag == 7) {

      //Letzter Sonntag im März
      if((tageImMonat[3] - datumTag) < 7) {
        
        //Umstellung auf Sommerzeit UTC+2
        utcx = 2;
      }
    }

    //Aufruf jeden Sonntag im Oktober
    if(datumMonat == 10 && wochenTag == 7) {

      //Letzter Sonntag im Oktober
      if((tageImMonat[10] - datumTag) < 7) {
        
        //Umstellung auf Winterzeit UTC+1
        utcx = 1;
      }
    }

    //Sommer-/Winterzeit bei Neustart bestimmen
    if(utcx == 0){

      //Neustart im Monat März
      if(datumMonat == 3){

        //Neustart in der letzten Woche im März
        if((tageImMonat[3] - datumTag) < 7){

          //Beispiel Jahr 2020: März 31 Tage; Neustart 26. März 2020 (Donnerstag = Wochentag = 4); 5 Tage Rest; Letzter Sonntag 29. März 2020
          //Berechnung: 31 - 26 = 5; 5 + 4 = 9;
          //Ergebnis: Letzter Tag im März ist ein Dienstag. Es folgt noch ein weiterer Sonntag im Oktober => Winterzeit einstellen
          
          //Beispiel Jahr 2021: März 31 Tage; Neustart 30. März 2021 (Dienstag = Wochentag = 2); 1 Tage Rest; Letzter Sonntag 28. März 2021
          //Berechnung: 31 - 30 = 1; 1 + 2 = 3;
          //Ergebnis: Letzter Tag im März ist ein Mittwoch. Umstellung auf Sommerzeit bereits erfolgt => Sommerzeit einstellen

          //Es folgt innerhalb der letzten Woche im März noch ein weiterer Sonntag => Winterzeit einstellen
          if(((tageImMonat[3] - datumTag) + wochenTag) >= 7){
            utcx = 1;
          }

          //Letzter Sonntag im März bereits vergangen => Sommerzeit einstellen
          else{
            utcx = 2;
          }
        }

        //Neustart in den ersten drei Wochen im März => Winterzeit einstellen
        else{
          utcx = 1;
        }
      }

      //Neustart im Monat Oktober
      if(datumMonat == 10){

        //Neustart in der letzten Woche im Oktober
        if((tageImMonat[10] - datumTag) < 7){

          //Beispiel Jahr 2020: Oktober 31 Tage; Neustart 26. Oktober 2020 (Montag = Wochentag = 1); 5 Tage Rest; Letzter Sonntag 25. Oktober 2020
          //Berechnung: 31 - 26 = 5; 5 + 1 = 6;
          //Ergebnis: Letzter Tag im Oktober ist ein Samstag. Umstellung auf Winterzeit bereits erfolgt => Winterzeit einstellen
          
          //Beispiel Jahr 2021: Oktober 31 Tage; Neustart 26. Oktober 2021 (Dienstag = Wochentag = 2); 5 Tage Rest; Letzter Sonntag 31. Oktober 2021
          //Berechnung: 31 - 26 = 5; 5 + 2 = 7;
          //Ergebnis: Letzter Tag im Oktober ist ein Sonntag. Es folgt noch ein weiterer Sonntag im Oktober => Sommerzeit einstellen

          //Es folgt innerhalb der letzten Woche im Oktober noch ein weiterer Sonntag => Sommerzeit einstellen
          if(((tageImMonat[10] - datumTag) + wochenTag) >= 7){
            utcx = 2;
          }

          //Letzter Sonntag im Oktober bereits vergangen => Winterzeit einstellen
          else{
            utcx = 1;
          }
        }

        //Neustart in den ersten drei Wochen im Oktober => Sommerzeit einstellen
        else{
          utcx = 2;
        }
      }

      //Falls Neustart der Uhr innerhalb der Sommerzeit (Ausnahme März/Oktober)
      if(datumMonat > 3 && datumMonat < 10) {      
        utcx = 2;
      }

      //Falls Neustart der Uhr innerhalb der Winterzeit (Ausnahme März/Oktober)
      if(datumMonat < 3 || datumMonat > 10) {    
        utcx = 1;
      }        
    }

    //Ende: Sommer-/ Winterzeit einstellen
    


    //Start: Aktuelle Uhrzeit hh:mm:ss aus dem NTP-Paket ermitteln
    
    //Beispiel: Berechnung der aktuellen Stunde 
    //Sekunden seit dem 1. Januar 1900 (3812811794) / Sekunden pro Tag (86400) = 44129 volle Tage seit dem 1. Januar 1900
    //Tage seit dem 1. Januar (44129) * Sekunden pro Tag (86400) = 3812745600 Sekunden der vollen Tage seit dem 1. Januar 1900
    //Sekunden seit dem 1. Januar 1900 (3812811794) - Sekunden der vollen Tage (3812745600) = 66194 Sekunden am aktuellen Tag vergangen
    //Sekunden des aktuellen Tages (66194) / Sekunden pro Stunde (3600) = 18 Stunden am aktuellen Tag vergangen => Aktuelle Uhrzeit 18:mm:ss

    //Berechnung der aktuellen Stunde
    utcaktstunde = ((newsecsSince1900  % secondperday) / secondperhour);

    //Berechnung der aktuellen Minute
    utcaktminute = (newsecsSince1900  % secondperhour) / secondperminute;

    //Berechnung der aktuellen Sekunde
    utcaktsekunde = (newsecsSince1900 % secondperminute);

    //Ende: Aktuelle Uhrzeit hh:mm:ss aus dem NTP-Paket ermitteln
       
  }

  //Ende: Datum und Uhrzeit ermitteln



  //Start: Uhrzeit über den millis()-Timer berechnen

  //Alle 1000 ms die Sekunden hochzählen
  if (millis() - lastmillisSecond > millisecondpersecond){

    //Zeitdifferenz
    lastmillisdifferenz = lastmillisdifferenz + ((millis() - lastmillisSecond) - millisecondpersecond);

    if(lastmillisdifferenz > millisecondpersecond){
      lastmillisdifferenz = 0;
      utcaktsekunde++;
    }
    
    //Systemzeit speichern für den nächsten Vergleich
    lastmillisSecond = millis();

    utcaktsekunde++;

  }

  //Alle 60 s den Minutenzähler hochzählen
  if(utcaktsekunde >= secondperminute){

    //Sekunden zurücksetzen
    utcaktsekunde = 0;

    //Minuten hochzählen
    utcaktminute++;
    
  }

  //Alle 60 min den Stundenzähler hochzählen
  if(utcaktminute >= minuteperhour){

    //Minuten zurücksetzen
    utcaktminute = 0;

    //Stunden hochzählen
    utcaktstunde++;

    //Wechsel von 24 Uhr zu 1 Uhr
    if(utcaktstunde == 25){
      utcaktstunde = 1;
    }

  }

  //Ende: Uhrzeit über den millis()-Timer berechnen



  //Start: Von UTC nach MEZ

  //Sekunden und Minuten unverändert von UTC übernehmen
  mezaktminute = utcaktminute;
  mezaktsekunde = utcaktsekunde;
  
  //Umrechnung von UTC nach MEZ mittels Zeitverschiebung
  mezaktstunde = utcaktstunde + utcx;

  //Aktuelle Stundenzahl in MEZ als 24h Format speichern
  mezaktstunde24 = mezaktstunde;
  

  //Stundenzahl erhöhen
  //Takt mit vollen 5 Minuten
  if(WordClockType == HIGH){
    
    //Nach "Halb" Stundenzahl erhöhen => Beispiel: 16:25:00 => Fünf vor halb Fünf
    if(mezaktminute >= 25 && mezaktminute <= 59) {    
      mezaktstunde = mezaktstunde + 1;
    }  
  }

  //Stundenzahl erhöhen
  //Takt mit +2/-2 Minuten
  if(WordClockType == LOW){
    
    //Nach "Halb" Stundenzahl erhöhen => Beispiel: 16:25:00 => Fünf vor halb Fünf
    if(mezaktminute >= 23 && mezaktminute <= 59) {    
      mezaktstunde = mezaktstunde + 1;
    }   
  }

  
  //Beispiel: UTC             = 22:15:00
  //          MEZ (Winter +1) = 23:15:00 => 23:15:00 => 11:15:00 => viertel nach Elf
  //          MEZ (Sommer +2) = 24:15:00 => 00:15:00 => viertel nach Zwölf
  
  //Beispiel: UTC             = 23:15:00
  //          MEZ (Winter +1) = 24:15:00 => 00:15:00 => viertel nach Zwölf
  //          MEZ (Sommer +2) = 25:15:00 => 01:15:00 => viertel nach Eins

  //Problemstellung 1: Stundenzahlen über 23 abfangen
  if(mezaktstunde >= 24){
    mezaktstunde = mezaktstunde - 24;
  }
  

  //Beispiel: UTC             = 10:47:00
  //          MEZ (Winter +1) = 11:15:00 => 11:15:00 => viertel nach Elf
  //          MEZ (Sommer +2) = 12:15:00 => 00:15:00 => viertel nach Zwölf
  
  //Beispiel: UTC             = 11:47:00
  //          MEZ (Winter +1) = 12:15:00 => 00:15:00 => viertel nach Zwölf
  //          MEZ (Sommer +2) = 13:15:00 => 01:15:00 => viertel nach Eins

  //Problemstellung 2: Stundenzahlen auf den Bereich "Eins" bis "Zwölf" begrenzen
  if(mezaktstunde >= 12){
    mezaktstunde = mezaktstunde - 12;
  }

  //Ende: Von UTC nach MEZ

  

  //Start: Stundenzahl ausgeben
  
  if(mezaktstunde != oldstunde) {

    //Stundenzahl für den nächsten Vergleich speichern
    oldstunde = mezaktstunde;

    //Adressbereich auf Null setzen
    ResetArray(NumberOfHours, HourAddr, newletter);

    //Anzeige der Stunden
    WriteArray(mezaktstunde, HourAddr, newletter, 1);

  }

  //Ende: Stundenzahl ausgeben


  
  //Start: Minutenzahl ausgeben
  
  if(mezaktminute != oldminute) {
    
    //Minutenzahl für den nächsten Vergleich speichern
    oldminute = mezaktminute;

    //Adressbereich auf Null setzen
    ResetArray(NumberOfMinute, MinuteAddr, newletter);

    //Adressbereich auf Null setzen
    ResetArray(NumberOfSpecialChar, ToPastAddr, newletter);

    //Adressbereich auf Null setzen
    ResetArray(NumberOfSpecialChar, HalfFullHourAddr, newletter);
    

    //WordClock mit zusätzlicher Minutenanzeige
    if(WordClockType == HIGH){
      
      //Pointer hat einen Wert zwischen 0-4
      pointer = mezaktminute % 5;
      
      //Adressbereich auf Null setzen
      if(pointer == 0){
        ResetArray(NumberOfAddMinutes, AddMinutesAddr, newletter);             
      }
      //Minutenanzeige +1 +2 +3 +4
      else{
        for(i = 0; i < pointer; i++){
          WriteArray(i, AddMinutesAddr, newletter, 1);
        }      
      }
    }
    

    //Pointer zurücksetzen
    pointer = 99;

    //TimeTable[60][4]
    //{0,0,0,0} = Minutenzahl, Pointer_MinuteAddr, Pointer_ToPastAddr, Pointer_HalfFullHourAddr
    //Wert '0' bedeutet, dass das Array übersprungen wird und die Adresse nicht angezeigt wird

    //Anzeige der Minuten
    for(i = 0; i <= 59; i++){

      //Aktuelle Minutenzahl mit der Zeittabelle vergleichen
      if(mezaktminute == TimeTable[i][0]){
        
        //MinuteAddr
        if(TimeTable[i][1] != 0){
          pointer = TimeTable[i][1] - 1;
          WriteArray(pointer, MinuteAddr, newletter, 1);
        }

        //ToPastAddr
        if(TimeTable[i][2] != 0){
          pointer = TimeTable[i][2] - 1;
          WriteArray(pointer, ToPastAddr, newletter, 1);
        }

        //HalfFullHourAddr
        if(TimeTable[i][3] != 0){
          pointer = TimeTable[i][3] - 1;
          WriteArray(pointer, HalfFullHourAddr, newletter, 1);
        }
        
        break;
        
      }  
    }

    //Beispiel: ES IST FÜNF VOR EINS
    if(mezaktstunde == 1){
      newletter[HourAddr[1][0]] = 1;
    }
    
    //Sonderfall: ES IST EIN(S) UHR
    if(mezaktstunde == 1 && newletter[HalfFullHourAddr[1][0]] == 1 && newletter[HalfFullHourAddr[1][1]] == 1){
      newletter[HourAddr[1][0]] = 0;
    }

  }

  //Ende: Minutenzahl ausgeben
  


  //Start: Altes Bild mit neuem Bild vergleichen

  for(i = ersteLED; i <= letzteLED; i++){
    
    //showletter = 1; newletter = 0 => Wort ausschalten
    if(showletter[i] > newletter[i]){
      newdisplay = HIGH;
      changeletter[i] = 0;
    }
    
    //showletter = 0; newletter = 1 => Wort einschalten
    if(showletter[i] < newletter[i]){
      newdisplay = HIGH;
      changeletter[i] = 1;
    }
    
    if(showletter[i] == newletter[i]){
      changeletter[i] = 2;
    }
  }

  //Ende: Altes Bild mit neuem Bild vergleichen
  
  

  //Start: LED-Display in einem definierten Zeitfenster ausschalten
  
  if(Parameter.EnableNightMode == 1){

    //Beispiel: Display On: 5 Uhr; Display Off: 2 Uhr
    if(Parameter.TimeDisplayOff < Parameter.TimeDisplayOn){
      if(mezaktstunde24 >= Parameter.TimeDisplayOff && mezaktstunde24 < Parameter.TimeDisplayOn){ 
        
        for(i = ersteLED; i <= letzteLED; i++){

          //Angezeigte Wörter im ersten Aufruf ausschalten
          if(setDisplayOff == LOW){
            if(showletter[i] == 1 || newletter[i] == 1){
              changeletter[i] = 0;
              newdisplay = HIGH;
            }
          }

          //Display im Zeitfenster ausgeschaltet lassen
          else{
            changeletter[i] = 2;
            showletter[i] = 0;
            newdisplay = LOW;
          }        
        }
      }
      
      else{
        setDisplayOff = LOW;           
      }
    }
    
    //Beispiel: Display On: 6 Uhr; Display Off: 23 Uhr
    else{
      if(mezaktstunde24 >= Parameter.TimeDisplayOff || mezaktstunde24 < Parameter.TimeDisplayOn){
        
        for(i = ersteLED; i <= letzteLED; i++){
          
          //Angezeigte Wörter im ersten Aufruf ausschalten
          if(setDisplayOff == LOW){            
            if(showletter[i] == 1 || newletter[i] == 1){
              changeletter[i] = 0;
              newdisplay = HIGH;
            }
          }
          
          //Display im Zeitfenster ausgeschaltet lassen
          else{
            changeletter[i] = 2;
            showletter[i] = 0;
            newdisplay = LOW;
          }        
        }
      }
      
      else{
        setDisplayOff = LOW;           
      }      
    }  
  }

  else{
    setDisplayOff = LOW;           
  }

  //Ende: LED-Display in einem definierten Zeitfenster ausschalten



  //Start: Ausgabe des Bildes

  if(newdisplay == HIGH) {

    //Farbwerte in dem eingestellten Intervall dimmen
    if (millis() - lastmillisFade > IntervallFade) {

      //Systemzeit speichern für den nächsten Vergleich
      lastmillisFade = millis();

      //Farbwerte reduzieren "herunterdimmen"     
      if(newredminus > 0.0){
        newredminus = newredminus - fadevaluered;
        if(newredminus <= 0.0){
          newredminus = 0.0;
          newgreenminus = 0.0;
          newblueminus = 0.0;
        }
      }

      //Farbwerte reduzieren "herunterdimmen"   
      if(newgreenminus > 0.0){
        newgreenminus = newgreenminus - fadevaluegreen;
        if(newgreenminus <= 0.0){
          newredminus = 0.0;
          newgreenminus = 0.0;
          newblueminus = 0.0;
        }
      }

      //Farbwerte reduzieren "herunterdimmen"   
      if(newblueminus > 0.0){
        newblueminus = newblueminus - fadevalueblue;
        if(newblueminus <= 0.0){
          newredminus = 0.0;
          newgreenminus = 0.0;
          newblueminus = 0.0;
        }
      }

      //Farbwerte erhöhen "hochdimmen"     
      if(newredplus < Parameter.RGBValueRed){
        newredplus = newredplus + fadevaluered;
        if(newredplus >= Parameter.RGBValueRed){
          newredplus = Parameter.RGBValueRed;
          newgreenplus = Parameter.RGBValueGreen; 
          newblueplus = Parameter.RGBValueBlue;                
        }
      }

      //Farbwerte erhöhen "hochdimmen"
      if(newgreenplus < Parameter.RGBValueGreen){
        newgreenplus = newgreenplus + fadevaluegreen;
        if(newgreenplus >= Parameter.RGBValueGreen){
          newredplus = Parameter.RGBValueRed;
          newgreenplus = Parameter.RGBValueGreen; 
          newblueplus = Parameter.RGBValueBlue;                   
        }
      }

      //Farbwerte erhöhen "hochdimmen"
      if(newblueplus < Parameter.RGBValueBlue){
        newblueplus = newblueplus + fadevalueblue;
        if(newblueplus >= Parameter.RGBValueBlue){ 
          newredplus = Parameter.RGBValueRed;
          newgreenplus = Parameter.RGBValueGreen; 
          newblueplus = Parameter.RGBValueBlue;                 
        }
      }

      //Neue Farbwerte an die LED's übermitteln
      for(i = ersteLED; i <= letzteLED; i++){

        red = newredminus;
        green = newgreenminus;
        blue = newblueminus;

        if(changeletter[i] == 0){
          pixels.setPixelColor(i, pixels.Color(red,green,blue)); 
        }

        red = newredplus;
        green = newgreenplus;
        blue = newblueplus;
        
        if(changeletter[i] == 1){
          pixels.setPixelColor(i, pixels.Color(red,green,blue));
        } 
      }

      //LED's aktualisieren
      pixels.show();

    }

    
    //Bild wurde komplett aktualisiert
    if(newredminus <= 0.0 && newgreenminus <= 0.0 && newblueminus <= 0.0 && newredplus >= Parameter.RGBValueRed && newgreenplus >= Parameter.RGBValueGreen && newblueplus >= Parameter.RGBValueBlue){
      
      //Arrays kopieren für den nächsten Vergleich
      for(i = ersteLED; i <= letzteLED; i++) {
        showletter[i] = newletter[i];  
      }
      

      //Start: Aktuelle Stromaufnahme ermitteln
      
      currentPerLed = 0;
      
      //Aktuelle Stromfaufnahme mittels RGB-Wert und Anzahl der aktiven LED's ermitteln
      //Die Stromaufnahme wird dabei nicht gemessen, sondern mit einem Faktor und der Anzahl der aktiven LED's berechnet
      for(i = ersteLED; i <= letzteLED; i++){
        
        if(showletter[i] == 1){
          currentPerLed = currentPerLed + round((float)(currentPerRgbValue * (red + green + blue)));
        }
        
      }
      
      //Ende: Aktuelle Stromaufnahme ermitteln


      //Aktualisierung des Bildes beenden
      newdisplay = LOW;
      setDisplayOff = HIGH;


      //Farbwerte für die nächste Aktualisierung zurücksetzen    
      newredminus = Parameter.RGBValueRed;
      newgreenminus = Parameter.RGBValueGreen;
      newblueminus = Parameter.RGBValueBlue;
      newredplus = 0.0;
      newgreenplus = 0.0;
      newblueplus = 0.0;
    }
      
  }

  //Ende: Ausgabe des Bildes


  
  //Start: Ausgabe fürs Debuging
  
  if(debug == HIGH && newdisplay == LOW) {

    if (millis() - lastmillisDebug > IntervallDebug) {
      
      //Systemzeit speichern für den nächsten Vergleich
      lastmillisDebug = millis();
      
      Serial.print("SSID: ");
      Serial.println(Parameter.Ssid);
      
      Serial.print("Passwort: ");
      Serial.println(Parameter.Password);

      Serial.print("IP-Adresse: ");
      Serial.println(WiFi.localIP());

      Serial.print("WLAN Daten gespeichert: ");
      Serial.println(Parameter.LoginSaved);

      Serial.print("Parameter gespeichert: ");
      Serial.println(Parameter.ConfigSaved);

      Serial.print("NTP-Paket: ");
      Serial.println(newsecsSince1900);

      Serial.print("Zeitdifferenz: ");
      Serial.print(lastmillisdifferenz);
      Serial.println("ms");

      Serial.print("Uhrzeit UTC: ");
      Serial.print(utcaktstunde);
      Serial.print(":");
      Serial.print(utcaktminute);
      Serial.print(":");
      Serial.println(utcaktsekunde);

      Serial.print("Zeitverschiebung: +");
      Serial.println(utcx);
 
      Serial.print("Uhrzeit MEZ: ");
      Serial.print(mezaktstunde24);
      Serial.print(":");
      Serial.print(mezaktminute);
      Serial.print(":");
      Serial.println(mezaktsekunde);

      Serial.print("Datum: ");
      Serial.print(datumTag);
      Serial.print(".");
      Serial.print(datumMonat);
      Serial.print(".");
      Serial.println(datumJahr);

      Serial.print("Wochentag: ");
      Serial.println(wochenTag);

      Serial.print("Schaltjahr: ");
      Serial.println(schaltJahr);

      Serial.print("Anzahl Schalttage: ");
      Serial.println(newschaltTage);

      Serial.print("Nachtmodus aktiv: ");
      Serial.println(Parameter.EnableNightMode);

      Serial.print("Display ausschalten um: ");
      Serial.print(Parameter.TimeDisplayOff);
      Serial.println(" Uhr");

      Serial.print("Display einschalten um: ");
      Serial.print(Parameter.TimeDisplayOn);
      Serial.println(" Uhr");     

      Serial.print("RGB-Farbwert Rot: ");
      Serial.print(Parameter.RGBValueRed);
      Serial.println("/255");

      Serial.print("RGB-Farbwert Grün: ");
      Serial.print(Parameter.RGBValueGreen);
      Serial.println("/255");

      Serial.print("RGB-Farbwert Blau: ");
      Serial.print(Parameter.RGBValueBlue);
      Serial.println("/255");

      Serial.print("Faktor Dimmen Rot: ");
      Serial.println(fadevaluered);

      Serial.print("Faktor Dimmen Grün: ");
      Serial.println(fadevaluegreen);

      Serial.print("Faktor Dimmen Blau: ");
      Serial.println(fadevalueblue);

      Serial.print("Stromaufnahme: ");
      Serial.print(currentPerLed);
      Serial.println("mA");

      Serial.println();
      
    }  
  }

  //Ende: Ausgabe fürs Debuging
  
  
}


//****************************** Eigene Funktionen ******************************


//Daten aus dem EEPROM lesen
void LoadParameter(){
  EEPROM.begin(sizeof(Parameter));
  EEPROM.get(0,Parameter);
  EEPROM.end();
}



//Daten im EEPROM speichern
bool SaveParameter(){
  bool RetVal = LOW;
  EEPROM.begin(sizeof(Parameter));
  EEPROM.put(0,Parameter);
  RetVal = EEPROM.commit();
  EEPROM.end();
  return RetVal;
}



//Daten im EEPROm löschen
void ClearParameter(){
  int count = 0;
  EEPROM.begin(sizeof(Parameter));
  for(count = 0; count < sizeof(Parameter); count++) {
    EEPROM.write(count, 0);
  }
  EEPROM.end();
}



//Adressbereich zurücksetzen
void ResetArray(int ArraySize, const int Addr[][2], unsigned int *Array){

  int c1 = 0;
  int c2 = 0;
  
  //Array durchlaufen
  for(c1 = 0; c1 <= ArraySize - 1; c1++) {

    //Bereich auf Null setzen
    for(c2 = Addr[c1][0]; c2 <= Addr[c1][1]; c2++) {
      Array[c2] = 0;
    }
  }
}



//Adressbereich beschreiben
void WriteArray(int Pointer, const int Addr[][2], unsigned int *Array, int Value){

  int c1 = 0;

  //Array-Bereich beschreiben
  for(c1 = Addr[Pointer][0]; c1 <= Addr[Pointer][1]; c1++) {
    Array[c1] = Value;
  }
}



//Webserver
void HandlerWebpage() {
  Webserver.send(HTTPRequest, "text/html", LoginHTML);
}



//Webserver
void HandlerNotFound(){
  Webserver.send(HTTPNotFound, "text/html", NotFoundHTML);
}



//Webserver 
void HandlerSave() {

  //Eingaben vom Webserver übernehmen
  Parameter.TimeDisplayOff = GetArgumentToInt("displayoff");
  Parameter.TimeDisplayOn = GetArgumentToInt("displayon");
  GetArgument("ssid").toCharArray(Parameter.Ssid, 40);
  GetArgument("password").toCharArray(Parameter.Password, 40);

  //Nachtmodus
  if(GetArgument("enablenightmode") == "on"){
    Parameter.EnableNightMode = 1;
  }
  else{
    Parameter.EnableNightMode = 0;
  }

  //Neue Parameter im EEPROM
  Parameter.LoginSaved = 1;

  //Parameter im EEPROM speichern
  SaveParameter();

  //Neue Seite anzeigen
  Webserver.send(HTTPPost, "text/html", SavedHTML);
  
}



//Webserver 
void HandlerRefresh() {

  //HTML übernehmen
  String RefreshHTML = LoginHTML;
  
  //Eingaben vom Webserver übernehmen
  Parameter.RGBValueRed = GetArgumentToInt("rgbred");
  Parameter.RGBValueGreen = GetArgumentToInt("rgbgreen");
  Parameter.RGBValueBlue = GetArgumentToInt("rgbblue");

  //Farbwerte berechnen
  newredminus = Parameter.RGBValueRed;
  newgreenminus = Parameter.RGBValueGreen;
  newblueminus = Parameter.RGBValueBlue;
  fadevaluered = FadeValueRed(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
  fadevaluegreen = FadeValueGreen(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
  fadevalueblue = FadeValueBlue(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
  
  //Farbwerte um x Nachkommastellen verschieben 
  for(int count = 0; count <= 10; count++) {
    if(fadevaluered < 1.0 && fadevaluegreen < 1.0 && fadevalueblue < 1.0){
      break;
    }
    
    fadevaluered = fadevaluered / 10.0;
    fadevaluegreen = fadevaluegreen / 10.0;
    fadevalueblue = fadevalueblue / 10.0;

  }

  //Neue Farbwerte zuweisen
  for(i = ersteLED; i <= letzteLED; i++) {
    if(showletter[i] == 1){
      pixels.setPixelColor(i, pixels.Color(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue));
    }  
  }

  //LED's aktualisieren
  pixels.show();
  
  //HTML aktualisieren
  RefreshHTML.replace("%%RED%%", String(Parameter.RGBValueRed, DEC));
  RefreshHTML.replace("%%GREEN%%", String(Parameter.RGBValueGreen, DEC));
  RefreshHTML.replace("%%BLUE%%", String(Parameter.RGBValueBlue, DEC));

  //Neue Seite anzeigen
  Webserver.send(HTTPPost, "text/html", RefreshHTML);
  
}



//Webserver Argument auslesen (toINT)
int GetArgumentToInt(String ID){
  
  int Value = 0;
  
  if(Webserver.hasArg(ID)){
    Value = Webserver.arg(ID).toInt();
  }

  return Value;
}



//Webserver Argument auslesen
String GetArgument(String ID){
  
  String Value = "0";
  
  if(Webserver.hasArg(ID)){
    Value = Webserver.arg(ID);
  }

  return Value;
  
}



//Funktion UDP-Daten empfangen
void UdpDataReceive(){ 
  unsigned int PaketSize = 0;  
  PaketSize = UDP.parsePacket();
  if(PaketSize != 0){
    if(PaketSize == NtpPaketSize){
      UDP.read(NtpPaketBuffer, NtpPaketSize);
      newsecsSince1900 = GetNtpTimestamp(NtpPaketBuffer);
    }
    else if(PaketSize == UserInterfacePaketSize){
      UDP.read(UserInterfacePaketBuffer, UserInterfacePaketSize);
      UserInterface(UserInterfacePaketBuffer);
    }
    else{
      //Nothing to do here
    }
  }  
}



//Funktion Nutzerdaten aus UDP-Daten auslesen
int UserInterfaceGetValue(byte Data[], int PointerStart, int PointerEnd){
  int Value = 0;
  Value = Data[PointerStart];
  Value |= Data[PointerEnd] << 8;
  return Value;
}



//Funktion Handshake
bool HandshakeOk(byte Data[]){
  bool HandshakeOk = HIGH;
  int HandshakeOkCount = 0;
  for(HandshakeOkCount = 2; HandshakeOkCount < 12; HandshakeOkCount++){
    if(Data[HandshakeOkCount] != 0){
      HandshakeOk = LOW;
    }
  }
  return HandshakeOk;
}



//Funktion Nutzerdaten übergeben
void UserInterface(byte UserInterfaceData[]) {

  //ID-Nummer
  UdpId = UserInterfaceGetValue(UserInterfaceData,0,1);
  
  switch(UdpId){
    
    case UdpIdHandshakeRequest:
      if(HandshakeOk(UserInterfaceData)){
        UserInterfaceData[0] = UdpIdHandshakeAnswer;
        UserInterfaceData[1] = 0;
        UserInterfaceData[2] = byte(Parameter.RGBValueRed);
        UserInterfaceData[3] = byte(Parameter.RGBValueRed >> 8);
        UserInterfaceData[4] = byte(Parameter.RGBValueGreen);
        UserInterfaceData[5] = byte(Parameter.RGBValueGreen >> 8);
        UserInterfaceData[6] = byte(Parameter.RGBValueBlue);
        UserInterfaceData[7] = byte(Parameter.RGBValueBlue >> 8);
        UserInterfaceData[8] = Parameter.EnableNightMode;
        UserInterfaceData[9] = Parameter.TimeDisplayOff;
        UserInterfaceData[10] = Parameter.TimeDisplayOn;
        UserInterfaceData[11] = 0;
        UserInterfaceIP = UDP.remoteIP();
        UDP.beginPacket(UserInterfaceIP, localPort);
        UDP.write(UserInterfaceData, 12);
        UDP.endPacket();
      }
    break;
    
    case UdpIdRgb:
      Parameter.RGBValueRed = UserInterfaceGetValue(UserInterfaceData,2,3);
      Parameter.RGBValueGreen = UserInterfaceGetValue(UserInterfaceData,4,5);
      Parameter.RGBValueBlue = UserInterfaceGetValue(UserInterfaceData,6,7);
      
      //Farbwerte berechnen
      newredminus = Parameter.RGBValueRed;
      newgreenminus = Parameter.RGBValueGreen;
      newblueminus = Parameter.RGBValueBlue;
      fadevaluered = FadeValueRed(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
      fadevaluegreen = FadeValueGreen(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);
      fadevalueblue = FadeValueBlue(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue);

      //Parameter im EEPROM speichern
      SaveParameter();

      //Farbwerte um x Nachkommastellen verschieben 
      for(i = 0; i <= 10; i++) {

        if(fadevaluered < 1.0 && fadevaluegreen < 1.0 && fadevalueblue < 1.0){
          break;
        }

        fadevaluered = fadevaluered / 10.0;
        fadevaluegreen = fadevaluegreen / 10.0;
        fadevalueblue = fadevalueblue / 10.0;

      }

      //Neue Farbwerte zuweisen
      for(i = ersteLED; i <= letzteLED; i++) {
        if(showletter[i] == 1){
          pixels.setPixelColor(i, pixels.Color(Parameter.RGBValueRed,Parameter.RGBValueGreen,Parameter.RGBValueBlue));
        }  
      }

      //LED's aktualisieren
      pixels.show();
    
    break;
    
    case UdpIdTime:
      UdpHour = UserInterfaceGetValue(UserInterfaceData,2,3);
      UdpMinute = UserInterfaceGetValue(UserInterfaceData,4,5);
      UdpSecond = UserInterfaceGetValue(UserInterfaceData,6,7);
      if(NoWifi == HIGH){
        utcaktstunde = UdpHour;
        utcaktminute = UdpMinute;
        utcaktsekunde = UdpSecond;
        utcx = 0;
      } 
    break;
    
    case UdpIdDate:
      UdpDay = UserInterfaceGetValue(UserInterfaceData,2,3);
      UdpMonth = UserInterfaceGetValue(UserInterfaceData,4,5);
      UdpYear = UserInterfaceGetValue(UserInterfaceData,6,7);
    break;

    case UdpIdDisplay: 
      Parameter.EnableNightMode = UserInterfaceGetValue(UserInterfaceData,2,3);
      Parameter.TimeDisplayOn = UserInterfaceGetValue(UserInterfaceData,4,5);
      Parameter.TimeDisplayOff = UserInterfaceGetValue(UserInterfaceData,6,7);
      setDisplayOff = LOW;
      SaveParameter();
    break;
  }
  
}



//Funktion Dimmwerte Rot berechnen
float FadeValueRed(int Red, int Green, int Blue){
  float Value = 1.0;
  
  if(Red == 0){
    Value = 0.0;
    return Value;
  }

  //Ab hier hat Rot einen Wert

  //Drei Werte vorhanden
  if(Green > 0 && Blue > 0){
    if(Green <= Red && Green <= Blue){
      Value = (float)Red / Green;
    }
    if(Blue <= Red && Blue <= Green){
      Value = (float)Red / Blue;
    }
    return Value; 
  }

  //Ab hier haben entweder Grün oder Blau eine Null oder Beide

  //Rot und Grün mit Werten; Blau muss Null sein
  if(Green > 0 && Green < Red){
    Value = (float)Red / Green;
    return Value; 
  }

  //Ab hier hat Grün eine Null und Blau fraglich

  //Rot und Blau
  if(Blue > 0 && Blue < Red){
    Value = (float)Red / Blue;
    return Value; 
  }

  //Ab hier nur Rot vorhanden oder Rot ist kleinster Wert
  return Value; 
}



//Funktion Dimmwerte Grün berechnen
float FadeValueGreen(int Red, int Green, int Blue){
  float Value = 1.0;
  
  if(Green == 0){
    Value = 0.0;
    return Value;
  }

  //Ab hier hat Grün einen Wert

  //Drei Werte vorhanden
  if(Red > 0 && Blue > 0){
    if(Red <= Green && Red <= Blue){
      Value = (float)Green / Red;
    }
    if(Blue <= Green && Blue <= Red){
      Value = (float)Green / Blue;
    }
    return Value; 
  }

  //Ab hier haben entweder Rot oder Blau eine Null oder Beide

  //Grün und Rot mit Werten; Blau muss Null sein
  if(Red > 0 && Red < Green){
    Value = (float)Green / Red;
    return Value; 
  }

  //Ab hier hat Rot eine Null und Blau fraglich

  //Grün und Blau
  if(Blue > 0 && Blue < Green){
    Value = (float)Green / Blue;
    return Value; 
  }

  //Ab hier nur Grün vorhanden oder Grün ist kleinster Wert
  return Value;  
}



//Funktion Dimmwerte Blau berechnen
float FadeValueBlue(int Red, int Green, int Blue){
  float Value = 1.0;
  
  if(Blue == 0){
    Value = 0.0;
    return Value;
  }

  //Ab hier hat Blau einen Wert

  //Drei Werte vorhanden
  if(Red > 0 && Green > 0){
    if(Red <= Blue && Red <= Green){
      Value = (float)Blue / Red;
    }
    if(Green <= Blue && Green <= Red){
      Value = (float)Blue / Green;
    }
    return Value; 
  }

  //Ab hier haben entweder Rot oder Grün eine Null oder Beide

  //Blau und Rot mit Werten; Grün muss Null sein
  if(Red > 0 && Red < Blue){
    Value = (float)Blue / Red;
    return Value; 
  }

  //Ab hier hat Rot eine Null und Grün fraglich

  //Blau und Grün
  if(Green > 0 && Green < Blue){
    Value = (float)Blue / Green;
    return Value; 
  }
 
  //Ab hier nur Blau vorhanden oder Blau ist kleinster Wert
  return Value;
}



//Funktion NTP-Daten abrufen
void SendNtpRequest() {

  //Verbindung zu einem NTP-Server aus dem Pool aufbauen
  WiFi.hostByName(NtpServerName, TimeServerIP);

  //NTP-Anfrage an den Server stellen
  SendNtpPaket(TimeServerIP);

  delay(50);
  
}



//Funktion NTP-Zeitstempel auslesen
long GetNtpTimestamp(byte NtpData[]){
  
  unsigned long NtpTimestamp = 0;
  
  //Der Zeitstempel befindet sich im Byte 40, 41, 42, 43
  //Zeitstempel aus den vier Bytes auslesen => Ergebnis: Sekunden seit dem 1. Januar 1900, 00:00:00 Uhr 
  NtpTimestamp = word(NtpData[40], NtpData[41]) << 16 | word(NtpData[42], NtpData[43]);
  return NtpTimestamp;
}



//Funktion zur Anfrage eines NTP-Paketes
void SendNtpPaket(IPAddress& address) {
  memset(NtpPaketBuffer, 0, NtpPaketSize);
  
  //Erstes Byte
  //Leap Indicator: Bit 0, 1 => 11 => 3 => Schaltsekunde wird nicht synchronisiert
  //Version: Bit Bit 2, 3, 4 => 000 => 0 => Version xy?
  //Mode: Bit 5, 6, 7 => 111 => 7 => für den Privaten Gebrauch
  NtpPaketBuffer[0] = 0b11100011;
  
  //Zweites Byte
  //Stratum: Bit 0 - 7 => 00000000 => 0 => nicht angegeben
  NtpPaketBuffer[1] = 0;

  //Drittes Byte
  //Poll: Bit 0 - 7 => 00000110 => 6 => Abfrageintervall
  NtpPaketBuffer[2] = 6;

  //Viertes Byte
  //Precision: Bit 0 - 7 => 0xEC => Taktgenauigkeit
  NtpPaketBuffer[3] = 0xEC;

  //Die nächsten 8 Byte haben nur für Servernaachrichten eine Bedeutung
  //8 Bytes => 0

  NtpPaketBuffer[12]  = 49;
  NtpPaketBuffer[13]  = 0x4E;
  NtpPaketBuffer[14]  = 49;
  NtpPaketBuffer[15]  = 52;

  UDP.beginPacket(address, 123);
  UDP.write(NtpPaketBuffer, NtpPaketSize);
  UDP.endPacket();
}



//Funktion zur Berechnung des Wochentages
//Montag = 1, Dienstag = 2, Mittwoch = 3, Donnerstag = 4, Freitag = 5, Samstag = 6, Sonntag = 7
unsigned int wochenTag_calc(unsigned long tage1900) {

  //Der 1. Januar 1900 war ein Montag
  unsigned int ergebnis = 1;
  
  int for_i = 0;

  for(for_i = 0; for_i < tage1900; for_i++) {
    
    if(ergebnis < 7) {
      ergebnis = ergebnis + 1;
    }
    
    else {
      ergebnis = 1;
    }
  }

  return ergebnis;
}



//Funktion zur Berechnung der Jahreszahl
unsigned int datumJahr_calc(unsigned long sec1900) {

  //NTP beginnt am 1. Januar 1900
  unsigned int ergebnis = 1900;
  unsigned int tageimjahr = 0;
  unsigned int tage = 0;
  unsigned int tage1900 = 0;

  int for_i = 0;
  bool schaltjahr = LOW;

  tage1900 = sec1900 / 86400;

  for(for_i = 0; for_i < tage1900; for_i++){

    schaltjahr = schaltJahrJN(ergebnis);

    if(schaltjahr){
      tageimjahr = 366;
    }
    
    else{
      tageimjahr = 365;
    }

    tage++;

    if(tage >= tageimjahr){
      ergebnis++;
      tage = 0;
    }
    
  }

  return ergebnis;
  
}



//Funktion zur Erkennung ob ein Schaltjahr vorliegt
bool schaltJahrJN(unsigned int jahr) {

  bool ergebnis_jn = LOW;
  
  //Erkennung von Schaltjahren
  if((jahr % 4) == 0) {
    
    ergebnis_jn = HIGH;

    if((jahr % 100) == 0) {
      
      ergebnis_jn = LOW;

      if((jahr % 400) == 0) {
        
        ergebnis_jn = HIGH;
      }
    }
  }

  else {
    ergebnis_jn = LOW;
  }

  return ergebnis_jn;
}



//Funktion zur Berechnung des Monates
int welcherMonat(int tg_nr, bool schaltjn) {

  //Monatsanfänge
  int monatMin[13] = {0, 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
  //Monatsenden
  int monatMax[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};

  int datum = 0;

  int y = 0;

  //Berechnung des Anfang und Ende jeden Montas im Schaltjahr
  if(schaltjn == HIGH) {

    for(y = 3; y < 13; y++) {
      monatMin[y] = monatMin[y] + 1;
    }

    for(y = 2; y < 13; y++) {
      monatMax[y] = monatMax[y] + 1;
    }
  }

  //Monat Januar
  if(tg_nr >= monatMin[1] && tg_nr <= monatMax[1]) {
    datum = 1;
  }

  //Monat Februar
  if(tg_nr >= monatMin[2] && tg_nr <= monatMax[2]) {
    datum = 2;
  }

  //Monat März
  if(tg_nr >= monatMin[3] && tg_nr <= monatMax[3]) {
    datum = 3;
  }

  //Monat April
  if(tg_nr >= monatMin[4] && tg_nr <= monatMax[4]) {
    datum = 4;
  }

  //Monat Mai
  if(tg_nr >= monatMin[5] && tg_nr <= monatMax[5]) {
    datum = 5;
  }

  //Monat Juni
  if(tg_nr >= monatMin[6] && tg_nr <= monatMax[6]) {
    datum = 6;
  }

  //Monat Juli
  if(tg_nr >= monatMin[7] && tg_nr <= monatMax[7]) {
    datum = 7;
  }

  //Monat August
  if(tg_nr >= monatMin[8] && tg_nr <= monatMax[8]) {
    datum = 8;
  }

  //Monat September
  if(tg_nr >= monatMin[9] && tg_nr <= monatMax[9]) {
    datum = 9;
  }

  //Monat Oktober
  if(tg_nr >= monatMin[10] && tg_nr <= monatMax[10]) {
    datum = 10;
  }

  //Monat November
  if(tg_nr >= monatMin[11] && tg_nr <= monatMax[11]) {
    datum = 11;
  }

  //Monat Dezember
  if(tg_nr >= monatMin[12] && tg_nr <= monatMax[12]) {
    datum = 12;
  }

  return datum;
}

ohne dem Buchstabenlayout wird das wohl keiner ändern können.

Außerdem wenn ich manuelle NTP Aufrufe auf einem ESP sehe, dann wäre das für mich sowieso schon ein NOGO.

NTP ist Teil des ESP Cores. Nutze die ESP Core Funktionalität.

Meine Seite dazu:
NTP for the ESP8266 including day light saving Time (DST) without 3rd party library (rothschopf.net)

Wenn "viertel nach" angezeigt werden so zeigst Du "viertel" und Stunde +1 an. (weiß jetzt nicht ob das bei allen Stunden stimmt oder es Sonderfälle gibt.
Bei "viertel vor" zeist Du "dreivieltel" an.

Die Tabelle der ersten und letzten LED mußt Du an das neue Wort anpassen.

Grüße Uwe

Sorry, gerade umgekehrt:
Dreiviertel + (Stunde +1)
Viertel nach + Stunde

[OT]
einen etwas anderen Ansatz der Anzeige verfolgt dieser hier
[/OT]

Bitte lies das Zitat welches ich im Post gemacht habe.
Was ich oder Du zuhause sagen gilt hier nicht.

Grüße Uwe

Na ja, dann mal ausführlich:
11:45 = Stunde:Minute = dreiviertel zwölf = dreiviertel (11+1)

Grüße Uwe

11:15 = Stunde:Minute = Viertel zwölf = Viertel (Stunde+1)
Da hast Du Recht!

Ich bin nicht von der Zeit ausgegangen sondern von den angezeigten Wörtern; also alter Text zu neuer Text. Der Sketch macht ja schon die Konvertierung Zeit zu alten Text.
Grüße Uwe

1 Like

Nach "dreiviertel zwölf" kommt "zwölf" und danach "viertel eins".
"Viertel Dreizehn" hab ich noch nie gehört.
Wann der Zusatz "nachmittags" oder "abends" erforderlich ist, wäre interessant.

00:15 ist nach meinem Empfinden nur als "viertel eins nachts" verständlich. :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.