241 lines
7.3 KiB
C
241 lines
7.3 KiB
C
// Zeit-bezogene Variablen und Includes
|
|
#pragma once
|
|
#include <Arduino.h>
|
|
#include <ArduinoJson.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
// Globale Zeitvariablen
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
time_t now;
|
|
struct tm timeinfo;
|
|
|
|
// Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein.
|
|
void setupTimeAPI(AsyncWebServer &server);
|
|
// Gibt die aktuelle Zeit als JSON-String zurück.
|
|
String getCurrentTimeJSON();
|
|
// Setzt die Systemzeit auf den angegebenen Zeitstempel (Sekunden seit 1970).
|
|
bool setSystemTime(long timestamp);
|
|
|
|
// Hilfsfunktion: Gibt die aktuelle Zeit als JSON-String zurück.
|
|
String getCurrentTimeJSON() {
|
|
gettimeofday(&tv, &tz);
|
|
now = tv.tv_sec;
|
|
|
|
StaticJsonDocument<200> doc;
|
|
doc["timestamp"] = (long)now;
|
|
doc["success"] = true;
|
|
|
|
// Zusätzliche Zeitinformationen
|
|
gmtime_r(&now, &timeinfo);
|
|
char timeStr[64];
|
|
strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", &timeinfo);
|
|
doc["formatted"] = String(timeStr);
|
|
doc["year"] = timeinfo.tm_year + 1900;
|
|
doc["month"] = timeinfo.tm_mon + 1;
|
|
doc["day"] = timeinfo.tm_mday;
|
|
doc["hour"] = timeinfo.tm_hour;
|
|
doc["minute"] = timeinfo.tm_min;
|
|
doc["second"] = timeinfo.tm_sec;
|
|
|
|
String response;
|
|
serializeJson(doc, response);
|
|
return response;
|
|
}
|
|
|
|
void syncTimeWithNTP(const char *ntpServer = "pool.ntp.org",
|
|
long gmtOffset_sec = 3600, int daylightOffset_sec = 0) {
|
|
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
|
Serial.println("Warte auf NTP-Zeit (max 5s)...");
|
|
struct tm timeinfo;
|
|
unsigned long start = millis();
|
|
bool synced = false;
|
|
while (millis() - start < 5000) {
|
|
if (getLocalTime(&timeinfo, 10)) { // 10ms Timeout pro Versuch
|
|
synced = true;
|
|
break;
|
|
}
|
|
delay(10); // Kurze Pause, damit der Loop nicht blockiert
|
|
}
|
|
if (synced) {
|
|
Serial.println("\nNTP-Zeit synchronisiert!");
|
|
Serial.printf("Aktuelle Zeit: %02d:%02d:%02d\n", timeinfo.tm_hour,
|
|
timeinfo.tm_min, timeinfo.tm_sec);
|
|
} else {
|
|
Serial.println("\nNTP-Sync fehlgeschlagen (Timeout nach 5s)");
|
|
}
|
|
}
|
|
|
|
// Hilfsfunktion: Setzt die Systemzeit auf den angegebenen Zeitstempel.
|
|
bool setSystemTime(long timestamp) {
|
|
struct timeval tv;
|
|
tv.tv_sec = timestamp;
|
|
tv.tv_usec = 0;
|
|
|
|
if (settimeofday(&tv, NULL) == 0) {
|
|
Serial.println("Zeit erfolgreich gesetzt: " + String(timestamp));
|
|
return true;
|
|
} else {
|
|
Serial.println("Fehler beim Setzen der Zeit");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein.
|
|
void setupTimeAPI(AsyncWebServer &server) {
|
|
|
|
// API-Endpunkt: Aktuelle Zeit abrufen
|
|
server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
String response = getCurrentTimeJSON();
|
|
request->send(200, "application/json", response);
|
|
});
|
|
|
|
// API-Endpunkt: Zeit setzen
|
|
server.on("/api/set-time", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
StaticJsonDocument<100> doc;
|
|
|
|
if (request->hasParam("timestamp", true)) {
|
|
String timestampStr = request->getParam("timestamp", true)->value();
|
|
long timestamp = timestampStr.toInt();
|
|
|
|
if (timestamp > 0) {
|
|
bool success = setSystemTime(timestamp);
|
|
|
|
doc["success"] = success;
|
|
if (success) {
|
|
doc["message"] = "Zeit erfolgreich gesetzt";
|
|
doc["timestamp"] = timestamp;
|
|
} else {
|
|
doc["message"] = "Fehler beim Setzen der Zeit";
|
|
}
|
|
} else {
|
|
doc["success"] = false;
|
|
doc["message"] = "Ungültiger Timestamp";
|
|
}
|
|
} else {
|
|
doc["success"] = false;
|
|
doc["message"] = "Timestamp-Parameter fehlt";
|
|
}
|
|
|
|
String response;
|
|
serializeJson(doc, response);
|
|
request->send(200, "application/json", response);
|
|
});
|
|
|
|
// Alternative Implementierung für manuelle Datum/Zeit-Eingabe
|
|
server.on("/api/set-datetime", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
StaticJsonDocument<150> doc;
|
|
|
|
if (request->hasParam("year", true) && request->hasParam("month", true) &&
|
|
request->hasParam("day", true) && request->hasParam("hour", true) &&
|
|
request->hasParam("minute", true) &&
|
|
request->hasParam("second", true)) {
|
|
|
|
struct tm timeinfo;
|
|
timeinfo.tm_year =
|
|
request->getParam("year", true)->value().toInt() - 1900;
|
|
timeinfo.tm_mon = request->getParam("month", true)->value().toInt() - 1;
|
|
timeinfo.tm_mday = request->getParam("day", true)->value().toInt();
|
|
timeinfo.tm_hour = request->getParam("hour", true)->value().toInt();
|
|
timeinfo.tm_min = request->getParam("minute", true)->value().toInt();
|
|
timeinfo.tm_sec = request->getParam("second", true)->value().toInt();
|
|
|
|
time_t timestamp = mktime(&timeinfo);
|
|
|
|
if (timestamp != -1) {
|
|
bool success = setSystemTime(timestamp);
|
|
|
|
doc["success"] = success;
|
|
if (success) {
|
|
doc["message"] = "Zeit erfolgreich gesetzt";
|
|
doc["timestamp"] = (long)timestamp;
|
|
} else {
|
|
doc["message"] = "Fehler beim Setzen der Zeit";
|
|
}
|
|
} else {
|
|
doc["success"] = false;
|
|
doc["message"] = "Ungültiges Datum/Zeit";
|
|
}
|
|
} else {
|
|
doc["success"] = false;
|
|
doc["message"] = "Datum/Zeit-Parameter fehlen";
|
|
}
|
|
|
|
String response;
|
|
serializeJson(doc, response);
|
|
request->send(200, "application/json", response);
|
|
});
|
|
|
|
// Erweiterte Zeit-Informationen (optional)
|
|
server.on("/api/time/info", HTTP_GET, [](AsyncWebServerRequest *request) {
|
|
gettimeofday(&tv, &tz);
|
|
now = tv.tv_sec;
|
|
gmtime_r(&now, &timeinfo);
|
|
|
|
StaticJsonDocument<400> doc;
|
|
doc["timestamp"] = (long)now;
|
|
doc["uptime"] = millis();
|
|
|
|
// Formatierte Zeitstrings
|
|
char buffer[64];
|
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo);
|
|
doc["date"] = String(buffer);
|
|
|
|
strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
|
|
doc["time"] = String(buffer);
|
|
|
|
strftime(buffer, sizeof(buffer), "%A", &timeinfo);
|
|
doc["weekday"] = String(buffer);
|
|
|
|
strftime(buffer, sizeof(buffer), "%B", &timeinfo);
|
|
doc["month_name"] = String(buffer);
|
|
|
|
// Zusätzliche Infos
|
|
doc["day_of_year"] = timeinfo.tm_yday + 1;
|
|
doc["week_of_year"] = (timeinfo.tm_yday + 7 - timeinfo.tm_wday) / 7;
|
|
doc["is_dst"] = timeinfo.tm_isdst;
|
|
|
|
String response;
|
|
serializeJson(doc, response);
|
|
request->send(200, "application/json", response);
|
|
});
|
|
Serial.println("Zeit-API initialisiert");
|
|
}
|
|
|
|
// Hilfsfunktion: Zeit-Validierung
|
|
// Prüft, ob die übergebenen Datums- und Zeitwerte gültig sind.
|
|
bool isValidDateTime(int year, int month, int day, int hour, int minute,
|
|
int second) {
|
|
if (year < 2020 || year > 2099)
|
|
return false;
|
|
if (month < 1 || month > 12)
|
|
return false;
|
|
if (day < 1 || day > 31)
|
|
return false;
|
|
if (hour < 0 || hour > 23)
|
|
return false;
|
|
if (minute < 0 || minute > 59)
|
|
return false;
|
|
if (second < 0 || second > 59)
|
|
return false;
|
|
|
|
// Erweiterte Validierung für Monatstage
|
|
int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
// Schaltjahr-Prüfung
|
|
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
|
|
daysInMonth[1] = 29;
|
|
}
|
|
|
|
return day <= daysInMonth[month - 1];
|
|
}
|
|
|
|
// Gibt den aktuellen Zeitstempel in Millisekunden seit 1970 zurück.
|
|
uint64_t getCurrentTimestampMs() {
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL;
|
|
}
|