Ethernetshield sendet nicht - ungewöhnliches Verhalten

Hallo allerseits,

mein Heizduino :slight_smile: Projekt läuft recht gut, die bisherigen Probleme sind beseitigt und der Mega stürzt (bisher) nicht mehr ab.
Ich habe aber Schwierigkeiten mit dem Übermitteln der Daten zu Cosm. Den Arduino-Client habe ich in meinen Sketch eingebaut, es läuft nach den Strart ganz gut. Nach einigen Stunden/Tagen kommt bei Cosm aber nichts mehr an. Arduino läuft aber weiterhin ohne Absturz weiter, die Temperaturen werden im LCD-Display weiterhin neu angezeigt und mein rudimentäre Fehlerstatus läuft durch (kein Aufhängen).

Nun das Ungewöhnliche: Wenn ich das USB-Kabel anschliesse und den seriellen Monitor starte, sehe ich den String zu COSM und die Werte beginnen wieder bei Cosm einzugehen.
Unten habe ich die von der Loop aufgerufenen Funktionen zusammengefaßt.

Habt Ihr vielleicht eine Idee, warum es diese Abbrüche gibt?

Grüße
Michael


Zuerst die Variablen

byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// assign an IP address for the controller:
IPAddress ip(192,168,178,46);
// initialize the library instance:
EthernetClient client;
char server[] = "api.cosm.com";
//IPAddress server(216,52,233,121);


byte gateway[] = {
  192,168,178,1};	
byte subnet[] = { 
  255, 255, 255, 0};

Daten zu Cosm senden, die KEys etc. sind zuvor definiert

void sendData() // zu COSM
{lcd.setCursor(5,0); lcd.print(F("p"));
	// this method makes a HTTP connection to the server:
	// if there's a successful connection:
    if (client.connect(server, 80)) 
	{
    
    Serial.println(F("connecting..."));
    // send the HTTP PUT request:
    client.print(F("PUT /v2/feeds/"));
    client.print(FEEDID);
    client.println(F(".csv HTTP/1.1"));
    client.println(F("Host: api.pachube.com"));
    client.print(F("X-PachubeApiKey: "));
    client.println(APIKEY);
    client.print(F("User-Agent: "));
    client.println(USERAGENT);    
    
    client.print(F("Content-Length: "));
    client.println(strlen(temp_string), DEC);
    // war client.println(temp_string.length(), DEC);
	// last pieces of the HTTP PUT request:
    client.print(F("Content-Type: text/csv\n"));
    client.println(F("Connection: close\n"));

    // here's the actual content of the PUT request:
    Serial.println(temp_string);
    client.println(temp_string);
   
    // note the time that the connection was made:
    lastConnectionTime = millis();
	}
	else 
	{
    // if you couldn't make a connection:
    Serial.println(F("connection failed"));
    Serial.println();
    Serial.println(F("disconnecting."));
    client.stop();
	}
lcd.setCursor(5,0); lcd.print(F("P"));
}
void daten_zu_cosm()
{lcd.setCursor(5,0); lcd.print(F("q"));
  // if there's incoming data from the net connection.
  // send it out the serial port.  This is for debugging
  // purposes only:
	while (client.available()) 	
	{
     char c = client.read();
     Serial.print(c);
    } 
  // if there's no net connection, but there was one last time
  // through the loop, then stop the client:
	if (!client.connected() && lastConnected) 
	{
		Serial.println();
		Serial.println(F("disconnecting.."));
		client.stop();
	}

  // if you're not connected, and ten seconds have passed since
  // your last connection, then connect again and send data:
    if(!client.connected() && (millis() - lastConnectionTime > postingInterval)) 
	{
      cosm_daten_zusammenstoepseln();
      sendData(); // temp_string wird verschickt
	}
	// store the state of the connection for next time through
	// the loop:
	lastConnected = client.connected();
lcd.setCursor(5,0); lcd.print(F("Q"));  
}

Der Ausschnitt des Setup

Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


 // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) 
	{
		Serial.println(F("Failed to configure Ethernet using DHCP"));
		// DHCP failed, so use a fixed IP address:
		Ethernet.begin(mac, ip);
	}
  
  // -----------------------------------------

  Wire.begin(); // für den Anschluss der Expander-Karte 
  delay(1000);

michael--g:
Nun das Ungewöhnliche: Wenn ich das USB-Kabel anschliesse und den seriellen Monitor starte, sehe ich den String zu COSM und die Werte beginnen wieder bei Cosm einzugehen.

Wenn Du den seriellen Monitor startest, macht Dein Arduino einen Reset und startet neu.
Der funktioniert dann wieder, weil er resettet wurde.

