Forum: Mikrocontroller und Digitale Elektronik ESP Frage zu Setup-Code, mit reconnect, etc.


von Klausi (Gast)


Lesenswert?

Hallo allerseits,
in meinen jetzigen Projekten arbeite ich viel mit Esp8266, die über WiFi 
und MQTT mit einem Raspi kommunizieren. Soweit alles gut.

Über die letzten Monate habe ich das Setup, bzw. das Verhalten bei 
verlorenen Verbindungen (z.B. Wlan für x Minuten tot, oder Verbindung 
schlecht oder was es auch gibt) immer wieder erweitert.

Es würde mich freuen wenn ihr über diesen Code einmal drüberschauen 
könntet.
Ich bin sehr offen für Kritik und Verbesserungsvorschläge (ich habe das 
Programmieren nie richtig erlernen dürfen, deswegen gibt es bestimmt 
Fehler/ineffektive Zeilen ;)

An sich funktioniert dieser Code, aber ich würde sehr gerne einmal eure 
Meinung hören. Vielleicht hat ja jemand ähnliche Zeilen bei sich, und 
kann diese gerne als Alternative vorschlagen.
1
#include <ESP8266WiFi.h>
2
#include <PubSubClient.h>
3
const char* ssid = "SSID";
4
const char* password = "PASSWORD";
5
const char* mqtt_server = "192.168.178.31";
6
const char* deviceID = "WEMOSD1mini";
7
8
WiFiClient wifiClient;                  //create an instance of the WiFi client
9
PubSubClient MQTTclient(wifiClient);    //create an instance of the MQTT client, based on the WiFi client
10
11
long lastMsgTime = 0;                   //The time (from millis()) at which last message was published
12
char msg[64];                           //A buffer to hold messages to be sent/have been received
13
char topic[32];                         //The topic in which to publish a message
14
int pulseCount = 0;                     //Counter for number of heartbeat pulses sent
15
16
void setup() {
17
  //PIN für LED & Anschalten dieser, um boot Vorgang anzuzeigen
18
  pinMode(D3, OUTPUT);
19
  digitalWrite(D3, HIGH);
20
  
21
  wifiSetup();            //WIFI-Connection
22
  mqttSetup();            //MQTT-Connection
23
24
  //LED kurze Blinken & ausschalten (zum Anzeigen, dass Boot-Vorgang abgeschlossen ist
25
  for (int i = 0; i < 4; i++){
26
    digitalWrite(D3, HIGH);
27
    delay(250);
28
    digitalWrite(D3, LOW);
29
    delay(250);
30
  }
31
}
32
33
void loop() {
34
  mqttLoop();
35
  LED();
36
}
37
38
void mqttCallback(char* topic, byte* payload, unsigned int length) {      //Callback function each time a message is published in any of the topics to which this client is subscribed
39
...
40
}
41
42
void mqttLoop() {
43
  if (MQTTclient.connected()) {
44
    MQTTclient.loop();              //call the main loop to check for and publish messages
45
  }
46
}
47
48
void mqttReconnect() {
49
  if (!MQTTclient.connected()) {
50
    Serial.print("\nAttempting to connect to MQTT broker at (30sek) ");
51
    Serial.print(mqtt_server);
52
53
    MQTTclient.connect(deviceID);
54
    unsigned long currentF = millis();
55
    unsigned long previousF = currentF;
56
57
    //der Versuch, sich neu zu verbinden soll maximal 45 Sekunden dauern, da er im Zweifel nicht 5h in dieser Schleife hängen soll. (Ich habe vor, mithilfe einer Variable im EEPROM-Speicher, bei jedem 5. Reboot hintereinander, ersteinmal eine Pause von z.B. 15min zu lassen. Denn wenn nach 5 Versuchen keine Verbindung gelingt, liegt das Problem eher am Router
58
    while (!MQTTclient.connected() && currentF - previousF <= 45000) {   
59
      delay(100);
60
      Serial.print("-");
61
      MQTTclient.connect(deviceID);
62
      currentF = millis();
63
    }
64
    if (!MQTTclient.connected()) {
65
    //Serial.print(MQTTclient.state());       //Debug Info
66
    Serial.write("\nMQTT-Reconnect fehlgeschlagen. Automatischer Neustart in 3 sek!\n");
67
    delay(3000);
68
    ESP.restart();
69
    }
70
    else {
71
      Serial.println("\nConnected to MQTT broker\n");        //Debug info
72
    
73
      //hier stehen alle subscriptions
74
    }
75
  }
76
}
77
78
void WiFiReconnect() {
79
  if (WiFi.status() != WL_CONNECTED) {
80
    Serial.print("\nVersuch, eine WLan Verbindung aufzustellen bei (30sek): ");
81
    Serial.print(ssid);
82
83
    WiFi.begin(ssid, password);
84
    unsigned long currentH = millis();
85
    unsigned long previousH = currentH;
86
87
    //der Versuch, sich neu zu verbinden soll maximal 45 Sekunden dauern, da er im Zweifel nicht 5h in dieser Schleife hängen soll
88
    while (WiFi.status() != WL_CONNECTED && currentH - previousH <= 45000) {
89
      delay(500);
90
      Serial.print(".");
91
      currentH = millis();
92
    }
93
    if (!MQTTclient.connected()) {
94
    //Serial.print(MQTTclient.state());       //Debug Info
95
    Serial.write("\nWLan-Reconnect fehlgeschlagen. Automatischer Neustart in 10 sek!\n");
96
    delay(10000);
97
    ESP.restart();
98
    }
99
    else {
100
      Serial.println("\nConnected to WiFi\n");                 //Debug info
101
      Serial.println("MQTT Verbindung wird hergestellt...");
102
      mqttReconnect();
103
    }
104
  }
105
}
106
107
void mqttSetup() {
108
  MQTTclient.setServer(mqtt_server, 1883);
109
  MQTTclient.setCallback(mqttCallback);
110
111
  Serial.print("Attempting to connect to MQTT broker at (20sek) ");
112
  Serial.println(mqtt_server);
113
  
114
  MQTTclient.connect(deviceID);
115
  unsigned long currentG = millis();
116
  unsigned long previousG = currentG;
117
118
  //der Versuch, sich neu zu verbinden soll maximal 20 Sekunden dauern, da er im Zweifel nicht 5h in dieser Schleife hängen soll
119
  while (!MQTTclient.connected() && currentG - previousG <= 20000) {
120
    delay(100);
121
    Serial.print("-");
122
    MQTTclient.connect(deviceID);
123
    currentG = millis();
124
  }
125
  if (!MQTTclient.connected()) {
126
    //Serial.print(MQTTclient.state());       //Debug Info
127
    Serial.write("\nMQTT-Verbindung fehlgeschlagen. Automatischer Neustart in 3sek!\n");
128
    delay(3000);
129
    ESP.restart();
130
  }
131
  else {
132
    Serial.println("\nConnected to MQTT broker");       //Debug info
133
  
134
    //hier stehen alle subscriptions
135
  }
136
}
137
138
void wifiSetup() {
139
  if (!Serial) {
140
    Serial.begin(9600);                   //Start the Serial connection
141
    Serial.println("\nESP gestartet");
142
  }
143
144
  Serial.print("Ausgewähltes Wlan-Netzwerk: ");     //Connect to the specified WiFi network
145
  Serial.println(ssid);
146
  WiFi.begin(ssid, password);
147
  unsigned long currentH = millis();
148
  unsigned long previousH = currentH;
149
150
  Serial.print("Verbidung wird hergestellt (15sek)  ...");                    //progress while we wait to connect
151
  while (WiFi.status() != WL_CONNECTED && currentH - previousH <= 15000) {     //wenn keine Verbindung und weniger als 15 Sekunden vorbei: h < 15000
152
    delay(500);
153
    Serial.print(".");
154
    currentH = millis();
155
  }
156
  if (WiFi.status() != WL_CONNECTED) {
157
    Serial.write("\n Wlan Verbindung fehlgeschlagen.   Auto Restart\n");
158
    ESP.restart();
159
  }
160
  else {
161
    delay(1500);                 //give the WiFi a couple of seconds to initialize
162
    Serial.println();
163
  
164
    Serial.print("Verbunden! IP-Adresse: ");
165
    Serial.println(WiFi.localIP());
166
  }
167
}
168
169
void LED() {
170
  if  (WiFi.status() != WL_CONNECTED) {
171
    digitalWrite(D3, HIGH);
172
    Serial.print("\nWlan Verbindung verloren");
173
    WiFiReconnect();
174
  }
175
  else if (!MQTTclient.connected()) {
176
    digitalWrite(D3, HIGH);
177
    Serial.print("\nMQTT Verbindung Verloren");
178
    mqttReconnect();
179
  }
180
  else {
181
    digitalWrite(D3, LOW);
182
  }
183
}

