Webserver hängt sich aus

Hallo
bin Arduino Neuling und bräuchte eueren Rat,
ich habe auf Basis eines UNO r3+ Ethernet shield einen Webserver zusammen genagelt.
Dieser ist mit Hyperlinks so verknüpft, dass er bei abruf eines links ein 433mhz Signal über einen Sender verschickt.
Sinn dahinter soll eine Hausautomation werden mit der ein paar Steckdosen geschaltet werden. Das funktioniert auch sehr gut von iPad und Co. Jedoch "hängt?" er sich nach ca 12-13h auf und er versendet keine Signale mehr. Das Ethernet shield zeigt aber an das es den Abruf bekommt. Nach dem Drücken des Reset Buttons geht es Dan wieder. Ich hab den Code nur etwas verändert und die Frequenzen der Steckdosen eingetragen,ansonsten ist er aus der RCSwitch libary demo. mehr verstehe ich nicht davon. :cold_sweat:

Frage: gibt es einen Weg, dass er sich alle 6h selbst neu startet ohne das jemand kommen muss? Ist es normal das sich Programme nach einiger zeit einfach aufhängen?
Wäre sehr nett, wenn ihr Mal drüber schauen könntet, ob sich da ein grober Schnitzer breitgemacht hat.

#include <SPI.h>
#include <Ethernet.h>
#include <RCSwitch.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1, 70 };
EthernetServer server(80);
RCSwitch mySwitch = RCSwitch();
int RCTransmissionPin = 6;


void setup() {
  Ethernet.begin(mac, ip);
  server.begin();
  mySwitch.enableTransmit( RCTransmissionPin );
}
void loop() {
  char* command = httpServer();
}

