ESP8266 mit Telegram Bot - im Dauerbetrieb unzuverlässig

Ja, du hast wie immer Recht. :slightly_smiling_face:

Kann es sein, dass mein Code schon zu groß für so einen kleinen Prozessor ist?

Ich wüsste gar nicht, wie ich die vielen Strings vermeiden könnte ohne auf die Funktionalität zu verzichten.

Ich habe es jetzt mit dem Reset gemacht, aber ohne Zeit Abfrage, sondern einfach mit Zähler (und millis) im loop, nach einem Tag wird Reset ausgelöst.

Das muss ich jetzt erstmal beobachten.

Nach dem kompilieren stecht doch der speicherverbrauch, und wen überläuft machen das die Srtings wie @Plumps schreibt.

Es liegt nicht an der Menge des Codes. Es liegt daran wie die Daten im RAM abgelegt werden. Strings sind bei Mikrocontrollern böse.

Hier änderst du zehnmal den String help. Jedesmal wird er in den RAM gelegt. Jedesmal wird er größer, braucht dann wahrscheinlich eine neue Stelle im RAM.

1 Like

Also besser, so wie in C-Zeiten wohl, eine feste Länge für den String (ein definiertes char-Array) definieren und den ganzen Kladderadatsch direkt rein schreiben?

Kann ich schon machen, ich programmiere das ja einmal, dann soll es möglichst stabil und lange durchgehend laufen, der Code muss ja nicht hübsch leserlich sein.
Ich könnte auch schon die meisten Strings fest definieren, nicht erst zur Laufzeit.

Mit dem Zähler (ein Tag) und Reset läuft es jetzt gerade erst einen Tag, bisher natürlich noch stabil.
Ich warte jetzt aber noch ein paar Tage ab, ob es wirklich stabil bleibt.

Du kannst Dir auch mal PString anschauen. Das basiert auf einem festen char-Array, hat aber mehr Komfort.

Gruß Tommy

2 Likes

Oder es gibt ja auch SafeString, habe ich von Taming Arduino Strings -- How to Avoid Memory Issues
Dort habe ich aber auch gelesen, dass die ESP8266 mit der darunterliegenden extensiven String Nutzung deren Web-Bibliotheken sowieso schon Probleme mit dem Speicher haben:
Handling Out-Of-Memory on ESP32 and ESP8266 – Adding Periodic Automatic Reboots
Dort wird auch ein regelmäßiger Reboot empfohlen:

The ESP32 and ESP8266 have much more SRAM available so provided you follow the guidelines for using Arduino Strings you are unlikely to have any memory problem due to your code. However the web libraries of these boards make extensive use of Strings so if you are coding a web project you may eventually run out-of-memory memory due to problems in the underlying web libraries. In that case the board will either lock up or reboot. Removing Strings from your code won't change that. The best you can do it to arrange for an automatic reboot of the board on a periodic basis to clear out the memory. Both the ESP32 and EPS8266 have a watch dog timer, so enabling the watch dog timer and putting the code into tight loop will cause a reboot.

Bisher habe ich mit dem täglichen Reboot keine Probleme mehr, nur dass ich jetzt keine dauerhafte chat_id-Freigaben mehr setzen kann, die werden beim Reboot ja dann gelöscht. Muss ich evtl. schauen, wie ich Variablen (chat_id die längerfristig für die Nutzung freigegeben werden sollen) von einer Webseite auslesen kann...

Mir ist auch noch nicht klar, was passiert, wenn jemand meinem Bot eine sehr lange Nachricht schickt, die ja im ESP ausgewertet wird, also einem String zugewiesen.
Das könnte ihn auf einen Schlag zum Absturz bringen, fürchte ich, was bisher glücklicherweise noch nicht passiert ist...
Muss ich die Eingabe-Strings beim zuweisen wohl abschneiden o.ä.
Der Bot ist ja für jeden erreichbar, jeder kann ihn suchen und ihm eine Nachricht schreiben, das ist ja auch nützlich.

Du kannst für die Speicherung auch den RTC-RAM benutzen.

Gruß Tommy

Aber was passiert, wenn der ESP vom Bot eine sehr, sehr große Nachricht als String speichert? Kann er da mit einem Mal abstürzen?
Die entsprechende Zeile meines Codes ist ja hier:


