Update: 2024-02-25

Esp8266 Sonnenlauf als Arduino Tab.

Sonnenlauf.ino

// ****************************************************************
// Arduino IDE Tab Esp8266 Sonnen Auf/Untergang Modular
// created: Jens Fleischer, 2019-01-05
// last mod: Jens Fleischer, 2021-04-24
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.4.2 - 3.1.2
// Getestet auf: Nodemcu, Wemos D1 Mini Pro
/******************************************************************
  Copyright (c) 2018 Jens Fleischer. All rights reserved.

  This file is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  This file is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Sonnenlauf sollte als Tab eingebunden werden.
// Der Lokalzeit Tab (bzw. "struct tm") ist zum ausführen erforderlich.
// Gib beim Aufrufen der Funktion "twilight(Länge, Breite)"
// den Längen- und Breitengrad deines Ortes an.
// "twilight(12.348283, 51.345751);" https://www.laengengrad-breitengrad.de/
/**************************************************************************************/

struct SunTime {
  char sunrise[6];
  char sunset[6];
};

void twilight(const double longitude, const double latitude) {
  SunTime sun;
  localTime();                                                                   // Sollte die Lokalzeit schon im loop eingebunden sein, auskommentieren!
  static byte lastday, lastdst;
  if (tm.tm_mday != lastday || tm.tm_isdst != lastdst) {                         // Sonnenlauf für Tage mit Zeitumstellung zweimal Täglich berechnen.
    lastday = tm.tm_mday;
    lastdst = tm.tm_isdst;
    const double h = -0.833333333333333 * DEG_TO_RAD;
    const double w = latitude * DEG_TO_RAD;
    double JD = julianDate(1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday);
    double T = (JD - 2451545.0) / 36525.0;
    double DK;
    double EOT = calculateEOT(DK, T);
    double differenceTime = 12.0 * acos((sin(h) - sin(w) * sin(DK)) / (cos(w) * cos(DK))) / PI;
    strcpy(sun.sunrise, outputFormat((12.0 - differenceTime - EOT) - longitude / 15.0 + (_timezone* -1) / 3600 + tm.tm_isdst));
    strcpy(sun.sunset, outputFormat((12.0 + differenceTime - EOT) - longitude / 15.0 + (_timezone* -1) / 3600 + tm.tm_isdst));
    Serial.printf("Sonnenaufgang: %s\nSonnenuntergang: %s\n", sun.sunrise, sun.sunset);
    server.on("/sun", [=]() {
      char buf[18];
      snprintf(buf, sizeof(buf), "[\"%s\",\"%s\"]", sun.sunrise, sun.sunset);
      server.send(200, "application/json", buf);
    });
  }
}

double julianDate (int y, int m, int d) {        // Gregorianischer Kalender
  if (m <= 2) {
    m = m + 12;
    y = y - 1;
  }
  int gregorian = (y / 400) - (y / 100) + (y / 4); // Gregorianischer Kalender
  return 2400000.5 + 365.0 * y - 679004.0 + gregorian + (30.6001 * (m + 1)) + d + 12.0 / 24.0;
}

double InPi(double x) {
  int n = x / TWO_PI;
  x = x - n * TWO_PI;
  if (x < 0) x += TWO_PI;
  return x;
}

double calculateEOT(double &DK, double T) {
  double RAm = 18.71506921 + 2400.0513369 * T + (2.5862e-5 - 1.72e-9 * T) * T * T;
  double M  = InPi(TWO_PI * (0.993133 + 99.997361 * T));
  double L  = InPi(TWO_PI * (  0.7859453 + M / TWO_PI + (6893.0 * sin(M) + 72.0 * sin(2.0 * M) + 6191.2 * T) / 1296.0e3));
  double e = DEG_TO_RAD * (23.43929111 + (-46.8150 * T - 0.00059 * T * T + 0.001813 * T * T * T) / 3600.0);    // Neigung der Erdachse
  double RA = atan(tan(L) * cos(e));
  if (RA < 0.0) RA += PI;
  if (L > PI) RA += PI;
  RA = 24.0 * RA / TWO_PI;
  DK = asin(sin(e) * sin(L));
  RAm = 24.0 * InPi(TWO_PI * RAm / 24.0) / TWO_PI;
  double dRA = RAm - RA;
  if (dRA < -12.0) dRA += 24.0;
  if (dRA > 12.0) dRA -= 24.0;
  dRA = dRA * 1.0027379;
  return dRA ;
}