von Michael U. (amiga)


Angehängte Dateien:

Lesenswert?

Hallo,

das Problem mit WLAN-Verlust und MQTT Reconnect kenne ich...
Ich hatte vor Zeiten mal einen Ansatz im Netz gefunden, den ich 
letztlich bei mir benutze.
Ich hänge mal einen Sketch von mir ran, da ist zwar noch die Ansteuerung 
einer IN-9 mit Temperaturwerten drin, das sind aber nur ein paar Zeilen.
Ich benutze aber den AsyncMQTT, PubSubClient nehme ich nur noch bei 
Sensoren, die in den DeepSleep gehen, weil mir sein teilweise 
blockierendes Verhalten erspart, nachzuschauen, ob er wirklich fertig 
ist.
Ich weiß jetzt nicht, ob es da noch Sachen bei Dir vorkommen, die ich 
hier so nicht hatte.
Ist also mehr so zum reinschauen gedacht...
PS: ESP.restart(); gibt es bei mir nie, aber nur so aus Prinzip.
Entweder habe ich im Programm was nicht berücksichtigt oder es ist 
wirklich eine Störung von Außen, die ich in Kauf nehmen muß, dann würde 
aber nur ein Hardware-Watchdog helfen, wenn er im Programm hängt, kommt 
er auch nicht zum Restart Befehl.