Zeig mal Deine Deklaration von "temp_string" vor und die Funktion "cosm_daten_zusammenstoepseln();", sind da vielleicht String-Objekte drin? Oder hast Du alles mit char-Arrays realisiert? Kann es vielleicht sein, dass irgendwo Objekte vom Typ "String" zum Einsatz in Deinem Code kommen?

Hallo,

ja, die arrays... habe ich alle rausgenommen.
Hier das Zusammenstopseln.

Also ein Reset, hmmm. Dann muss ich wohl mit meinen Fehlercodes das irgendwie abfangen (der Mega stürzt als solcher nicht ab und durchläuft die loop)...

char test[4];
char tmp[10];
void cosm_daten_zusammenstoepseln()
{lcd.setCursor(5,0); lcd.print(F("r"));
//  Serial.println("daten_zusammenstoepseln!");  

  strcpy (temp_string,"sensor0");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[0],3,2,tmp)));
  strcat(temp_string,"\n");

  strcat (temp_string,"sensor1");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[1],3,2,tmp)));
  strcat(temp_string,"\n");

  strcat (temp_string,"sensor2");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[2],3,2,tmp)));
  strcat(temp_string,"\n");

  strcat (temp_string,"sensor3");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[3],3,2,tmp)));
  strcat(temp_string,"\n");

  strcat (temp_string,"sensor4");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[4],3,2,tmp)));
  strcat(temp_string,"\n");

  strcat (temp_string,"sensor5");    strcat(temp_string,",");
  strcat(temp_string,(dtostrf(TEMP_SENSOR[5],3,2,tmp)));
  strcat(temp_string,"\n");

 
  strcat(temp_string,"relais0");    strcat(temp_string,",");
  strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,0),test,10)));
  strcat(temp_string,"\n");

  strcat(temp_string,"relais1");    strcat(temp_string,",");
  strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,1),test,10)));
  strcat(temp_string,"\n");

  strcat(temp_string,"relais3");    strcat(temp_string,",");
  strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,3),test,10)));
  strcat(temp_string,"\n");

  strcat(temp_string,"relais4");    strcat(temp_string,",");
  strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,4),test,10)));
  strcat(temp_string,"\n");

  strcat(temp_string,"relais6");    strcat(temp_string,",");
  strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,5),test,10)));
  strcat(temp_string,"\n");

  strcat(temp_string,"manuell");    strcat(temp_string,",");
  strcat(temp_string,(itoa(arbeitsmodus,test,10)));
  strcat(temp_string,"\n");
  
lcd.setCursor(5,0); lcd.print(F("R"));
}

Die Deklaration von "temp_string" sehe ich jetzt aber immer noch nirgends?
Ist das auch ein char-Array? Lang genug?

michael--g:
char tmp[10];
...
strcat(temp_string,(dtostrf(TEMP_SENSOR[0],3,2,tmp)));

Und bei diesem Zusammenstöpseln mit dtostrf(), ist da sichergestellt, dass in den TEMP_SENSOR-Daten wirklich nur zu 100% sicher überprüfte Temperaturwerte drinstehen? Sagen wir mal Werte zwischen -99.9 und 99.9?

Weil wenn da auch nur einmal irgendein Quatsch drinsteht mit einer zu grossen Zahl, dann formatiert Dir dtostrf() gnadenlos über das Ende des char-Arrays hinaus einen Buffer-Overflow. Denn dtostrf() nimmt sich die Länge, die es braucht.

Ein Aufruf mit

float f;
char tmp[10];
f=12345678901234567890.123;
Serial.println(dtostrf(f,3,2,tmp));
delay(4000);

liefert einen schönen Quatsch auf dem Bildschirm.
Und mit der Deklaration "char tmp[25];" wird es immerhin ein 23 Zeichen langer String, der ausgegeben wird.

Wenn es nicht sicher ist, dass nur gültige Temperaturwerte formatiert werden, sondern ggf. eine Variable auch mal Datenmüll enthalten kann, dann würden Deine Formatierungen nur mit zusätzlichen Prüfungen einen Pufferüberlauf zur Laufzeit des Programms verhindern:

if ((TEMP_SENSOR[0]>-100.0)&&(TEMP_SENSOR[0]<100.0))
dtostrf(TEMP_SENSOR[0],3,2,tmp);
else
strcpy(tmp,"***");

Wenn Du überall im Programm einen so leichtsinnigen Programmierstil pflegst, mit der Möglichkeit eines potentiellen Buffer-Overflows bei Funktionsaufrufen, und Du andererseits dann irgendwo an anderer Stelle im Programm vergisst, diese Variablen entsprechend auf ihren Gültigkeitsbereich zu prüfen, die woanders durch die leichtsinnige Programmierung zum Buffer-Overflow führen können, dann kann aus einem potentiellen Buffer-Overflow auch schnell mal ein tatsächlicher Buffer-Overflow werden. Mit entsprechender Fehlfunktion im weiteren Programmverlauf, weil falsche Variablenbereiche überschrieben wurden.