char* outputFormat(double sunTime) {
  if (sunTime < 0) sunTime += 24;
  else if (sunTime >= 24) sunTime -= 24;
  int8_t decimal = 60 * (sunTime - static_cast<int>(sunTime)) + 0.5;
  int8_t predecimal = sunTime;
  if (decimal >= 60) {
    decimal -= 60;
    predecimal++;
  }
  else if (decimal < 0) {
    decimal += 60;
    predecimal--;
    if (predecimal < 0) predecimal += 24;
  }
  static char buf[9];
  snprintf(buf, sizeof(buf), "%.2d:%.2d", predecimal, decimal);
  return buf;
}

Der Aufruf der Funktion "twilight(Länge, Breite)" ist nach ausführen von "setupTime();" möglich.
Die Berechnung des Sonnenaufgang und Sonnenuntergang durch "twilight(Länge, Breite)" wird nur einmal am Tag durchgeführt.
Die Funktion "localTime();" muss entweder im "void loop" oder am Anfang der Funktion "twilight()" aufgerufen werden.

Beispiel:

*************** Aufruf localTime() aus void loop() ******************

Bsp.: Melbourne Australien

void loop() {
 ......
  localTime();
  twilight(144.97, -37.75);  // geographische Länge und Breite
 ......
}

Im Lokalzeit Tab die korrekte POSIX Zeitzone für Melbourne Australien einfügen.

void setupTime() {
  .....
  setenv("TZ", "AEST-10AEDT,M10.1.0,M4.1.0/3", 1);
  ....
}

Es ist unbedingt erforderlich die richtige POSIX Zeitzone zu setzen da diese die Information zur möglichen Sommerzeit enthält.

Beispiel:

************* Aufruf localTime() aus void twilight() ****************

Bsp.: Leipzig Deutschland

void loop() {
 ......
  twilight(12.348283, 51.345751);  // geographische Länge und Breite
 ......
}

void twilight(const double longitude, const double latitude) {
  localTime();
  static byte lastday = 0;
  ......
}

Im Lokalzeit Tab die korrekte POSIX Zeitzone für Leipzig Deutschland einfügen.

void setupTime() {
  .....
  setenv("TZ", "CET-1CEST,M3.5.0/02,M10.5.0/03", 1);
  ....
}
suntime.html

<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Uhrzeit</title>
    <script>
	  function suntime() {
        fetch('/sun').then(response => {
          return response.json();
        }).then(array => {
		  document.querySelector('#sunrise').innerHTML = array[0];
          document.querySelector('#sunset').innerHTML = array[1];
        });
      }
      function renew() {
        fetch('/time').then(response => {
          return response.json();
        }).then(array => {
		  document.querySelector('#time').innerHTML = array[0];
          document.querySelector('#date').innerHTML = array[1];
        });
      }
      document.addEventListener('DOMContentLoaded', suntime(), renew(), setInterval(renew, 1000));
    </script>
    <style>
      body {
        padding: 10px;
        font-size: 3.2em;
      }
      main {
		display: flex;
        flex-direction: column;
        align-items: center;
        background-color: black;
        width: 280px;
        padding: .2em;
        border: .15em solid #aeaeab;
        box-shadow: 5px 10px 5px rgba(0, 0, 0, 0.7);
        border-radius: .2em;
      }
      span {
        color: #00ff05;
        position: relative;
        top: .3em;
        left: .1em;
        letter-spacing: .1em;
        font-weight: bold
      }
	  #date {
        font-size: .7em;
        margin-top: .5em;
      }
	  div {
        display: flex;
        justify-content: space-evenly;
		width: 100%;
	    font-size: .5em;
        margin-top: .7em;
	  }
    </style>
  </head>
  <body>
    <main>
      <span id="time"></span>
      <span id="date"></span>
	  <div>
	    <span id="sunrise"></span>
		&#9728;&#65039;
	    <span id="sunset"></span>
	  </div>
    </main>
  </body>
</html>