Gruß aus Berlin
Michael

von Bernhard S. (b_spitzer)


Lesenswert?

Der ESP8266 braucht in der Loop (oder den darin ausgeführten Funktionen) 
irgendwo ein delay(irgendwas) oder yield(), um der WLAN-Task Rechenzeit 
abzugeben.
Mache daher mal ein delay(0) in die Loop.

von Michael U. (amiga)


Lesenswert?

Hallo,

Bernhard S. schrieb:
> Der ESP8266 braucht in der Loop (oder den darin ausgeführten
> Funktionen)
> irgendwo ein delay(irgendwas) oder yield(), um der WLAN-Task Rechenzeit
> abzugeben.
> Mache daher mal ein delay(0) in die Loop.

loop() selbst erledigt das bei jedem Durchlauf. Ein yield(); muß dort 
hin, wo es länger dazernde Blockierungen gibt, ich habe seine 
aufgerufenen Funktionen noch nicht komplett durchgeschaut.
Wenn ein Durchlauf der loop immer schnell genug ist, braucht kan es 
nicht.
1
...
2
while (digitalRead(Pin) == HIGH)
3
{
4
  yield();
5
}
6
...

Hier wäre ein yield(); Pflicht, wenn der Pin länger auf High sein kann, 
z.B. Warten auf einen Tastendruck.

Gruß aus Berlin
Michael

von Stefan F. (Gast)


Lesenswert?

Für den Wiederaufbau der WLAN Verbindung muss man gar nichts 
programmieren, das macht die Basis-Firmware des ESP8266 von ganz 
alleine.

Was den MQTT Anteil angeht: Das könnte es anders sein.

von Michael U. (amiga)


Lesenswert?

Hallo,

Stefan F. schrieb:
> Für den Wiederaufbau der WLAN Verbindung muss man gar nichts
> programmieren, das macht die Basis-Firmware des ESP8266 von ganz
> alleine.

Prinzipiell ja, es gibt aber Umstände, wo er nicht will. Hier 
(vielleicht nur in Verbindung mit meinem Router), als Beispiele: 
längerer WLAN Ausfall, Kanalwechsel. Nicht immer, aber es passiert. 
Natürlich kann man dann eben einen Restart auslösen, teilweise laufen 
aber auf den ESPs Sachen, wo ich mir dann extra den letzten Zustand 
merken müßte usw.

> Was den MQTT Anteil angeht: Das könnte es anders sein.
Da passiert folgendes: Der ESP8266 meldet WL_CONNECTED sobald die 
Verbindung zum Router steht. Da hat er aber noch keine IP vom DHCP. Wenn 
jetzt bereits das MQTT connect folgt, geht das schief.
Da hilft dann der Event onWifiConnect(const WiFiEventStationModeGotIP& 
event); weiter, der triggert, wenn eine gültige IP vom DHCP angekommen 
ist.
Es wird immer von der Anwendung abhängen, welchen Weg man gehen will.
zumindest habe ich seitdem keine hängenden ESP8266 mehr im Netz gehabt.

Gruß aus Berlin
Michael

von Stefan F. (Gast)


Lesenswert?

Michael U. schrieb:
> prinzipiell ja, es gibt aber Umstände, wo er nicht will.

Dann hast du wohl eine Fehlerhafte Firmware Version.

Ich habe einen ESP8266 seit 4 Jahren im Dauerbetrieb. Jede Nacht schalte 
ich meinen Router aus und zwischendurch bricht das WLAN Netz auch 
tagsüber wegen Störungen (von woher weiß der Teufel) zusammen.

Bisher hat das ESP Modul die Verbindung immer von alleine neu aufgebaut. 
Wirklich immer. 100%

Ich benutze wegen der Zuverlässigkeit bewusst den alten Arduino Core 
2.3.0, welcher (soweit ich weiß) auf dem SDK 1.5.4 basiert.

von Klausi (Gast)


Lesenswert?

Danke schonmal für eure Antworten.
Ja, grundsätzlich verbindet sich der ESP zwar automatisch wieder mit dem 
WLAN, ich will aber verhindern, dass er sich bei gestörter Verbindung in 
irgendwelchen loops verfängt.

Fällt zum Beispiel das WLAN aus, ist natürlich sowohl die WLAN 
Verbindung ald auch die MQTT Verbindung weg.
Ich will dann eben eine geregelte Reconnection.

Und wenn längere Zeit keine Chance auf einen erfolgreichen Aufbau der 
Verbindung steht, soll er halt - anstatt Minuten-/stundenlang einen 
reconnect zu versuchen - einen Neustart und oder eine Pause machen und 
sich eben nicht damit selbst blockieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.