jurs:
Die Deklaration von "temp_string" sehe ich jetzt aber immer noch nirgends?
Ist das auch ein char-Array? Lang genug?

Ja, es ist ein
char temp_string[200];

char tmp[10];
...
strcat(temp_string,(dtostrf(TEMP_SENSOR[0],3,2,tmp)));

Und bei diesem Zusammenstöpseln mit dtostrf(), ist da sichergestellt, dass in den TEMP_SENSOR-Daten wirklich nur zu 100% sicher überprüfte Temperaturwerte drinstehen? Sagen wir mal Werte zwischen -99.9 und 99.9?

Eine Überprüfung habe ich nicht vorgesehen, da der LM35 Werte zwischen 0 und 100 liefert und über mehrere Werte gemittelt wird. Aber das ist egal, auf Deinen Rat hin habe ich es eingebaut - macht Sinn. Ich habe das Ergebnis beim Lesen der Sensoren auf 0 und 99 gestutzt. Diese Werte werden niemals real erreicht und ich kann mit diesen Werten über Cosm einen Twitter o.ä. auslösen (später :-).

... Ausführungen zu Buffer-Overflow

Gehe voll mit.

Danke für Deinen hilfreichen Ratschlag, habe ich umgesetzt.
Im Fall meines aktuellen Cosm-Problems bin ich aber noch immer nicht weiter. Wenn jemand eine Idee hat, bitte nur zu!

Grüße
Michael

michael--g:
Im Fall meines aktuellen Cosm-Problems bin ich aber noch immer nicht weiter. Wenn jemand eine Idee hat, bitte nur zu!

Also ich verstehe Deinen Sketch soweit, dass er Daten per HTTP an einen Server im Internet sendet.

Wartest Du dann irgendwo auch die Antwort des Servers ab, der die Daten bekommt?
Wertest Du die Serverantwort aus?

Und was mir auffällt: client.stop(); wird nur im Fehlerfall ausgeführt, wenn kein connect zum Server zustande kommt. Brauchst Du kein client.stop(); ausführen, wenn der Server die Daten erhalten, verarbeitet und seine Serverantwort an Dich zurückgesendet hat?

Wartest Du dann irgendwo auch die Antwort des Servers ab, der die Daten bekommt?
Wertest Du die Serverantwort aus?

Und was mir auffällt: client.stop(); wird nur im Fehlerfall ausgeführt, wenn kein connect zum Server zustande kommt. Brauchst Du kein client.stop(); ausführen, wenn der Server die Daten erhalten, verarbeitet und seine Serverantwort an Dich zurückgesendet hat?

Ehrlich gesagt bin ich damit überfordert; die Ansprache von Cosm habe ich praktisch 1:1 aus dem beispielhaften Sketch übernommen, den Cosm anbietet und der auch im IDE des Arduino als Beispiel angeführt ist. Ursprünglich dachte ich, ich hätte noch irgendwoanders einen Fehler. Aber auf Deinen Vorschlag hin, werde ich mich mit dem client.stop und dem, was dahinter steckt beschäftigen müssen. Irgendwie gibt es überall Baustellen :-/ Aber man wächst an seinen Aufgaben und Fehlern :wink:

Die Ausgabe des Servers wird im ser. Montor ausgegeben; ich versuche mal, mich dem zu nähern... Ich berichte. Und solange jemand den Fehler auf Anhieb erkennt, schreibe nur zu, ich bohre im Dunkelen.

Grüße
Michael

michael--g:
die Ansprache von Cosm habe ich praktisch 1:1 aus dem beispielhaften Sketch übernommen, den Cosm anbietet und der auch im IDE des Arduino als Beispiel angeführt ist.

Wenn man fremden Gratis-Code aus dem Internet herunterlädt, kann es nichts schaden, diesen Code zu durchschauen, um ihn ggf. verbessern und korrigieren zu können.

Manchmal gilt für Gratis-Code nämlich: You get what you pay for!
:wink:

Manchmal gilt für Gratis-Code nämlich: You get what you pay for!
:wink:

Genau so ist es! Habe nachgedacht :wink: und eine Codezeile verändert.

	// war: if (!client.connected() && (millis() - lastConnectionTime > postingInterval)) 
	if (millis() - lastConnectionTime > postingInterval)

Seitr fünf Tagen läuft der Mega ohne abzustürzen und die Verbindung zu verlieren!
Juhu!

Danke insb. für die Tipps zur Vermeidung der Stringklasse!

Jetzt versuche ich die Daten auch lokal zu sichern, um sie bei Bedarf auszuwerten. Habe nun den FTP-Server der Fritzbox im Auge...
Leider klappt es mit dem Store nicht richtig - dann könnnte ich meine Daten ganz einfach dort ablegen. Ich berichte...

Grüße
Michael