void processCommand(char* command) {
  if(strcmp(command, "1-on") == 0) {
    mySwitch.send("010101111101010000001100");
  } else if (strcmp(command, "1-off") == 0) {
    mySwitch.send("010101111101010000000011");
	//Steckdose 1 Daniel Monitore//
	
  } else if (strcmp(command, "2-on") == 0) {
    mySwitch.send("010101110111010000001100");
  } else if (strcmp(command, "2-off") == 0) {
    mySwitch.send("010101110111010000000011");
	//Steckdose 2 Daniel Tv//
	
  } else if (strcmp(command, "3-on") == 0) {
    mySwitch.send("010101110101110000001100");
  } else if (strcmp(command, "3-off") == 0) {
    mySwitch.send("010101110101110000000011");
	//Steckdose 3 Daniel Fila//
	
  } else if (strcmp(command, "4-on") == 0) {
    mySwitch.switchOn('a', 3);
  } else if (strcmp(command, "4-off") == 0) {
    mySwitch.switchOff('a', 3);
	//Laura Tv//
	
  } else if (strcmp(command, "5-on") == 0) {
    mySwitch.send("011101011101010000001100");
  } else if (strcmp(command, "5-off") == 0) {
    mySwitch.send("011101011101010000000011");
	//Treppe Licht//
	
  } else if (strcmp(command, "6-on") == 0) {
    mySwitch.send("011101010111010000001100");
  } else if (strcmp(command, "6-off") == 0) {
    mySwitch.send("011101010111010000000011");
	//Schlafzimmer Kommode//
	
  } else if (strcmp(command, "7-on") == 0) {
    mySwitch.send("011101010101110000001100");
  } else if (strcmp(command, "7-off") == 0) {
    mySwitch.send("011101010101110000000011");
	//Schlafzimmer Tisch//
	
  } else if (strcmp(command, "8-on") == 0) {
    mySwitch.switchOn('a', 1);
  } else if (strcmp(command, "8-off") == 0) {
    mySwitch.switchOff('a', 1);
	//Wohnzimmer eg Lampe//
	
  } else if (strcmp(command, "9-on") == 0) {
    mySwitch.switchOn('a', 2);
  } else if (strcmp(command, "9-off") == 0) {
    mySwitch.switchOff('a', 2);
	//Flur eg Lampe//
	
  } else if (strcmp(command, "10-on") == 0) {
    mySwitch.send("INSERT BINÄRY");
  } else if (strcmp(command, "10-off") == 0) {
    mySwitch.send("INSERT BINÄRY");
	//** FREI **//
	
  } else if (strcmp(command, "11-on") == 0) {
    mySwitch.send("INSERT BINÄRY");
  } else if (strcmp(command, "11-off") == 0) {
    mySwitch.send("INSERT BINÄRY");
	//** FREI **//
  }
}
void httpResponseHome(EthernetClient c) {
  c.println("HTTP/1.1 200 OK");
  c.println("Content-Type: text/html");
  c.println();
  c.println("<html>");
  c.println("<head>");
  c.println("<title>Hausautomation</title>");
  c.println("<style>");
  c.println("body { font-family: Arial, sans-serif; font-size:20px; }");
  c.println("</style>");
  c.println("</head>");
  c.println("<body>");
  c.println("<h1>Hausautomation</h1>");
  c.println("<a href=\"./?1-on\">Monitore on</a>");
  c.println("<a href=\"./?1-off\">Monitore off</a>");
  c.println("<a href=\"./?2-on\">Daniel Tv on</a>");
  c.println("<a href=\"./?2-off\">Daniel Tv off</a>");
  c.println("<a href=\"./?3-on\">Daniel Fila on</a>");
  c.println("<a href=\"./?3-off\">Daniel Fila off</a>");
  c.println("<a href=\"./?4-on\">Laura Tv on</a>");
  c.println("<a href=\"./?4-off\">Laura Tv off</a>");
  c.println("<a href=\"./?5-on\">Treppe Licht on</a>");
  c.println("<a href=\"./?5-off\">Treppe Licht off</a>");
  c.println("<a href=\"./?6-on\">Sz Kommode on</a>");
  c.println("<a href=\"./?6-off\">Sz Kommode off</a>");
  c.println("<a href=\"./?7-on\">Sz Tisch on</a>");
  c.println("<a href=\"./?7-off\">Sz Tisch off</a>");
  c.println("<a href=\"./?8-on\">Wohnzimmer eg on</a>");
  c.println("<a href=\"./?8-off\">Wohnzimmer eg off</a>");
  c.println("<a href=\"./?9-on\">Flur eg on</a>");
  c.println("<a href=\"./?9-off\">Flur eg off</a>");
  c.println("<a href=\"./?10-on\">#10 on</a>");
  c.println("<a href=\"./?10-off\">#10 off</a>");
  c.println("<a href=\"./?11-on\">#11 on</a>");
  c.println("<a href=\"./?11-off\">#11 off</a>");
  c.println("</body>");
  c.println("</html>");
}
void httpResponseRedirect(EthernetClient c) {
  c.println("HTTP/1.1 301 Found");
  c.println("Location: /");
  c.println();
}
void httpResponse414(EthernetClient c) {
  c.println("HTTP/1.1 414 Request URI too long");
  c.println("Content-Type: text/plain");
  c.println();
  c.println("414 Request URI too long");
}
char*  httpServer() {
  EthernetClient client = server.available();
  if (client) {
    char sReturnCommand[32];
    int nCommandPos=-1;
    sReturnCommand[0] = '\0';
    while (client.connected()) {
      if (client.available()) {
char c = client.read();
if ((c == '\n') || (c == ' ' && nCommandPos>-1)) {
  sReturnCommand[nCommandPos] = '\0';
  if (strcmp(sReturnCommand, "\0") == 0) {
    httpResponseHome(client);
  } else {
    processCommand(sReturnCommand);
    httpResponseRedirect(client);
  }
  break;
}
if (nCommandPos>-1) {
  sReturnCommand[nCommandPos++] = c;
}
if (c == '?' && nCommandPos == -1) {
  nCommandPos = 0;
}
      }
      if (nCommandPos > 30) {
httpResponse414(client);
sReturnCommand[0] = '\0';
break;
      }
    }
    if (nCommandPos!=-1) {
      sReturnCommand[nCommandPos] = '\0';
    }

    delay(0);
    client.stop();
    return sReturnCommand;
  }
  return '\0';
}

Das Problem ist daß das RAM überläuft.
2 Dinge zur Lösung:

Grüße Uwe

Wie kommt dieser Überlauf zustande?

Bereits wenn ich in meinem Sketch zuviele Strings verwende und hochlade?

Oder belegt jeder Aufruf eines Strings (z.b. einfaches print("string")) Ram und nach x-Durchläufen ist dann der Ram voll?

Oder belegt jeder Aufruf eines Strings (z.b. einfaches print("string")) Ram und nach x-Durchläufen ist dann der Ram voll?

Di gedruckten Texte werden im RAM zwischengespeichert. Mit F() werden sie direkt aus dem Flash-Speicher gelesen und ausgegeben ohne Umweg über das RAM.

Strings werden in C nicht richtig verarbeitet.

Grüße Uwe

[b]print("string"); // [/b] kann beliebig oft ausgeführt werden, aber ...

