Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f762b97f6f | ||
|
|
c6c41e1f5d | ||
|
|
258f68185e | ||
|
|
e74580fa11 | ||
|
|
e4febb1035 | ||
|
|
a11285d332 | ||
|
|
f815962d7a | ||
|
|
7cc7a1249f | ||
|
|
dd38e291de | ||
|
|
f579d1e1c8 | ||
|
|
d6b6cc9a2b | ||
|
|
51f5ed1744 | ||
|
|
a4835b057a |
No files matched your search
@@ -35,9 +35,6 @@ Am Anfang den ESP anlöten:
|
|||||||
|
|
||||||
|
|
||||||
## Flashen
|
## Flashen
|
||||||
|
|
||||||
**Board und RTC wählen:** PlatformIO: Umgebung `<board>_<rtc>` mit Board `d1_mini` oder `nodemcu` und RTC `ds3231`, `ds1307` oder `keine` (z. B. `pio run -e nodemcu_ds3231 -t upload`). Arduino IDE: Board unter Werkzeuge wählen und oben in `arduino.ino` `RTC_MODUL_DS3231`, `RTC_MODUL_DS1307` oder `RTC_MODUL_KEINE` definieren (Standard: DS3231). Die RTC hängt an D2 (SDA) / D1 (SCL); der DS1307 braucht 5 V.
|
|
||||||
|
|
||||||
1. Dieses Repo clonen (`git clone https://github.com/c3e/wordclock.git`)
|
1. Dieses Repo clonen (`git clone https://github.com/c3e/wordclock.git`)
|
||||||
2. Die Arduino IDE installieren (https://www.arduino.cc/en/Main/Software)
|
2. Die Arduino IDE installieren (https://www.arduino.cc/en/Main/Software)
|
||||||
3. Die Datei `wordclock/wordclock.ino` in der Arduino IDE öffnen
|
3. Die Datei `wordclock/wordclock.ino` in der Arduino IDE öffnen
|
||||||
@@ -48,7 +45,6 @@ Am Anfang den ESP anlöten:
|
|||||||
8. Folgende installieren:
|
8. Folgende installieren:
|
||||||
* FastLED
|
* FastLED
|
||||||
* WiFiManager
|
* WiFiManager
|
||||||
* RTClib (Adafruit)
|
|
||||||
9. Folgende Zip-Dateien herunterladen:
|
9. Folgende Zip-Dateien herunterladen:
|
||||||
* https://github.com/PaulStoffregen/Time/archive/master.zip
|
* https://github.com/PaulStoffregen/Time/archive/master.zip
|
||||||
* https://github.com/JChristensen/Timezone/archive/master.zip
|
* https://github.com/JChristensen/Timezone/archive/master.zip
|
||||||
|
|||||||
+1993
-2493
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+29
-203
@@ -5,40 +5,10 @@
|
|||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
#include <Wire.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
// ================= RTC =================
|
|
||||||
// RTC-Modul waehlen (vor dem Kompilieren): RTC_MODUL_DS3231, RTC_MODUL_DS1307 oder RTC_MODUL_KEINE.
|
|
||||||
// PlatformIO: ueber die Umgebung (siehe platformio.ini). Arduino IDE: hier eintragen.
|
|
||||||
// Das Board (D1 mini / NodeMCU) waehlt man in der IDE bzw. ebenfalls ueber die Umgebung;
|
|
||||||
// die Pinnamen D1/D2/D4 sind bei beiden identisch.
|
|
||||||
#if !defined(RTC_MODUL_DS3231) && !defined(RTC_MODUL_DS1307) && !defined(RTC_MODUL_KEINE)
|
|
||||||
#define RTC_MODUL_DS3231
|
|
||||||
#endif
|
|
||||||
#if defined(RTC_MODUL_DS3231) || defined(RTC_MODUL_DS1307)
|
|
||||||
#include <RTClib.h>
|
|
||||||
#define RTC_VORHANDEN
|
|
||||||
#endif
|
|
||||||
#if defined(RTC_MODUL_DS3231)
|
|
||||||
RTC_DS3231 rtc;
|
|
||||||
#elif defined(RTC_MODUL_DS1307)
|
|
||||||
RTC_DS1307 rtc;
|
|
||||||
#endif
|
|
||||||
#define RTC_SDA D2
|
|
||||||
#define RTC_SCL D1
|
|
||||||
bool rtcOk = false;
|
|
||||||
bool rtcBatteryEmpty = false; // RTC hat beim Start die Zeit verloren
|
|
||||||
volatile bool ntpJustSynced = false;
|
|
||||||
void onTimeSet(bool fromSntp){ if(fromSntp) ntpJustSynced = true; }
|
|
||||||
|
|
||||||
// ================= LED =================
|
// ================= LED =================
|
||||||
#define LED_PIN D4
|
#define LED_PIN D4
|
||||||
// Neue Aufteilung (cad/openscad/gemeinsam.scad): je Wort ein LED-Streifen mit einer
|
#define NUMPIXELS 110
|
||||||
// LED pro Buchstabe, Zickzack durch die Zeilen. Zeile 0 laeuft links->rechts, Zeile 1
|
|
||||||
// rechts->links usw. Die Fuellbuchstaben gibt es nicht, nur die Woerter (inkl. NTP,
|
|
||||||
// API, RTC, ESP). Minutenpunkte sind abgeschaltet.
|
|
||||||
#define NUMPIXELS 104
|
|
||||||
Adafruit_NeoPixel pixels(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
Adafruit_NeoPixel pixels(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
|
|
||||||
// ================= SERVER & NTP =================
|
// ================= SERVER & NTP =================
|
||||||
@@ -49,7 +19,7 @@ String ntpServer = DEFAULT_NTP;
|
|||||||
String tzString = DEFAULT_TZ;
|
String tzString = DEFAULT_TZ;
|
||||||
|
|
||||||
// ================= SETTINGS =================
|
// ================= SETTINGS =================
|
||||||
#define SETTINGS_MAGIC 0x57554853UL // erkennt gueltige EEPROM-Daten
|
#define SETTINGS_MAGIC 0x57554852UL // erkennt gueltige EEPROM-Daten
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -62,8 +32,6 @@ struct Settings {
|
|||||||
uint32_t bgColor;
|
uint32_t bgColor;
|
||||||
bool backgroundMode;
|
bool backgroundMode;
|
||||||
bool nightBackgroundDisabled;
|
bool nightBackgroundDisabled;
|
||||||
bool statusLeds;
|
|
||||||
int ntpShowSec;
|
|
||||||
char ntpServer[64];
|
char ntpServer[64];
|
||||||
char tzString[64];
|
char tzString[64];
|
||||||
} settings;
|
} settings;
|
||||||
@@ -73,8 +41,6 @@ uint32_t currentColor = 0x00FFFF;
|
|||||||
uint32_t bgColor = 0x000000;
|
uint32_t bgColor = 0x000000;
|
||||||
bool backgroundMode = false;
|
bool backgroundMode = false;
|
||||||
bool nightBackgroundDisabled = false;
|
bool nightBackgroundDisabled = false;
|
||||||
bool statusLeds = true; // Statuswoerter NTP/RTC/ESP anzeigen
|
|
||||||
int ntpShowSec = 10; // Mindestdauer der gruenen NTP-Anzeige
|
|
||||||
bool forceInstant = false;
|
bool forceInstant = false;
|
||||||
bool updateNeeded = true;
|
bool updateNeeded = true;
|
||||||
int brightness = 120;
|
int brightness = 120;
|
||||||
@@ -104,8 +70,6 @@ void saveSettings(){
|
|||||||
settings.bgColor = bgColor;
|
settings.bgColor = bgColor;
|
||||||
settings.backgroundMode = backgroundMode;
|
settings.backgroundMode = backgroundMode;
|
||||||
settings.nightBackgroundDisabled = nightBackgroundDisabled;
|
settings.nightBackgroundDisabled = nightBackgroundDisabled;
|
||||||
settings.statusLeds = statusLeds;
|
|
||||||
settings.ntpShowSec = ntpShowSec;
|
|
||||||
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
|
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
|
||||||
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
||||||
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
|
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
|
||||||
@@ -127,8 +91,6 @@ void loadSettings(){
|
|||||||
bgColor = settings.bgColor & 0xFFFFFF;
|
bgColor = settings.bgColor & 0xFFFFFF;
|
||||||
backgroundMode = settings.backgroundMode;
|
backgroundMode = settings.backgroundMode;
|
||||||
nightBackgroundDisabled = settings.nightBackgroundDisabled;
|
nightBackgroundDisabled = settings.nightBackgroundDisabled;
|
||||||
statusLeds = settings.statusLeds;
|
|
||||||
ntpShowSec = constrain(settings.ntpShowSec, 1, 60);
|
|
||||||
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
||||||
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
|
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
|
||||||
ntpServer = String(settings.ntpServer);
|
ntpServer = String(settings.ntpServer);
|
||||||
@@ -139,109 +101,6 @@ void loadSettings(){
|
|||||||
configTime(tzString.c_str(), ntpServer.c_str());
|
configTime(tzString.c_str(), ntpServer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeit aus der RTC uebernehmen. Die RTC laeuft in UTC.
|
|
||||||
#define RTC_INTERVAL_MS 3600000UL // ohne NTP: RTC stuendlich neu einlesen
|
|
||||||
#define NTP_GUELTIG_MS 7200000UL // NTP-Sync gilt 2 h als aktuell
|
|
||||||
unsigned long lastRtcReadMs = 0;
|
|
||||||
unsigned long lastNtpMs = 0; // 0 = noch kein NTP-Sync seit Start
|
|
||||||
bool ntpShowing = false; // NTP-Wort leuchtet gerade
|
|
||||||
unsigned long ntpShowUntil = 0;
|
|
||||||
|
|
||||||
bool ntpAktuell(){
|
|
||||||
return lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Liest die RTC; liefert false, wenn sie keine gueltige Zeit hat (Batterie leer).
|
|
||||||
bool readRtcToSystem(){
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
lastRtcReadMs = millis();
|
|
||||||
#if defined(RTC_MODUL_DS3231)
|
|
||||||
bool valid = !rtc.lostPower();
|
|
||||||
#else
|
|
||||||
bool valid = rtc.isrunning();
|
|
||||||
#endif
|
|
||||||
DateTime n = rtc.now();
|
|
||||||
if(valid && n.year() >= 2024){
|
|
||||||
struct timeval tv = { (time_t)n.unixtime(), 0 };
|
|
||||||
settimeofday(&tv, nullptr);
|
|
||||||
ntpJustSynced = false; // der eigene Aufruf ist keine NTP-Zeit
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initRtc(){
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
Wire.begin(RTC_SDA, RTC_SCL);
|
|
||||||
if(!rtc.begin()) return;
|
|
||||||
rtcOk = true;
|
|
||||||
// Nach dem Nachstellen per NTP ist der Batteriestatus nicht mehr erkennbar,
|
|
||||||
// daher wird er nur beim Start bestimmt.
|
|
||||||
rtcBatteryEmpty = !readRtcToSystem();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nach jeder NTP-Synchronisation die RTC nachstellen; ohne aktuellen NTP-Sync
|
|
||||||
// stuendlich die Systemzeit aus der RTC auffrischen.
|
|
||||||
void updateTimeSources(){
|
|
||||||
if(ntpJustSynced){
|
|
||||||
ntpJustSynced = false;
|
|
||||||
if(timeSynced()){
|
|
||||||
lastNtpMs = millis() ? millis() : 1;
|
|
||||||
ntpShowing = true;
|
|
||||||
ntpShowUntil = millis() + (unsigned long)ntpShowSec * 1000UL;
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
if(rtcOk) rtc.adjust(DateTime((uint32_t)time(nullptr)));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ntpShowing && (long)(millis() - ntpShowUntil) >= 0) ntpShowing = false;
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
if(rtcOk && !rtcBatteryEmpty && !ntpAktuell() && millis() - lastRtcReadMs >= RTC_INTERVAL_MS)
|
|
||||||
readRtcToSystem();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= STATUS-WOERTER =================
|
|
||||||
// NTP (gruen): waehrend/kurz nach dem NTP-Abgleich
|
|
||||||
// RTC (gelb): RTC vorhanden, aber kein aktueller NTP-Abgleich; bei leerer Batterie
|
|
||||||
// blinkt das Wort im 10-s-Takt
|
|
||||||
// ESP (rot): weder gueltige RTC noch NTP
|
|
||||||
#define STATUS_GRUEN 0x00FF00
|
|
||||||
#define STATUS_GELB 0xFFFF00
|
|
||||||
#define STATUS_ROT 0xFF0000
|
|
||||||
struct StatusFarben { uint32_t ntp, rtc, esp; };
|
|
||||||
StatusFarben statusAktuell = {0, 0, 0};
|
|
||||||
|
|
||||||
StatusFarben berechneStatus(){
|
|
||||||
StatusFarben st = {0, 0, 0};
|
|
||||||
if(!statusLeds) return st;
|
|
||||||
bool ntpOk = ntpAktuell();
|
|
||||||
bool rtcGut = rtcOk && !rtcBatteryEmpty;
|
|
||||||
if(ntpShowing) st.ntp = STATUS_GRUEN;
|
|
||||||
if(rtcOk){
|
|
||||||
if(rtcBatteryEmpty) st.rtc = ((millis() / 10000UL) & 1) ? 0 : STATUS_GELB;
|
|
||||||
else if(!ntpOk) st.rtc = STATUS_GELB;
|
|
||||||
}
|
|
||||||
if(!rtcGut && !ntpOk) st.esp = STATUS_ROT;
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aenderung der Statusanzeige erkennen (loest ein Neuzeichnen aus)
|
|
||||||
bool statusGeaendert(){
|
|
||||||
StatusFarben st = berechneStatus();
|
|
||||||
bool changed = st.ntp != statusAktuell.ntp || st.rtc != statusAktuell.rtc || st.esp != statusAktuell.esp;
|
|
||||||
statusAktuell = st;
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyStatus(){
|
|
||||||
for(int i=34;i<=36;i++) if(statusAktuell.ntp) targetBuffer[i] = statusAktuell.ntp;
|
|
||||||
for(int i=56;i<=58;i++) if(statusAktuell.rtc) targetBuffer[i] = statusAktuell.rtc;
|
|
||||||
for(int i=67;i<=69;i++) if(statusAktuell.esp) targetBuffer[i] = statusAktuell.esp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= HELFER =================
|
// ================= HELFER =================
|
||||||
bool isNightTime(struct tm *t){
|
bool isNightTime(struct tm *t){
|
||||||
if(!autoNight) return false;
|
if(!autoNight) return false;
|
||||||
@@ -322,34 +181,29 @@ void fadeToTargetSmooth(int steps = 30, int delayMs = 15){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================= WORTUHR LOGIK =================
|
// ================= WORTUHR LOGIK =================
|
||||||
// LED-Indizes in Kettenreihenfolge (Zeilen im Zickzack, Woerter je Zeile in Laufrichtung)
|
void word_ES(){ setRange(0,1); }
|
||||||
void word_ES(){ setRange(0,1); } // Zeile 0 (links->rechts)
|
void word_IST(){ setRange(3,5); }
|
||||||
void word_IST(){ setRange(2,4); }
|
void word_FUENF_M(){ setRange(7,10); }
|
||||||
void word_FUENF_M(){ setRange(5,8); }
|
void word_ZEHN_M(){ setRange(18,21); }
|
||||||
void word_ZEHN_M(){ setRange(9,12); } // Zeile 1 (rechts->links)
|
void word_ZWANZIG(){ setRange(11,17); }
|
||||||
void word_ZWANZIG(){ setRange(13,19); }
|
void word_VIERTEL(){ setRange(22,28); }
|
||||||
void word_VIERTEL(){ setRange(20,26); } // Zeile 2
|
void word_VOR(){ setRange(30,32); }
|
||||||
void word_VOR(){ setRange(27,29); }
|
void word_NACH(){ setRange(40,43); }
|
||||||
void word_NACH(){ setRange(30,33); } // Zeile 3 (rechts->links)
|
void word_HALB(){ setRange(33,36); }
|
||||||
// NTP 34-36
|
void word_EINS(){ setRange(62,65); }
|
||||||
void word_HALB(){ setRange(37,40); }
|
void word_EIN(){ setRange(62,64); }
|
||||||
void word_NEUN(){ setRange(41,44); } // Zeile 4
|
void word_ZWEI(){ setRange(55,58); }
|
||||||
// API 45-47
|
void word_DREI(){ setRange(66,69); }
|
||||||
void word_FUENF_H(){ setRange(48,51); }
|
void word_VIER(){ setRange(73,76); }
|
||||||
void word_EINS(){ setRange(52,55); } // Zeile 5 (rechts->links): S, EIN, RTC, ZWEI
|
void word_FUENF_H(){ setRange(51,54); }
|
||||||
void word_EIN(){ setRange(53,55); }
|
void word_SECHS(){ setRange(83,87); }
|
||||||
// RTC 56-58
|
void word_SIEBEN(){ setRange(88,93); }
|
||||||
void word_ZWEI(){ setRange(59,62); }
|
void word_ACHT(){ setRange(77,80); }
|
||||||
void word_DREI(){ setRange(63,66); } // Zeile 6
|
void word_NEUN(){ setRange(44,47); }
|
||||||
// ESP 67-69
|
void word_ZEHN_H(){ setRange(106,109); }
|
||||||
void word_VIER(){ setRange(70,73); }
|
void word_ELF(){ setRange(103,105); }
|
||||||
void word_SECHS(){ setRange(74,78); } // Zeile 7 (rechts->links)
|
void word_ZWOELF(){ setRange(94,98); }
|
||||||
void word_ACHT(){ setRange(79,82); }
|
void word_UHR(){ setRange(99,101); }
|
||||||
void word_SIEBEN(){ setRange(83,88); } // Zeile 8
|
|
||||||
void word_ZWOELF(){ setRange(89,93); }
|
|
||||||
void word_ZEHN_H(){ setRange(94,97); } // Zeile 9 (rechts->links)
|
|
||||||
void word_ELF(){ setRange(98,100); }
|
|
||||||
void word_UHR(){ setRange(101,103); }
|
|
||||||
|
|
||||||
String getTimeText(int h, int m){
|
String getTimeText(int h, int m){
|
||||||
int m5 = (m/5)*5;
|
int m5 = (m/5)*5;
|
||||||
@@ -411,21 +265,10 @@ void showTimeWords(int h, int m){
|
|||||||
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
|
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
|
||||||
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
|
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
|
||||||
|
|
||||||
applyStatus();
|
|
||||||
|
|
||||||
if(forceInstant){ showInstant(); forceInstant = false; }
|
if(forceInstant){ showInstant(); forceInstant = false; }
|
||||||
else { fadeToTargetSmooth(20, 10); }
|
else { fadeToTargetSmooth(20, 10); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solange noch keine Zeit bekannt ist, nur Hintergrund und Statuswoerter zeigen
|
|
||||||
void showStatusOnly(){
|
|
||||||
for(int i=0;i<NUMPIXELS;i++) currentBuffer[i] = targetBuffer[i];
|
|
||||||
uint32_t activeBg = backgroundMode ? bgColor : 0;
|
|
||||||
for(int i=0;i<NUMPIXELS;i++) targetBuffer[i] = activeBg;
|
|
||||||
applyStatus();
|
|
||||||
showInstant();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= WEB HANDLERS =================
|
// ================= WEB HANDLERS =================
|
||||||
String jsonEscape(const String &s){
|
String jsonEscape(const String &s){
|
||||||
String out;
|
String out;
|
||||||
@@ -470,9 +313,7 @@ void handleStatus(){
|
|||||||
json += "\"nightStart\":" + String(nightStart) + ",";
|
json += "\"nightStart\":" + String(nightStart) + ",";
|
||||||
json += "\"nightEnd\":" + String(nightEnd) + ",";
|
json += "\"nightEnd\":" + String(nightEnd) + ",";
|
||||||
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
|
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
|
||||||
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0) + ",";
|
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0);
|
||||||
json += "\"statusLeds\":" + String(statusLeds ? 1 : 0) + ",";
|
|
||||||
json += "\"ntpShowSec\":" + String(ntpShowSec);
|
|
||||||
json += "}";
|
json += "}";
|
||||||
server.send(200, "application/json", json);
|
server.send(200, "application/json", json);
|
||||||
}
|
}
|
||||||
@@ -510,10 +351,6 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
|||||||
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
|
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
|
||||||
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
|
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
|
||||||
</div>
|
</div>
|
||||||
<div class='card'>
|
|
||||||
<input type='checkbox' id='sl' onchange="f('/statusLeds?val='+(this.checked?1:0))"> <b>Status-Anzeige</b> (NTP grün, RTC gelb, ESP rot)<br><br>
|
|
||||||
NTP-Anzeige mind. <input type='number' id='nsec' min='1' max='60' style='width:50px' oninput="if(this.value!=='')f('/ntpShowSec?val='+this.value)"> Sekunden
|
|
||||||
</div>
|
|
||||||
<div class='card'>
|
<div class='card'>
|
||||||
<label>NTP Server:</label>
|
<label>NTP Server:</label>
|
||||||
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
|
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
|
||||||
@@ -530,7 +367,7 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
|||||||
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
|
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
|
||||||
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
|
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
|
||||||
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
|
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
|
||||||
s('nightBgOff',d.nightBgOff,true);s('sl',d.statusLeds,true);s('nsec',d.ntpShowSec);
|
s('nightBgOff',d.nightBgOff,true);
|
||||||
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
|
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
|
||||||
setInterval(u, 2500); u();
|
setInterval(u, 2500); u();
|
||||||
</script></body></html>
|
</script></body></html>
|
||||||
@@ -560,8 +397,6 @@ void handleNightStart(){ int v; if(!argInRange(0,23,v)) return; nightStart=v; fo
|
|||||||
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleStatusLeds(){ int v; if(!argInRange(0,1,v)) return; statusLeds=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
|
||||||
void handleNtpShowSec(){ int v; if(!argInRange(1,60,v)) return; ntpShowSec=v; sendOK(); }
|
|
||||||
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
|
|
||||||
void handleNTP(){
|
void handleNTP(){
|
||||||
@@ -587,9 +422,7 @@ void setup(){
|
|||||||
pixels.clear();
|
pixels.clear();
|
||||||
pixels.show();
|
pixels.show();
|
||||||
|
|
||||||
settimeofday_cb(onTimeSet);
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
initRtc();
|
|
||||||
|
|
||||||
WiFiManager wm;
|
WiFiManager wm;
|
||||||
wm.autoConnect("Wortuhr");
|
wm.autoConnect("Wortuhr");
|
||||||
@@ -605,8 +438,6 @@ void setup(){
|
|||||||
server.on("/nightBrightness", handleNightBrightness);
|
server.on("/nightBrightness", handleNightBrightness);
|
||||||
server.on("/nightBgOff", handleNightBgOff);
|
server.on("/nightBgOff", handleNightBgOff);
|
||||||
server.on("/bg", handleBG);
|
server.on("/bg", handleBG);
|
||||||
server.on("/statusLeds", handleStatusLeds);
|
|
||||||
server.on("/ntpShowSec", handleNtpShowSec);
|
|
||||||
server.on("/setNTP", handleNTP);
|
server.on("/setNTP", handleNTP);
|
||||||
server.on("/setTZ", handleTZ);
|
server.on("/setTZ", handleTZ);
|
||||||
server.on("/save", handleSave);
|
server.on("/save", handleSave);
|
||||||
@@ -615,14 +446,9 @@ void setup(){
|
|||||||
|
|
||||||
void loop(){
|
void loop(){
|
||||||
server.handleClient();
|
server.handleClient();
|
||||||
updateTimeSources();
|
|
||||||
if(statusGeaendert()){ forceInstant = true; updateNeeded = true; }
|
|
||||||
|
|
||||||
// Ohne bekannte Zeit nur die Statuswoerter anzeigen
|
// Erst anzeigen, wenn die Zeit per NTP synchronisiert ist
|
||||||
if(!timeSynced()){
|
if(!timeSynced()) return;
|
||||||
if(updateNeeded){ updateNeeded = false; forceInstant = false; showStatusOnly(); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
struct tm *t = localtime(&now);
|
struct tm *t = localtime(&now);
|
||||||
|
|||||||
@@ -1,40 +1,8 @@
|
|||||||
; Umgebung = Board + RTC-Modul waehlen, z.B.: pio run -e nodemcu_ds3231 -t upload
|
[env:d1_mini]
|
||||||
; Board: d1_mini | nodemcu RTC: ds3231 | ds1307 | keine
|
|
||||||
[common]
|
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
|
board = d1_mini
|
||||||
framework = arduino
|
framework = arduino
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
lib_deps =
|
lib_deps =
|
||||||
adafruit/Adafruit NeoPixel
|
adafruit/Adafruit NeoPixel
|
||||||
tzapu/WiFiManager
|
tzapu/WiFiManager
|
||||||
adafruit/RTClib
|
|
||||||
|
|
||||||
[board_d1_mini]
|
|
||||||
board = d1_mini
|
|
||||||
|
|
||||||
[board_nodemcu]
|
|
||||||
board = nodemcuv2
|
|
||||||
|
|
||||||
[env:d1_mini_ds3231]
|
|
||||||
extends = common, board_d1_mini
|
|
||||||
build_flags = -DRTC_MODUL_DS3231
|
|
||||||
|
|
||||||
[env:d1_mini_ds1307]
|
|
||||||
extends = common, board_d1_mini
|
|
||||||
build_flags = -DRTC_MODUL_DS1307
|
|
||||||
|
|
||||||
[env:d1_mini_keine]
|
|
||||||
extends = common, board_d1_mini
|
|
||||||
build_flags = -DRTC_MODUL_KEINE
|
|
||||||
|
|
||||||
[env:nodemcu_ds3231]
|
|
||||||
extends = common, board_nodemcu
|
|
||||||
build_flags = -DRTC_MODUL_DS3231
|
|
||||||
|
|
||||||
[env:nodemcu_ds1307]
|
|
||||||
extends = common, board_nodemcu
|
|
||||||
build_flags = -DRTC_MODUL_DS1307
|
|
||||||
|
|
||||||
[env:nodemcu_keine]
|
|
||||||
extends = common, board_nodemcu
|
|
||||||
build_flags = -DRTC_MODUL_KEINE
|
|
||||||
+29
-203
@@ -5,40 +5,10 @@
|
|||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <WiFiManager.h>
|
#include <WiFiManager.h>
|
||||||
#include <Wire.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
// ================= RTC =================
|
|
||||||
// RTC-Modul waehlen (vor dem Kompilieren): RTC_MODUL_DS3231, RTC_MODUL_DS1307 oder RTC_MODUL_KEINE.
|
|
||||||
// PlatformIO: ueber die Umgebung (siehe platformio.ini). Arduino IDE: hier eintragen.
|
|
||||||
// Das Board (D1 mini / NodeMCU) waehlt man in der IDE bzw. ebenfalls ueber die Umgebung;
|
|
||||||
// die Pinnamen D1/D2/D4 sind bei beiden identisch.
|
|
||||||
#if !defined(RTC_MODUL_DS3231) && !defined(RTC_MODUL_DS1307) && !defined(RTC_MODUL_KEINE)
|
|
||||||
#define RTC_MODUL_DS3231
|
|
||||||
#endif
|
|
||||||
#if defined(RTC_MODUL_DS3231) || defined(RTC_MODUL_DS1307)
|
|
||||||
#include <RTClib.h>
|
|
||||||
#define RTC_VORHANDEN
|
|
||||||
#endif
|
|
||||||
#if defined(RTC_MODUL_DS3231)
|
|
||||||
RTC_DS3231 rtc;
|
|
||||||
#elif defined(RTC_MODUL_DS1307)
|
|
||||||
RTC_DS1307 rtc;
|
|
||||||
#endif
|
|
||||||
#define RTC_SDA D2
|
|
||||||
#define RTC_SCL D1
|
|
||||||
bool rtcOk = false;
|
|
||||||
bool rtcBatteryEmpty = false; // RTC hat beim Start die Zeit verloren
|
|
||||||
volatile bool ntpJustSynced = false;
|
|
||||||
void onTimeSet(bool fromSntp){ if(fromSntp) ntpJustSynced = true; }
|
|
||||||
|
|
||||||
// ================= LED =================
|
// ================= LED =================
|
||||||
#define LED_PIN D4
|
#define LED_PIN D4
|
||||||
// Neue Aufteilung (cad/openscad/gemeinsam.scad): je Wort ein LED-Streifen mit einer
|
#define NUMPIXELS 110
|
||||||
// LED pro Buchstabe, Zickzack durch die Zeilen. Zeile 0 laeuft links->rechts, Zeile 1
|
|
||||||
// rechts->links usw. Die Fuellbuchstaben gibt es nicht, nur die Woerter (inkl. NTP,
|
|
||||||
// API, RTC, ESP). Minutenpunkte sind abgeschaltet.
|
|
||||||
#define NUMPIXELS 104
|
|
||||||
Adafruit_NeoPixel pixels(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
Adafruit_NeoPixel pixels(NUMPIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||||
|
|
||||||
// ================= SERVER & NTP =================
|
// ================= SERVER & NTP =================
|
||||||
@@ -49,7 +19,7 @@ String ntpServer = DEFAULT_NTP;
|
|||||||
String tzString = DEFAULT_TZ;
|
String tzString = DEFAULT_TZ;
|
||||||
|
|
||||||
// ================= SETTINGS =================
|
// ================= SETTINGS =================
|
||||||
#define SETTINGS_MAGIC 0x57554853UL // erkennt gueltige EEPROM-Daten
|
#define SETTINGS_MAGIC 0x57554852UL // erkennt gueltige EEPROM-Daten
|
||||||
|
|
||||||
struct Settings {
|
struct Settings {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -62,8 +32,6 @@ struct Settings {
|
|||||||
uint32_t bgColor;
|
uint32_t bgColor;
|
||||||
bool backgroundMode;
|
bool backgroundMode;
|
||||||
bool nightBackgroundDisabled;
|
bool nightBackgroundDisabled;
|
||||||
bool statusLeds;
|
|
||||||
int ntpShowSec;
|
|
||||||
char ntpServer[64];
|
char ntpServer[64];
|
||||||
char tzString[64];
|
char tzString[64];
|
||||||
} settings;
|
} settings;
|
||||||
@@ -73,8 +41,6 @@ uint32_t currentColor = 0x00FFFF;
|
|||||||
uint32_t bgColor = 0x000000;
|
uint32_t bgColor = 0x000000;
|
||||||
bool backgroundMode = false;
|
bool backgroundMode = false;
|
||||||
bool nightBackgroundDisabled = false;
|
bool nightBackgroundDisabled = false;
|
||||||
bool statusLeds = true; // Statuswoerter NTP/RTC/ESP anzeigen
|
|
||||||
int ntpShowSec = 10; // Mindestdauer der gruenen NTP-Anzeige
|
|
||||||
bool forceInstant = false;
|
bool forceInstant = false;
|
||||||
bool updateNeeded = true;
|
bool updateNeeded = true;
|
||||||
int brightness = 120;
|
int brightness = 120;
|
||||||
@@ -104,8 +70,6 @@ void saveSettings(){
|
|||||||
settings.bgColor = bgColor;
|
settings.bgColor = bgColor;
|
||||||
settings.backgroundMode = backgroundMode;
|
settings.backgroundMode = backgroundMode;
|
||||||
settings.nightBackgroundDisabled = nightBackgroundDisabled;
|
settings.nightBackgroundDisabled = nightBackgroundDisabled;
|
||||||
settings.statusLeds = statusLeds;
|
|
||||||
settings.ntpShowSec = ntpShowSec;
|
|
||||||
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
|
strncpy(settings.ntpServer, ntpServer.c_str(), sizeof(settings.ntpServer) - 1);
|
||||||
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
||||||
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
|
strncpy(settings.tzString, tzString.c_str(), sizeof(settings.tzString) - 1);
|
||||||
@@ -127,8 +91,6 @@ void loadSettings(){
|
|||||||
bgColor = settings.bgColor & 0xFFFFFF;
|
bgColor = settings.bgColor & 0xFFFFFF;
|
||||||
backgroundMode = settings.backgroundMode;
|
backgroundMode = settings.backgroundMode;
|
||||||
nightBackgroundDisabled = settings.nightBackgroundDisabled;
|
nightBackgroundDisabled = settings.nightBackgroundDisabled;
|
||||||
statusLeds = settings.statusLeds;
|
|
||||||
ntpShowSec = constrain(settings.ntpShowSec, 1, 60);
|
|
||||||
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
settings.ntpServer[sizeof(settings.ntpServer) - 1] = '\0';
|
||||||
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
|
settings.tzString[sizeof(settings.tzString) - 1] = '\0';
|
||||||
ntpServer = String(settings.ntpServer);
|
ntpServer = String(settings.ntpServer);
|
||||||
@@ -139,109 +101,6 @@ void loadSettings(){
|
|||||||
configTime(tzString.c_str(), ntpServer.c_str());
|
configTime(tzString.c_str(), ntpServer.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zeit aus der RTC uebernehmen. Die RTC laeuft in UTC.
|
|
||||||
#define RTC_INTERVAL_MS 3600000UL // ohne NTP: RTC stuendlich neu einlesen
|
|
||||||
#define NTP_GUELTIG_MS 7200000UL // NTP-Sync gilt 2 h als aktuell
|
|
||||||
unsigned long lastRtcReadMs = 0;
|
|
||||||
unsigned long lastNtpMs = 0; // 0 = noch kein NTP-Sync seit Start
|
|
||||||
bool ntpShowing = false; // NTP-Wort leuchtet gerade
|
|
||||||
unsigned long ntpShowUntil = 0;
|
|
||||||
|
|
||||||
bool ntpAktuell(){
|
|
||||||
return lastNtpMs != 0 && millis() - lastNtpMs < NTP_GUELTIG_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Liest die RTC; liefert false, wenn sie keine gueltige Zeit hat (Batterie leer).
|
|
||||||
bool readRtcToSystem(){
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
lastRtcReadMs = millis();
|
|
||||||
#if defined(RTC_MODUL_DS3231)
|
|
||||||
bool valid = !rtc.lostPower();
|
|
||||||
#else
|
|
||||||
bool valid = rtc.isrunning();
|
|
||||||
#endif
|
|
||||||
DateTime n = rtc.now();
|
|
||||||
if(valid && n.year() >= 2024){
|
|
||||||
struct timeval tv = { (time_t)n.unixtime(), 0 };
|
|
||||||
settimeofday(&tv, nullptr);
|
|
||||||
ntpJustSynced = false; // der eigene Aufruf ist keine NTP-Zeit
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initRtc(){
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
Wire.begin(RTC_SDA, RTC_SCL);
|
|
||||||
if(!rtc.begin()) return;
|
|
||||||
rtcOk = true;
|
|
||||||
// Nach dem Nachstellen per NTP ist der Batteriestatus nicht mehr erkennbar,
|
|
||||||
// daher wird er nur beim Start bestimmt.
|
|
||||||
rtcBatteryEmpty = !readRtcToSystem();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nach jeder NTP-Synchronisation die RTC nachstellen; ohne aktuellen NTP-Sync
|
|
||||||
// stuendlich die Systemzeit aus der RTC auffrischen.
|
|
||||||
void updateTimeSources(){
|
|
||||||
if(ntpJustSynced){
|
|
||||||
ntpJustSynced = false;
|
|
||||||
if(timeSynced()){
|
|
||||||
lastNtpMs = millis() ? millis() : 1;
|
|
||||||
ntpShowing = true;
|
|
||||||
ntpShowUntil = millis() + (unsigned long)ntpShowSec * 1000UL;
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
if(rtcOk) rtc.adjust(DateTime((uint32_t)time(nullptr)));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ntpShowing && (long)(millis() - ntpShowUntil) >= 0) ntpShowing = false;
|
|
||||||
#ifdef RTC_VORHANDEN
|
|
||||||
if(rtcOk && !rtcBatteryEmpty && !ntpAktuell() && millis() - lastRtcReadMs >= RTC_INTERVAL_MS)
|
|
||||||
readRtcToSystem();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= STATUS-WOERTER =================
|
|
||||||
// NTP (gruen): waehrend/kurz nach dem NTP-Abgleich
|
|
||||||
// RTC (gelb): RTC vorhanden, aber kein aktueller NTP-Abgleich; bei leerer Batterie
|
|
||||||
// blinkt das Wort im 10-s-Takt
|
|
||||||
// ESP (rot): weder gueltige RTC noch NTP
|
|
||||||
#define STATUS_GRUEN 0x00FF00
|
|
||||||
#define STATUS_GELB 0xFFFF00
|
|
||||||
#define STATUS_ROT 0xFF0000
|
|
||||||
struct StatusFarben { uint32_t ntp, rtc, esp; };
|
|
||||||
StatusFarben statusAktuell = {0, 0, 0};
|
|
||||||
|
|
||||||
StatusFarben berechneStatus(){
|
|
||||||
StatusFarben st = {0, 0, 0};
|
|
||||||
if(!statusLeds) return st;
|
|
||||||
bool ntpOk = ntpAktuell();
|
|
||||||
bool rtcGut = rtcOk && !rtcBatteryEmpty;
|
|
||||||
if(ntpShowing) st.ntp = STATUS_GRUEN;
|
|
||||||
if(rtcOk){
|
|
||||||
if(rtcBatteryEmpty) st.rtc = ((millis() / 10000UL) & 1) ? 0 : STATUS_GELB;
|
|
||||||
else if(!ntpOk) st.rtc = STATUS_GELB;
|
|
||||||
}
|
|
||||||
if(!rtcGut && !ntpOk) st.esp = STATUS_ROT;
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aenderung der Statusanzeige erkennen (loest ein Neuzeichnen aus)
|
|
||||||
bool statusGeaendert(){
|
|
||||||
StatusFarben st = berechneStatus();
|
|
||||||
bool changed = st.ntp != statusAktuell.ntp || st.rtc != statusAktuell.rtc || st.esp != statusAktuell.esp;
|
|
||||||
statusAktuell = st;
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyStatus(){
|
|
||||||
for(int i=34;i<=36;i++) if(statusAktuell.ntp) targetBuffer[i] = statusAktuell.ntp;
|
|
||||||
for(int i=56;i<=58;i++) if(statusAktuell.rtc) targetBuffer[i] = statusAktuell.rtc;
|
|
||||||
for(int i=67;i<=69;i++) if(statusAktuell.esp) targetBuffer[i] = statusAktuell.esp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= HELFER =================
|
// ================= HELFER =================
|
||||||
bool isNightTime(struct tm *t){
|
bool isNightTime(struct tm *t){
|
||||||
if(!autoNight) return false;
|
if(!autoNight) return false;
|
||||||
@@ -322,34 +181,29 @@ void fadeToTargetSmooth(int steps = 30, int delayMs = 15){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================= WORTUHR LOGIK =================
|
// ================= WORTUHR LOGIK =================
|
||||||
// LED-Indizes in Kettenreihenfolge (Zeilen im Zickzack, Woerter je Zeile in Laufrichtung)
|
void word_ES(){ setRange(0,1); }
|
||||||
void word_ES(){ setRange(0,1); } // Zeile 0 (links->rechts)
|
void word_IST(){ setRange(3,5); }
|
||||||
void word_IST(){ setRange(2,4); }
|
void word_FUENF_M(){ setRange(7,10); }
|
||||||
void word_FUENF_M(){ setRange(5,8); }
|
void word_ZEHN_M(){ setRange(18,21); }
|
||||||
void word_ZEHN_M(){ setRange(9,12); } // Zeile 1 (rechts->links)
|
void word_ZWANZIG(){ setRange(11,17); }
|
||||||
void word_ZWANZIG(){ setRange(13,19); }
|
void word_VIERTEL(){ setRange(22,28); }
|
||||||
void word_VIERTEL(){ setRange(20,26); } // Zeile 2
|
void word_VOR(){ setRange(30,32); }
|
||||||
void word_VOR(){ setRange(27,29); }
|
void word_NACH(){ setRange(40,43); }
|
||||||
void word_NACH(){ setRange(30,33); } // Zeile 3 (rechts->links)
|
void word_HALB(){ setRange(33,36); }
|
||||||
// NTP 34-36
|
void word_EINS(){ setRange(62,65); }
|
||||||
void word_HALB(){ setRange(37,40); }
|
void word_EIN(){ setRange(62,64); }
|
||||||
void word_NEUN(){ setRange(41,44); } // Zeile 4
|
void word_ZWEI(){ setRange(55,58); }
|
||||||
// API 45-47
|
void word_DREI(){ setRange(66,69); }
|
||||||
void word_FUENF_H(){ setRange(48,51); }
|
void word_VIER(){ setRange(73,76); }
|
||||||
void word_EINS(){ setRange(52,55); } // Zeile 5 (rechts->links): S, EIN, RTC, ZWEI
|
void word_FUENF_H(){ setRange(51,54); }
|
||||||
void word_EIN(){ setRange(53,55); }
|
void word_SECHS(){ setRange(83,87); }
|
||||||
// RTC 56-58
|
void word_SIEBEN(){ setRange(88,93); }
|
||||||
void word_ZWEI(){ setRange(59,62); }
|
void word_ACHT(){ setRange(77,80); }
|
||||||
void word_DREI(){ setRange(63,66); } // Zeile 6
|
void word_NEUN(){ setRange(44,47); }
|
||||||
// ESP 67-69
|
void word_ZEHN_H(){ setRange(106,109); }
|
||||||
void word_VIER(){ setRange(70,73); }
|
void word_ELF(){ setRange(103,105); }
|
||||||
void word_SECHS(){ setRange(74,78); } // Zeile 7 (rechts->links)
|
void word_ZWOELF(){ setRange(94,98); }
|
||||||
void word_ACHT(){ setRange(79,82); }
|
void word_UHR(){ setRange(99,101); }
|
||||||
void word_SIEBEN(){ setRange(83,88); } // Zeile 8
|
|
||||||
void word_ZWOELF(){ setRange(89,93); }
|
|
||||||
void word_ZEHN_H(){ setRange(94,97); } // Zeile 9 (rechts->links)
|
|
||||||
void word_ELF(){ setRange(98,100); }
|
|
||||||
void word_UHR(){ setRange(101,103); }
|
|
||||||
|
|
||||||
String getTimeText(int h, int m){
|
String getTimeText(int h, int m){
|
||||||
int m5 = (m/5)*5;
|
int m5 = (m/5)*5;
|
||||||
@@ -411,21 +265,10 @@ void showTimeWords(int h, int m){
|
|||||||
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
|
else if(hh == 7) word_SIEBEN(); else if(hh == 8) word_ACHT(); else if(hh == 9) word_NEUN();
|
||||||
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
|
else if(hh == 10) word_ZEHN_H(); else if(hh == 11) word_ELF(); else word_ZWOELF();
|
||||||
|
|
||||||
applyStatus();
|
|
||||||
|
|
||||||
if(forceInstant){ showInstant(); forceInstant = false; }
|
if(forceInstant){ showInstant(); forceInstant = false; }
|
||||||
else { fadeToTargetSmooth(20, 10); }
|
else { fadeToTargetSmooth(20, 10); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solange noch keine Zeit bekannt ist, nur Hintergrund und Statuswoerter zeigen
|
|
||||||
void showStatusOnly(){
|
|
||||||
for(int i=0;i<NUMPIXELS;i++) currentBuffer[i] = targetBuffer[i];
|
|
||||||
uint32_t activeBg = backgroundMode ? bgColor : 0;
|
|
||||||
for(int i=0;i<NUMPIXELS;i++) targetBuffer[i] = activeBg;
|
|
||||||
applyStatus();
|
|
||||||
showInstant();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================= WEB HANDLERS =================
|
// ================= WEB HANDLERS =================
|
||||||
String jsonEscape(const String &s){
|
String jsonEscape(const String &s){
|
||||||
String out;
|
String out;
|
||||||
@@ -470,9 +313,7 @@ void handleStatus(){
|
|||||||
json += "\"nightStart\":" + String(nightStart) + ",";
|
json += "\"nightStart\":" + String(nightStart) + ",";
|
||||||
json += "\"nightEnd\":" + String(nightEnd) + ",";
|
json += "\"nightEnd\":" + String(nightEnd) + ",";
|
||||||
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
|
json += "\"bg\":" + String(backgroundMode ? 1 : 0) + ",";
|
||||||
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0) + ",";
|
json += "\"nightBgOff\":" + String(nightBackgroundDisabled ? 1 : 0);
|
||||||
json += "\"statusLeds\":" + String(statusLeds ? 1 : 0) + ",";
|
|
||||||
json += "\"ntpShowSec\":" + String(ntpShowSec);
|
|
||||||
json += "}";
|
json += "}";
|
||||||
server.send(200, "application/json", json);
|
server.send(200, "application/json", json);
|
||||||
}
|
}
|
||||||
@@ -510,10 +351,6 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
|||||||
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
|
<input type='range' id='nbr' min='0' max='255' oninput="f('/nightBrightness?val='+this.value)">
|
||||||
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
|
<input type='checkbox' id='nightBgOff' onchange="f('/nightBgOff?val='+(this.checked?1:0))"> Hintergrund in Nacht aus
|
||||||
</div>
|
</div>
|
||||||
<div class='card'>
|
|
||||||
<input type='checkbox' id='sl' onchange="f('/statusLeds?val='+(this.checked?1:0))"> <b>Status-Anzeige</b> (NTP grün, RTC gelb, ESP rot)<br><br>
|
|
||||||
NTP-Anzeige mind. <input type='number' id='nsec' min='1' max='60' style='width:50px' oninput="if(this.value!=='')f('/ntpShowSec?val='+this.value)"> Sekunden
|
|
||||||
</div>
|
|
||||||
<div class='card'>
|
<div class='card'>
|
||||||
<label>NTP Server:</label>
|
<label>NTP Server:</label>
|
||||||
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
|
<input type='text' id='ntpS' onchange="f('/setNTP?val='+encodeURIComponent(this.value))">
|
||||||
@@ -530,7 +367,7 @@ static const char INDEX_HTML[] PROGMEM = R"rawliteral(
|
|||||||
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
|
const s=(i,v,c=false)=>{const e=document.getElementById(i);if(e&&document.activeElement!==e){if(c)e.checked=(v==1);else e.value=v;}};
|
||||||
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
|
s('col',d.color);s('br',d.brightness);s('night',d.night,true);s('nS',d.nightStart);s('nE',d.nightEnd);
|
||||||
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
|
s('nbr',d.nightBrightness);s('bg',d.bg,true);s('bgcol',d.bgColor);s('ntpS',d.ntpServer);s('tzS',d.tzString);
|
||||||
s('nightBgOff',d.nightBgOff,true);s('sl',d.statusLeds,true);s('nsec',d.ntpShowSec);
|
s('nightBgOff',d.nightBgOff,true);
|
||||||
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
|
}).catch(()=>{document.getElementById('status').innerText='Verbindung unterbrochen...';})}
|
||||||
setInterval(u, 2500); u();
|
setInterval(u, 2500); u();
|
||||||
</script></body></html>
|
</script></body></html>
|
||||||
@@ -560,8 +397,6 @@ void handleNightStart(){ int v; if(!argInRange(0,23,v)) return; nightStart=v; fo
|
|||||||
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightEnd(){ int v; if(!argInRange(0,23,v)) return; nightEnd=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightBrightness(){ int v; if(!argInRange(0,255,v)) return; nightBrightness=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleNightBgOff(){ int v; if(!argInRange(0,1,v)) return; nightBackgroundDisabled=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
void handleStatusLeds(){ int v; if(!argInRange(0,1,v)) return; statusLeds=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
|
||||||
void handleNtpShowSec(){ int v; if(!argInRange(1,60,v)) return; ntpShowSec=v; sendOK(); }
|
|
||||||
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
void handleBG(){ int v; if(!argInRange(0,1,v)) return; backgroundMode=v; forceInstant=true; updateNeeded=true; sendOK(); }
|
||||||
|
|
||||||
void handleNTP(){
|
void handleNTP(){
|
||||||
@@ -587,9 +422,7 @@ void setup(){
|
|||||||
pixels.clear();
|
pixels.clear();
|
||||||
pixels.show();
|
pixels.show();
|
||||||
|
|
||||||
settimeofday_cb(onTimeSet);
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
initRtc();
|
|
||||||
|
|
||||||
WiFiManager wm;
|
WiFiManager wm;
|
||||||
wm.autoConnect("Wortuhr");
|
wm.autoConnect("Wortuhr");
|
||||||
@@ -605,8 +438,6 @@ void setup(){
|
|||||||
server.on("/nightBrightness", handleNightBrightness);
|
server.on("/nightBrightness", handleNightBrightness);
|
||||||
server.on("/nightBgOff", handleNightBgOff);
|
server.on("/nightBgOff", handleNightBgOff);
|
||||||
server.on("/bg", handleBG);
|
server.on("/bg", handleBG);
|
||||||
server.on("/statusLeds", handleStatusLeds);
|
|
||||||
server.on("/ntpShowSec", handleNtpShowSec);
|
|
||||||
server.on("/setNTP", handleNTP);
|
server.on("/setNTP", handleNTP);
|
||||||
server.on("/setTZ", handleTZ);
|
server.on("/setTZ", handleTZ);
|
||||||
server.on("/save", handleSave);
|
server.on("/save", handleSave);
|
||||||
@@ -615,14 +446,9 @@ void setup(){
|
|||||||
|
|
||||||
void loop(){
|
void loop(){
|
||||||
server.handleClient();
|
server.handleClient();
|
||||||
updateTimeSources();
|
|
||||||
if(statusGeaendert()){ forceInstant = true; updateNeeded = true; }
|
|
||||||
|
|
||||||
// Ohne bekannte Zeit nur die Statuswoerter anzeigen
|
// Erst anzeigen, wenn die Zeit per NTP synchronisiert ist
|
||||||
if(!timeSynced()){
|
if(!timeSynced()) return;
|
||||||
if(updateNeeded){ updateNeeded = false; forceInstant = false; showStatusOnly(); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
struct tm *t = localtime(&now);
|
struct tm *t = localtime(&now);
|
||||||
|
|||||||
Reference in new issue
Block a user