void handleNewMessages(int numNewMessages)
{ ...
  for (int i = 0; i < numNewMessages; i++)
  { ...  
->    String text = bot.messages[i].text;
    ...

das sollte ich vielleicht aber in der Lib. vom Bot schon prüfen und abfangen, falls das überhaupt geht.
Dort (nämlich in: C:\Users\f\Documents\Arduino\libraries\Universal-Arduino-Telegram-Bot-master\src\UniversalTelegramBot.cpp) finde ich eine Variable maxMessageLength, die in der UniversalTelegramBot.h definiert ist, public in der class UniversalTelegramBot: int maxMessageLength = 1500;
Also habe ich mal testweise eine 1500 Zeichen lange Nachricht geschickt und, wer hätte es gedacht, der ESP bleibt stehen, reagiert nicht mehr :cry:
Dann dürfte er aber auch nicht mehr resetten nach Ablauf des täglichen Counters, d.h. ich muss manuell resetten. Sehr schlecht.

Muss ich mal auf testweise nur 50 (statt 1500) setzen, schauen was passiert, sollte eigentlich dann beim Auslesen der Nachricht (body) auf 50 Zeichen geschnitten werden, siehe Z118 in der cpp:

...
       if (ch_count < maxMessageLength) {
          body += c;
          ch_count++;
        }
...

Tatsächlich ist die Bibliothek buggy, siehe: Bug in UniversalTelegramBot::getUpdates(): cycling problem when deserializeJson happen to endup with an error #275
Mit einer größeren Nachricht zum Bot bleibt der ESP stehen, weil er die Nachricht nicht abholen kann und aber auch nicht als gelesen setzt um die nächste Nachricht annehmen zu können. Die blockiert dann den Laden.
Das hat mich einige Stunden Kopfzerbrechen gekostet, bis ich den Issue gefunden hatte. Man kommt ja sonst nicht bei. Glücklicherweise hat jemand die cpp repariert, also gefixt, das geht.

Ein paar Wochen später:
Mit dem täglichen Reboot geht es seither zuverlässig.
Allerdings sehe ich immer wieder mal einen ungeplanten Reboot zwischendurch, da wird der ESP wohl von alleine abgestürzt sein und startet neu, warum auch immer. Das beunruhigt mich etwas.
Aber bisher gab es keine Ausfälle in der Funktionalität.

Ich habe noch einen Fork der benutzten Bibliothek entdeckt, die alle Strings durch char[]* tauscht, da werde ich noch einen Blick rein werfen, wobei diese einige commits hinten dran ist.
Die String Bibliotheken (SafeString etc.) muss ich auch noch anschauen und RTC-RAM natürlich und vieles mehr... meine ToDo Liste ist noch lange :slight_smile:

Wäre es besser, anstatt zB:

String year_hash; // Hash, 14 Zeichen immer

zu schreiben:

char year_hash[15]; // char-Array für den Hash, immer 14 Zeichen

Oder würde das nichts ändern?
Von der Sorte String hab ich ein paar. Einige String Variablen kann ich so ändern, aber nicht alle, bzw. wäre das sehr umständlich :frowning:

Solange der String immer die gleiche Länge behält, ist es kein Problem. Sobald man ihn aber kürzt oder verlängert führt es zu Fragmentierung.

Ob in einem char[] jetzt steht {'1','2','3','\0','5','6','7','8','9','0','\0'} oder {'1','2','3','4','5','6','7','8','9','0','\0'} sollte doch nichts machen, oder?
Einmal wäre es "123" und einmal wäre es "1234567890", das Array wäre immer 11 groß.

Und wenn ich Strings der Art "1234567890" habe, oder auch mal "9876543210" (also größer als 2^32), ist es sinnvoll diese als long long int abzulegen und jedes Mal, wenn ich sie (leider) als String brauche zu casten, also etwa:

long long int chat_id = 12345678901;
sendMessage(String(chat_id));

Ist das sinnvoll?
Bei einer Variable kann sich die Anzahl der Zeichen ändern, also mal sind es 10, mal nur 9.

So wie ich es jetzt verstanden habe, ist das Problem die String Änderung zur Laufzeit, wenn zB bei jeder Abfrage (sekündlich immerhin) ein neuer String zusammengesetzt oder geändert würde.
Eine Menge der Strings könnte ich schon fest anlegen, aber bei ein paar lasst es sich nicht vermeiden, da müsste ich dann eben (wie hier im Post oben das Beispiel) große Arrays anlegen und dann immer drüber schreiben und mit \0 abschließen.

Willst Du mit dieser id rechnen? Wenn nicht, warum willst Du die dann unbedingt als Zahl und nicht als Zeichenkette speichern?

Gruß Tommy

Wg. der Fragmentierung, ich ändere diese Zahl (chat_id) immer mal...

Wenn Du mit '\0' terminierte char-Arrays anstelle von Strings benutzt, fragmentiert nichts.
Wie bestimmst Du die neue id?

Gruß Tommy

In den Beispielen der IDE gibt es heapMetric für den ESP


void stats(const char* what) {
  // we could use getFreeHeap() getMaxFreeBlockSize() and getHeapFragmentation()
  // or all at once:
  uint32_t free;
  uint32_t max;
  uint8_t frag;
  ESP.getHeapStats(&free, &max, &frag);

  Serial.printf("free: %7u - max: %7u - frag: %3d%% <- ", free, max, frag);
  // %s requires a malloc that could fail, using println instead:
  Serial.println(what);
}

Die kannst Du an diversen Stellen aufrufen (mit Angabe vom Ort im Code oder was gerade aufgefunden oder was auch immer)
und es werden dir im Sermon die Speicherdaten ausgegeben

2 Likes

Per Befehl an der Bot, etwa:

/granted_chat_id:1234567890

da brauche ich aber schon wieder Strings :frowning:
Das ganze ohne die bequemen Strings zu machen ist verdammt mühsam :slightly_frowning_face:

Nö, das re

Nö, das redest Du Dir nur ein.

Beschäftige Dich mal etwas mit Char-Arrays, dann merkst Du, dass das nicht so schwer ist. Ich habe hier mal ein Tutorial dazu geschrieben.

Evtl. auch mal PString anschauen.

Gruß Tommy

1 Like