Hallo zusammen,
ich bin glaube ich kein völliger Anfänger mehr im Arduino Umfeld, Wissen fehlt mir aber trotzdem noch einiges. Trotzdem bin ich auf meine bisherige Arbeit mehr als Stolz.
Leider verzweifle ich gerade und komme nicht mehr weiter. Oder anders gesagt: Ich bin mit meinem Latein echt am Ende ![]()
Ich suche deshalb bei euch nach Hilfe und hoffe hier findet sich jemand/oder mehrere, die mir helfen können. Vielleicht ja @fony?
Was macht mein Projekt:
Es ist eine Steuerung für ein Balkonkraftwerk und funktioniert mittlerweile so wie es soll. Die Motorpositionen werden entsprechend der aktuellen Uhrzeit angesteuert, der Windsensor regelt ebenfalls und mittlerweile ist sogar via MQTT eine Steuerung über Homeassistant und App machbar. Soweit so gut.
Mein Problem ist, dass der Sketch einmal am Tag einfach stehen bleibt. Die Zeit, wann dies geschieht ist dabei immer eine andere. Heute war es bereits nach zwei Stunden der Fall, gestern erst nach 13 Stunden. Ich kann also nicht sagen, es hakt immer an der gleichen Stelle oder Uhrzeit.
Der Arduino UNO Wifi Rev2 wird jeden Tag um 06.00 Uhr gestartet und um 22.00 Uhr wieder abgeschaltet (Zeitschaltuhr).
Ich habe versucht in meinem Sketch einen Abbruch der WLAN-Verbindung abzufangen, die Zeit regelmäßig zu aktualisieren um Abstürze zu minimieren.
Ich finde aber keinen Weg, dies zu verhindern (das Hängenbleiben des Sketch)
Habe ich ggf. irgendwo Mist im Code drin, der so etwas verursachen könnte?
Ich hoffe ihr könnt mir helfen.
Anbei mein Sketch. Ich hoffe es ist verständlich. Ich habe zumindest immer versucht entsprechend zu kommentieren.
#include <WiFiNINA.h> // Bibliothek für die WLAN-Kommunikation mit dem Arduino NINA-WiFi-Modul
#include <AccelStepper.h> // Bibliothek für die Steuerung von Schrittmotoren mit Beschleunigung
#include <TimeLib.h> // Bibliothek für die Manipulation und Abfrage von Zeitinformationen
#include <WiFiUdp.h> // Bibliothek für die Kommunikation über UDP/IP-Protokoll im WLAN
#include <PubSubClient.h> // MQTT-Bibliothek für die Verbindung und Kommunikation mit einem MQTT-Broker
#include <ArduinoJson.h> // Bibliothek für die Bearbeitung von JSON-Daten
#include "secrets.h" // Header-Datei für geheime Zugangsdaten (z. B. WLAN-SSID, Passwort, MQTT-Benutzername und -Passwort)
// Pin-Belegungen für den Schrittmotor
#define dirPin 2 // Richtungs-Pin des Schrittmotors
#define stepPin 4 // Schritt-Pin des Schrittmotors
#define motorInterfaceType 1 // Art der Motorsteuerung (1 für Treiber wie A4988 oder DRV8825)
#define PIN_BUTTON 12 // Pin für den Windsensor
// Intervall für die Überprüfung der WLAN-Verbindung (10 Minuten)
#define WIFI_CHECK_INTERVAL 600000
// Zugangsdaten für WLAN und MQTT
const char* mqtt_server = "192.168.2.2"; // IP-Adresse oder Domain des MQTT-Brokers
const int mqtt_port = 1883; // MQTT-Port (standardmäßig 1883)
const char* mqtt_user = MQTT_USER; // MQTT-Benutzername
const char* mqtt_password = MQTT_PASSWORD; // MQTT-Passwort
char ssid[] = WIFI_SSID; // WLAN-SSID
char pass[] = WIFI_PASSWORD; // WLAN-Passwort
// Zeit- und Zeitstempelvariablen
unsigned long lastUpdateTime = 0; // Variable, um die Zeit des letzten Updates zu speichern
const int timeZoneOffset = 1; // Zeitverschiebung für die Winterzeit (UTC+1)
int timeZone = 2; // Zeitzone für die Umrechnung von lokaler Zeit in UTC
unsigned long currentTimestamp = 0; // Variable für den aktuellen Zeitstempel
time_t currentTime; // Variable für die aktuelle Zeit
// Variablen für den Windschutz
unsigned long WindschutzTimer; // Variable zur Speicherung der Zeit, seit der Windschutz aktiviert wurde
unsigned long WindWartezeit_ms = 900000; // Zeitdauer in Millisekunden, nach dem der Windschutz automatisch deaktiviert wird (900000 ms = 15 Minuten)
boolean Warteposition_aktiv = false; // Flag, das angibt, ob sich der Mechanismus in der Warteposition befindet
// Variable zur Speicherung des Zeitpunkts des letzten Sendens von Daten
unsigned long lastSendTime = 0;
//Position des Schrittmotors
const long Startposition = 17000;
const long Vormittag1 = 9000;
const long Vormittag2 = 2000;
const long Mittag1 = 0;
const long Mittag2 = -6000;
const long Nachmittag1 = -12000;
const long Nachmittag2 = -15000;
const long Nachmittag3 = -18000;
const long Endposition = -24000;
const long Windschutz = 0;
AccelStepper stepper(motorInterfaceType, stepPin, dirPin); // Initialisierung des Schrittmotors
WiFiUDP ntpUDP; // UDP-Objekt für die NTP-Zeitabfrage
IPAddress timeServerIP; // IP-Adresse des Zeit-Servers für die NTP-Synchronisation
WiFiUDP udp; // UDP-Objekt für die Kommunikation
WiFiClient wifiClient; // WiFi-Client-Objekt für die Verbindung zum MQTT-Broker
PubSubClient client(wifiClient); // MQTT-Client-Objekt für die MQTT-Kommunikation
WiFiServer server(80); // WiFi-Server-Objekt für die Verarbeitung von HTTP-Anfragen
unsigned long wifiCheckTimer = 0; // Timer für die Überprüfung der WLAN-Verbindung
// Funktion zur Überprüfung, ob Sommerzeit aktiv ist
bool isDaylightSavingTime(time_t currentTime);
// Funktion zur Ermittlung des letzten Sonntags im Monat
int getLastSundayDay(int year, int month);
// Überprüfung, ob Sommerzeit aktiv ist
bool isDaylightSavingTime(time_t currentTime) {
int currentYear = year(currentTime);
int currentMonth = month(currentTime);
int currentDay = day(currentTime);
// Ermittlung des letzten Sonntags im März
int lastSundayMarch = getLastSundayDay(currentYear, 3);
// Ermittlung des letzten Sonntags im Oktober
int lastSundayOctober = getLastSundayDay(currentYear, 10);
// Sommerzeit beginnt am letzten Sonntag im März und endet am letzten Sonntag im Oktober
bool isDST = ((currentMonth > 3 && currentMonth < 10) || (currentMonth == 3 && currentDay > lastSundayMarch) || (currentMonth == 10 && currentDay <= lastSundayOctober));
return isDST;
}
// Ermittlung des letzten Sonntags im Monat
int getLastSundayDay(int year, int month) {
const int lastDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // Anzahl Tage in jedem Monat
int lastSunday = lastDays[month - 1]; // Annahme: Monat hat maximal 31 Tage
tmElements_t timeInfo;
// Suche den letzten Sonntag des Monats
while (lastSunday > 0) {
timeInfo.Year = year - 1970; // Jahresversatz von 1970
timeInfo.Month = month;
timeInfo.Day = lastSunday;
time_t time = makeTime(timeInfo);
int dayOfWeek = weekday(time);
if (dayOfWeek == 1) { // Sonntag hat den Wert 1, unabhängig davon, wann die Woche beginnt
return lastSunday;
}
lastSunday--;
}
return -1; // Fehler, falls der letzte Sonntag nicht gefunden wird
}
/**
* Initialisierung der Mikrocontroller-Hardware und Einrichtung der Kommunikationsprotokolle.
*/
void setup() {
// Initialisierung der seriellen Kommunikation
Serial.begin(9600);
while (!Serial)
; // Warte, bis die serielle Kommunikation gestartet ist
// Konfiguration des Schrittmotors
stepper.setMaxSpeed(1400);
stepper.setAcceleration(800);
// Konfiguration der Pins
pinMode(PIN_BUTTON, INPUT_PULLUP); // Pin für den Windsensor als Eingang mit Pull-Up-Widerstand konfigurieren
pinMode(LED_BUILTIN, OUTPUT); // Pin für die eingebaute LED als Ausgang konfigurieren
// WLAN-Verbindung herstellen
connectWiFi();
// Zeit synchronisieren
setSyncProvider(getNtpTime);
// HTTP-Server starten
server.begin();
// Verbindung zum MQTT-Broker herstellen
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
if (!client.connected()) {
reconnect();
}
// Abonnement für das gewünschte Topic einrichten
client.subscribe("solar/bkw-steuerung/app-steuerung");
// Autodiscovery-Nachrichten senden
sendMQTTAutoDiscovery();
}
/**
* Die Haupt-Loop-Funktion, die wiederholt ausgeführt wird.
* Überprüft die WLAN-Verbindung, den Windsensor und aktualisiert die NTP-Zeit.
* Sendet MQTT-Daten an Home Assistant und verarbeitet MQTT-Nachrichten.
*/
void loop() {
// Überprüfe die WLAN-Verbindung
checkWiFiConnection();
// Überprüfe den Zustand des Windsensors
checkWindsensor();
// Sende MQTT-Daten an Home Assistant
sendMQTTData();
// Aktualisiere die MQTT-Verbindung und verarbeite eingehende Nachrichten
mqttLoop();
// Aktualisiere die NTP-Zeit
updateNtpTime();
}
/**
* ###########################################
* Bereich für WLAN-Verbindung und Zeitupdates
* ###########################################
*/
/**
* Stellt eine Verbindung zum WLAN her.
* Führt mehrere Verbindungsversuche durch und gibt eine Fehlermeldung aus, wenn die Verbindung fehlschlägt.
*/
void connectWiFi() {
int connectAttempts = 0; // Anzahl der Verbindungsversuche
// Führe bis zu 10 Verbindungsversuche durch
while (connectAttempts < 10) {
WiFi.begin(ssid, pass); // WLAN-Verbindung mit den angegebenen Zugangsdaten herstellen
unsigned long startTime = millis(); // Startzeit für den Verbindungsversuch
// Warte bis zur erfolgreichen Verbindung oder bis 30 Sekunden vergangen sind
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 30000) {
delay(1000); // Warte 1 Sekunde vor jedem erneuten Verbindungsversuch
}
// Wenn die Verbindung erfolgreich hergestellt wurde
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi verbunden");
IPAddress ip = WiFi.localIP();
//Serial.print("IP-Adresse: ");
Serial.println(ip);
return; // Verbindung erfolgreich, beende die Funktion
}
// Wenn die Verbindung fehlschlägt, trenne die Verbindung und warte vor dem nächsten Versuch
WiFi.disconnect();
delay(30000); // Warte 30 Sekunden vor dem nächsten Verbindungsversuch
connectAttempts++; // Erhöhe die Anzahl der Verbindungsversuche
}
// Wenn alle Verbindungsversuche fehlschlagen, gebe eine Fehlermeldung aus
Serial.println("WiFi-Verbindung fehlgeschlagen");
}
/**
* Überprüft die WLAN-Verbindung und stellt sie gegebenenfalls neu her.
*/
void checkWiFiConnection() {
if (millis() - wifiCheckTimer >= WIFI_CHECK_INTERVAL) { // Überprüfe das WLAN-Verbindungsintervall
wifiCheckTimer = millis(); // Timer zurücksetzen
// Überprüfe die WLAN-Verbindung
if (WiFi.status() != WL_CONNECTED) { // Wenn keine Verbindung besteht
Serial.println("Verbindung verloren. Trenne WLAN-Verbindung und stelle neu her.");
// Trenne WLAN-Verbindung
WiFi.disconnect();
// Warte, um sicherzustellen, dass die Verbindung getrennt wurde
delay(1000);
// Stelle die WLAN-Verbindung neu her
connectWiFi();
} else { // Wenn eine Verbindung besteht
Serial.println("WLAN-Verbindung aktiv.");
}
}
}
/**
* Holt die aktuelle Zeit von einem NTP-Zeitserver.
*
* @return Die aktuelle Zeit als Unix-Zeitstempel (time_t)
*/
time_t getNtpTime() {
// Warte, bis eine WLAN-Verbindung hergestellt ist
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
}
// Ermittle die IP-Adresse des NTP-Zeitservers ("pool.ntp.org")
WiFi.hostByName("pool.ntp.org", timeServerIP);
Serial.println("Zeitserver IP-Adresse erhalten");
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
// Initialisiere das NTP-Paket
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011;
packetBuffer[1] = 0;
packetBuffer[2] = 6;
packetBuffer[3] = 0xEC;
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
WiFiUDP udp;
udp.begin(123); // Starte die UDP-Kommunikation auf Port 123
udp.beginPacket(timeServerIP, 123); // Beginne ein UDP-Paket zum NTP-Zeitserver
udp.write(packetBuffer, NTP_PACKET_SIZE); // Sende das NTP-Paket
udp.endPacket(); // Beende das UDP-Paket
// Warte auf eine Antwort vom Zeitserver
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
udp.read(packetBuffer, NTP_PACKET_SIZE);
// Extrahiere die Sekunden seit 1900 aus dem NTP-Paket
unsigned long secsSince1900 = (unsigned long)packetBuffer[40] << 24 | (unsigned long)packetBuffer[41] << 16 | (unsigned long)packetBuffer[42] << 8 | (unsigned long)packetBuffer[43];
// Berechne den Unix-Zeitstempel (Anzahl der Sekunden seit 1970) aus den Sekunden seit 1900
time_t ntpTime = secsSince1900 - 2208988800UL;
// Führe die Sommerzeit-Korrektur durch, falls erforderlich
ntpTime += (isDaylightSavingTime(ntpTime) ? 3600 : 0);
// Füge die Zeitzonenverschiebung hinzu
ntpTime += (timeZoneOffset * 3600);
// Gib die aktuelle Zeit zurück
return ntpTime;
}
delay(10);
}
// Keine Antwort vom Zeitserver erhalten
Serial.println("Keine Antwort vom Zeitserver");
return 0; // Gib 0 zurück, um anzuzeigen, dass die Zeit nicht erfolgreich abgerufen wurde
}
/**
* Aktualisiert die Systemzeit durch Synchronisierung mit einem NTP-Zeitserver.
* Die Zeit wird alle 65 Minuten synchronisiert, sofern keine vorherige Synchronisierung erfolgt ist.
*/
void updateNtpTime() {
unsigned long currentMillis = millis(); // Erfasse die aktuelle Zeit in Millisekunden
static unsigned long previousSyncMillis = 0; // Speichere den Zeitpunkt des letzten Syncs
const unsigned long syncInterval = 3900000; // Synchronisiere die Zeit alle 65 Minuten
// Überprüfe, ob es Zeit ist, die Zeit zu synchronisieren
if (currentMillis - previousSyncMillis >= syncInterval || previousSyncMillis == 0) {
// Wenn die Zeitdifferenz seit dem letzten Sync größer oder gleich dem Synchronisierungsintervall ist oder wenn noch keine vorherige Synchronisierung erfolgt ist
// Aktualisiere die Zeit durch Aufrufen der getNtpTime() Funktion als Sync-Provider
setSyncProvider(getNtpTime);
previousSyncMillis = currentMillis; // Aktualisiere den Zeitpunkt des letzten Syncs auf die aktuelle Zeit
}
}
/**
* ###########################################
* Bereich für Motorsteuerung
* ###########################################
*/
/**
* Bewegt den Schrittmotor auf die angegebene Position, falls die aktuelle Position nicht bereits erreicht ist.
* @param position Die Zielposition, zu der der Schrittmotor bewegt werden soll.
*/
void moveStepper(int position) {
if (stepper.currentPosition() != position) {
stepper.moveTo(position); // Setzt die Zielposition des Schrittmotors
stepper.runToPosition(); // Führt den Schrittmotor zur Zielposition aus
}
}
/**
* Aktualisiert die Position des Schrittmotors basierend auf der aktuellen Zeit und den definierten Schwellenwerten für Sommer-, Winter- und Zwischenpositionen.
* Die Funktion bestimmt die Zielposition des Schrittmotors anhand der aktuellen Uhrzeit und des Monats. Je nach Monat werden unterschiedliche Schwellenwerte verwendet, um zwischen Sommer-, Winter- und Zwischenpositionen zu unterscheiden.
* Die Position wird entsprechend den definierten Schwellenwerten festgelegt, und der Schrittmotor wird auf die berechnete Position bewegt.
*/
// Diese Funktion aktualisiert die Position des Steppermotors basierend auf der aktuellen Zeit
void managePositions() {
currentTime = now(); // Aktualisiere den Wert der globalen Variable currentTime
int currentMonth = month(currentTime);
int currentDay = day(currentTime);
int currentHour = hour(currentTime);
int currentMinute = minute(currentTime);
int currentSecond = second(currentTime);
int currentTimeInSeconds = currentHour * 3600 + currentMinute * 60;
//Debugging
//Serial.print("Zeit: ");
//Serial.print(day(currentTime));
//Serial.print(".");
//Serial.print(month(currentTime));
//Serial.print(".");
//Serial.print(year(currentTime));
//Serial.print(" ");
// Führende Null für einstellige Stunden
//Serial.print(currentHour < 10 ? "0" + String(currentHour) : String(currentHour));
//Serial.print(":");
// Führende Null für einstellige Minuten
//Serial.print(currentMinute < 10 ? "0" + String(currentMinute) : String(currentMinute));
//Serial.print(":");
// Führende Null für einstellige Sekunden
//Serial.println(currentSecond < 10 ? "0" + String(currentSecond) : String(currentSecond));
// Überprüfe, ob Sommerzeit oder Winterzeit aktiv ist
bool isDST = isDaylightSavingTime(currentTime);
// Struktur zur Speicherung von Schwellenwerten für Positionen
struct PositionThreshold {
int thresholdHour;
int thresholdMinute;
long position;
};
// Definition von Schwellenwerten für Sommerpositionen
PositionThreshold PositionenSommer[] = {
{ 6, 0, Startposition },
{ 11, 0, Vormittag1 },
{ 12, 0, Vormittag2 },
{ 12, 30, Mittag1 },
{ 13, 0, Mittag2 },
{ 13, 30, Nachmittag1 },
{ 14, 0, Nachmittag2 },
{ 15, 0, Nachmittag3 },
{ 16, 0, Endposition },
{ 21, 0, Windschutz }
};
// Definition von Schwellenwerten für Winterpositionen
PositionThreshold PositionenWinter[] = {
{ 8, 0, Vormittag1 },
{ 10, 30, Vormittag2 },
{ 12, 0, Mittag1 },
{ 12, 30, Mittag2 },
{ 13, 0, Nachmittag1 },
{ 14, 0, Nachmittag2 },
{ 15, 0, Nachmittag3 },
{ 17, 30, Windschutz }
};
// Definition von Schwellenwerten für Zwischenpositionen
PositionThreshold PositionenZwischen[] = {
{ 7, 0, Startposition },
{ 9, 0, Vormittag1 },
{ 11, 0, Vormittag2 },
{ 12, 0, Mittag1 },
{ 13, 0, Mittag2 },
{ 14, 0, Nachmittag1 },
{ 15, 0, Nachmittag2 },
{ 16, 0, Nachmittag3 },
{ 17, 0, Endposition },
{ 18, 30, Windschutz }
};
int numThresholds;
PositionThreshold* thresholds;
// Bestimme die Schwellenwerte basierend auf dem aktuellen Monat
if (currentMonth == 1 || currentMonth == 2 || currentMonth == 11 || currentMonth == 12) {
thresholds = PositionenWinter;
numThresholds = sizeof(PositionenWinter) / sizeof(PositionenWinter[0]);
} else if (currentMonth == 3 || currentMonth == 4 || currentMonth == 9 || currentMonth == 10) {
thresholds = PositionenZwischen;
numThresholds = sizeof(PositionenZwischen) / sizeof(PositionenZwischen[0]);
} else if (currentMonth >= 5 && currentMonth <= 8) {
thresholds = PositionenSommer;
numThresholds = sizeof(PositionenSommer) / sizeof(PositionenSommer[0]);
} else {
// Standardpositionen oder eine andere Logik hier einfügen, wenn erforderlich
thresholds = PositionenSommer;
numThresholds = sizeof(PositionenSommer) / sizeof(PositionenSommer[0]);
}
long position = Startposition; // Standardposition
// Durchlaufe die Schwellenwerte und bestimme die aktuelle Position
for (int i = 0; i < numThresholds; i++) {
if (currentHour > thresholds[i].thresholdHour || (currentHour == thresholds[i].thresholdHour && currentMinute >= thresholds[i].thresholdMinute)) {
position = thresholds[i].position;
} else {
break; // Schleife abbrechen, da der Rest der Schwellenwerte nicht mehr relevant ist
}
}
// Bewege den Steppermotor zur berechneten Position
moveStepper(position);
// Drucke den aktuellen Status der Positionen basierend auf dem Monat
if (currentMonth == 1 || currentMonth == 2 || currentMonth == 11 || currentMonth == 12) {
//Serial.println("Winterpositionen");
} else if (currentMonth == 3 || currentMonth == 4 || currentMonth == 9 || currentMonth == 10) {
//Serial.println("Zwischenpositionen");
} else if (currentMonth >= 5 && currentMonth <= 8) {
//Serial.println("Sommerpositionen");
} else {
//Serial.println("Standardpositionen"); // Standardpositionen oder eine andere Bezeichnung hier einfügen, wenn erforderlich
}
// Drucke die aktuelle Position des Steppermotors
//Serial.print("Position: ");
//Serial.println(stepper.currentPosition());
}
/**
* Überprüft den Windsensor und aktualisiert die Position des Schrittmotors entsprechend.
* Wenn der Windsensor aktiviert ist, wird der Schrittmotor in die Windschutzposition bewegt.
* Nach Ablauf der definierten Wartezeit wird der Windsensor deaktiviert.
* Wenn der Windsensor nicht aktiv ist, wird die Funktion `managePositions()` aufgerufen, um die Position des Schrittmotors basierend auf der aktuellen Zeit zu aktualisieren.
*/
void checkWindsensor() {
// Überprüfe den Zustand des Windsensors
if (digitalRead(PIN_BUTTON) == HIGH) {
Warteposition_aktiv = true; // Setze den Wartepositionsstatus auf aktiv
WindschutzTimer = millis(); // Starte den Timer für die Wartezeit
stepper.moveTo(Windschutz); // Bewege den Schrittmotor zur Windschutzposition
stepper.runToPosition(); // Führe die Bewegung zum Windschutz durch
}
// Überprüfe, ob die Wartezeit für den Windschutz abgelaufen ist
if (millis() - WindschutzTimer >= WindWartezeit_ms) {
Warteposition_aktiv = false; // Setze den Wartepositionsstatus auf inaktiv
}
// Wenn der Windsensor nicht aktiv ist, rufe die Funktion managePositions auf
if (!Warteposition_aktiv) {
managePositions();
}
}
/**
* ###########################################
* Bereich für MQTT-Verbindung
* ###########################################
*/
/**
* Versucht, eine Verbindung zum MQTT-Broker herzustellen.
* Die Funktion wird solange ausgeführt, bis eine Verbindung erfolgreich hergestellt wurde.
* Wenn eine Verbindung hergestellt ist, wird eine entsprechende Meldung über die serielle Schnittstelle ausgegeben.
* Wenn die Verbindung fehlschlägt, wird der Fehlercode auf der seriellen Schnittstelle ausgegeben, und es wird alle 5 Sekunden erneut versucht, eine Verbindung herzustellen.
*/
void reconnect() {
// Loop, bis eine Verbindung hergestellt wird
while (!client.connected()) {
// Verbindung mit MQTT-Broker herstellen, einschließlich Benutzername und Passwort
if (client.connect("ArduinoClient", mqtt_user, mqtt_password)) {
Serial.println("verbunden"); // Verbindung erfolgreich
} else {
Serial.print("Fehlgeschlagen, rc="); // Verbindung fehlgeschlagen, rc=...
Serial.print(client.state());
Serial.println(" Versuche es in 5 Sekunden erneut");
// 5 Sekunden warten und erneut versuchen
delay(5000);
}
}
}
/**
* Sendet MQTT-Daten an den Broker.
* Die Funktion überprüft zunächst, ob eine Verbindung zum MQTT-Broker besteht. Wenn keine Verbindung besteht, wird eine neue Verbindung hergestellt.
* Die Funktion erfasst dann verschiedene Daten wie die Position des Motors, den Status des Windsensors, die Zeitzone und das Positionsset basierend auf dem aktuellen Monat.
* Diese Daten werden in MQTT-Nachrichten verpackt und an entsprechende Topics veröffentlicht.
* Die Funktion sorgt dafür, dass die Daten nur alle 5 Sekunden gesendet werden, um die Netzwerkbelastung zu reduzieren.
*/
void sendMQTTData() {
// Überprüfe, ob eine Verbindung zum MQTT-Broker besteht
if (!client.connected()) {
reconnect(); // Verbindung wiederherstellen, falls nicht verbunden
}
// Erfasse die aktuellen Zeit
unsigned long currentTime = millis();
// Sende die MQTT-Nachrichten nur, wenn seit dem letzten Senden mehr als 5 Sekunden vergangen sind
if (currentTime - lastSendTime >= 5000) {
// Erfasse die Daten, die du übertragen möchtest
int motorPosition = stepper.currentPosition(); // Aktuelle Position des Motors
String windsensorStatus = Warteposition_aktiv ? "Aktiviert" : "Deaktiviert"; // Status des Windsensors
String zeitzone = isDaylightSavingTime(currentTime) ? "Sommerzeit" : "Winterzeit"; // Aktuelle Zeitzone
String positionsset; // Positionsset entsprechend dem aktuellen Monat
int currentMonth = month(currentTime);
if (currentMonth == 1 || currentMonth == 2 || currentMonth == 11 || currentMonth == 12) {
positionsset = "Winter";
} else if (currentMonth == 3 || currentMonth == 4 || currentMonth == 9 || currentMonth == 10) {
positionsset = "Zwischen";
} else if (currentMonth >= 5 && currentMonth <= 8) {
positionsset = "Sommer";
} else {
positionsset = "Standard";
}
// Erfasse die aktuelle Uhrzeit
String currentTimeStr = String(hour()) + ":" + minute() + ":" + second();
// Debugging-Ausgabe
Serial.println("Veröffentliche MQTT-Nachrichten:");
// Erstelle und veröffentliche die MQTT-Nachrichten mit den erfassten Daten
Serial.print("Motorposition: ");
Serial.println(motorPosition);
client.publish("solar/bkw-steuerung/motorposition", String(motorPosition).c_str());
Serial.print("Windsensor-Status: ");
Serial.println(windsensorStatus);
client.publish("solar/bkw-steuerung/windsensor", windsensorStatus.c_str());
Serial.print("Zeitzone: ");
Serial.println(zeitzone);
client.publish("solar/bkw-steuerung/zeitzone", zeitzone.c_str());
Serial.print("Positionsset: ");
Serial.println(positionsset);
client.publish("solar/bkw-steuerung/positionsset", positionsset.c_str());
Serial.print("Aktuelle Uhrzeit: ");
Serial.println(currentTimeStr);
client.publish("solar/bkw-steuerung/uhrzeit", currentTimeStr.c_str());
// Setze die Zeit des letzten Sendens auf die aktuelle Zeit
lastSendTime = currentTime;
}
}
/**
* Sendet eine Entdeckungsnachricht an den MQTT-Broker, um ein neues Gerät oder eine neue Entität zu entdecken.
* Die Funktion akzeptiert den Namen des Geräts, das Topic für den Zustand und optional den state_class-Parameter, um den Typ des Zustands anzugeben.
* Die Entdeckungsnachricht wird im JSON-Format erstellt und an das entsprechende Topic für die Entdeckung veröffentlicht.
*
* @param topic Das Topic für die Entdeckungsnachricht.
* @param name Der Name des Geräts oder der Entität.
* @param stateTopic Das Topic für den Zustand des Geräts oder der Entität.
* @param stateClass Der optionale Parameter für den Typ des Zustands (standardmäßig leer).
*/
void sendDiscoveryMessage(const char* topic, const char* name, const char* stateTopic, const char* stateClass = "") {
// Erstelle die Entdeckungsnachricht im JSON-Format
String discoveryMessage = "{";
discoveryMessage += "\"name\":\"" + String(name) + "\",";
discoveryMessage += "\"state_topic\":\"" + String(stateTopic) + "\"";
// Füge den state_class-Parameter hinzu, wenn er übergeben wurde
if (strlen(stateClass) > 0) {
discoveryMessage += ",\"state_class\":\"" + String(stateClass) + "\"";
}
discoveryMessage += "}";
// Sende die Entdeckungsnachricht
client.publish(("homeassistant/sensor/bkw-steuerung/" + String(name) + "/config").c_str(), discoveryMessage.c_str(), true);
}
/**
* Sendet automatisch Entdeckungsnachrichten für verschiedene Sensoren der Solarsteuerung.
* Die Funktion sendet Entdeckungsnachrichten für die Motorposition, den Windsensor, die Zeitzone, das Positionsset und die Uhrzeit.
* Die Nachrichten werden an die entsprechenden Topics für die Entdeckung veröffentlicht.
*/
void sendMQTTAutoDiscovery() {
sendDiscoveryMessage("solar/bkw-steuerung/motorposition", "Motorposition", "solar/bkw-steuerung/motorposition", "measurement");
sendDiscoveryMessage("solar/bkw-steuerung/windsensor", "Windsensor", "solar/bkw-steuerung/windsensor");
sendDiscoveryMessage("solar/bkw-steuerung/zeitzone", "Zeitzone", "solar/bkw-steuerung/zeitzone");
sendDiscoveryMessage("solar/bkw-steuerung/positionsset", "Positionsset", "solar/bkw-steuerung/positionsset");
sendDiscoveryMessage("solar/bkw-steuerung/uhrzeit", "Uhrzeit", "solar/bkw-steuerung/uhrzeit");
}
/**
* Callback-Funktion, die aufgerufen wird, wenn eine MQTT-Nachricht empfangen wird.
* Die Funktion gibt den empfangenen Topic und den Payload aus und führt entsprechende Aktionen basierend auf dem Inhalt der Nachricht aus.
*
* @param topic Das Topic der empfangenen MQTT-Nachricht.
* @param payload Der Payload der empfangenen MQTT-Nachricht.
* @param length Die Länge des Payloads.
*/
void callback(char* topic, byte* payload, unsigned int length) {
// Ausgabe des empfangenen Topics
Serial.print("Nachricht empfangen [");
Serial.print(topic);
Serial.print("] ");
// Ausgabe des Payloads
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// Debugging-Ausgabe, um sicherzustellen, dass die callback-Funktion aufgerufen wird
Serial.println("Callback-Funktion wurde aufgerufen!");
// Unterscheidung der Nachrichten und Ausführung entsprechender Aktionen
if (strcmp(topic, "solar/bkw-steuerung/app-steuerung") == 0) {
String message = ""; // Initialisiere den String für die Nachricht
// Zusammenstellen der Nachricht aus dem Payload
for (int i = 0; i < length; i++) {
message += (char)payload[i]; // Füge jeden Byte des Payloads zum String hinzu
}
message.trim(); // Entferne führende und abschließende Leerzeichen
// Ausführen entsprechender Aktionen basierend auf dem Inhalt der Nachricht
if (message.equals("Windsensor aktivieren")) {
// Aktion: Windsensor aktivieren
digitalWrite(PIN_BUTTON, HIGH);
Serial.println("Windsensor wurde aktiviert.");
stepper.moveTo(Windschutz);
stepper.runToPosition();
Serial.println("Motor fährt zur Windschutzposition.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("Windsensor deaktivieren")) {
// Aktion: Windsensor deaktivieren
digitalWrite(PIN_BUTTON, LOW);
Serial.println("Windsensor wurde deaktiviert.");
Warteposition_aktiv = false;
} else if (message.equals("position_setzen")) {
// Aktion: Setzen der Windschutzposition
stepper.moveTo(Windschutz);
stepper.runToPosition();
Serial.println("Motor fährt zur definierten Windschutzposition.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("Startposition")) {
// Aktion: Motor zur Startposition bewegen
stepper.moveTo(Startposition);
stepper.runToPosition();
Serial.println("Motor fährt zur Startposition.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("Endposition")) {
// Aktion: Motor zur Endposition bewegen
stepper.moveTo(Endposition);
stepper.runToPosition();
Serial.println("Motor fährt zur Endposition.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("+1000")) {
// Aktion: Motor um +1000 Positionen bewegen
int newPos = stepper.currentPosition() + 1000;
stepper.moveTo(newPos);
stepper.runToPosition();
Serial.println("Motor fährt um +1000 Positionen.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("-1000")) {
// Aktion: Motor um -1000 Positionen bewegen
int newPos = stepper.currentPosition() - 1000;
stepper.moveTo(newPos);
stepper.runToPosition();
Serial.println("Motor fährt um -1000 Positionen.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("-17000")) {
// Aktion: Motor um -17000 Positionen bewegen
int newPos = stepper.currentPosition() - 17000;
stepper.moveTo(newPos);
stepper.runToPosition();
Serial.println("Motor fährt um -17000 Positionen.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
} else if (message.equals("+24000")) {
// Aktion: Motor um +24000 Positionen bewegen
int newPos = stepper.currentPosition() + 24000;
stepper.moveTo(newPos);
stepper.runToPosition();
Serial.println("Motor fährt um +24000 Positionen.");
Warteposition_aktiv = true;
WindschutzTimer = millis();
}
}
}
/**
* Funktion zum Aktualisieren des MQTT-Clients und zum Senden/Empfangen von Nachrichten.
* Wenn keine Verbindung zum Broker besteht, wird eine erneute Verbindung hergestellt.
*/
void mqttLoop() {
// Überprüfen, ob der Client verbunden ist
if (!client.connected()) {
// Wenn nicht verbunden, erneut verbinden
reconnect();
}
// Aktualisiere den MQTT-Client
client.loop();
}