"string" ist kein String sondern sind 7 Bytes, die -- wenn nicht speziell anders definiert [ s. F("string") ] -- , erstmal in den RAM kopiert werden, bevor sie verwendet werden können.

Ein String Objekt liegt immer im RAM und verursacht dynamische Speicheranforderungen, die 1. nicht sinnvoll , 2. fehlerbehaftet sind:

String test = "A";
test += "B";  // test enthält jetzt "AB", und das vorige "A" liegt zusätzlich auf dem Müll. Arduino hat aber keine Müllabfuhr.

Sepron:
char sReturnCommand[32];

Beim Einlesen von Zeichen prüfst Du nirgends, ob nicht eine Zeile in der Client-Anforderung möglicherweise länger als 31 Zeichen ist, dadurch besteht die Gefahr, über den Variablenbereich hinaus in fremde Datenbereiche zu schreiben.

Editieren
Hallo
Danke für die Rückmeldung

Ich habe jetzt die htmlcode strings mit F() versehen und der Sketch wurde kleiner, konnte bis jetzt auch kein Freeze erkennen.
Das mit den

char sReturnCommand[32];

Verstehe ich leider nicht. Wie muss ich den Code umschreiben, dass er nicht in fremde Datenbereiche schreibt?
Gibt es sonst noch Kleine kniffe, wie ich das Risiko des Einfrierens verringern kann oder, wenn es so weit kommt, er selbstständig neu startet?
Ich habe gelesen, dass man einen Pin mit reset brücken kann und ihn nach einer Reset den Neustart durchführen lässt, was aber nicht sonderlich gesund wäre.

Grüße aus dem Rheinland

Wie muss ich den Code umschreiben, dass er nicht in fremde Datenbereiche schreibt?

Hier:

if (nCommandPos>-1) {
  sReturnCommand[nCommandPos++] = c;
}

... solltest du, nach jurs' Meinung, eine Bremse einbauen.
z.B.

if (nCommandPos>-1 && nCommandPos < 31) {
  sReturnCommand[nCommandPos++] = c;
}

Aber wenn der server nicht von einem x-beliebigen client mit zu großen Kommandos gequält werden kann, ist das halb so wild.
Falls 31 Zeichen sicher reichen.

Sepron:
Verstehe ich leider nicht. Wie muss ich den Code umschreiben, dass er nicht in fremde Datenbereiche schreibt?

Siehe Vorschlag von Michael.

Beim nochmaligen Drüberschauen über Deinen Code habe ich gesehen, dass Du doch an einer Stelle noch etwas gegen das Schreiben über das Arrayende eingebaut hast, und zwar scheinst Du die Schleife bei Überschreiben einer bestimmten Länge zu unterbrechen:
if (nCommandPos > 30) ...

Aber der ganze Aufbau von Datenstruktur und Programmlogik scheint mir völlig verquer zu sein:

char*  httpServer()
...
char sReturnCommand[32];
...
return sReturnCommand

Rückgabewert für die Funktion httpServer() soll ein Ponter sein.
Du deklarierst innerhalb der Funktion ein Char-Array (Gültigkeit des Char-Arrays also nur innerhalb der Funktion)
Und lieferst einen Pointer auf das Char-Array zurück.

Wenn Du hinterher irgendwelche Aktionen mit dem auf diese Art zurückgelieferten Pointer ausführst, z.B. Stringvergleiche, dann ist nicht sichergestellt, dass da das abläuft, was ablaufen soll. Da das definierte Array mit verlassen der Funktion ungültig geworden ist, ist der der Speicherbereich danach als frei gekennzeichnet. Der nächste auftretende Interrupt kann genau diesen frei gewordenen Speicherbereich wieder für etwas anderes verwenden.

Das sieht für mich völlig fehlerhaft programmiert aus, wenn Du versuchst mit Pointern auf ungültig gewordene Variablenbereiche außerhalb des Gültigkeitsbereichs zuzugreifen.

Das würde ich jedenfalls nochmal überprüfen, wo Du möglicherweise mit Pointern auf RAM-Speicher zugreifst, dass das alles seine Richtigkeit hat.

Sepron:
.....
Ich habe gelesen, dass man einen Pin mit reset brücken kann und ihn nach einer Reset den Neustart durchführen lässt, was aber nicht sonderlich gesund wäre.

....

Nun das geht auch rein Softwaremäßig, und zwar so:
asm("JMP 0"); // Und Sprung nach RESET

So mache ich einen RESET nachdem ich die SD-Karte entfernt hatte und wieder neu initialisieren will...das braucht auch noch
einen Reset weil die SD-Lib da einen Bug hat.