Arduino Ethernet - RAM zu gering? VirtualWire + WebRequest

Hallo,

ich habe die letzten Tage an Funk-Temperatursensoren gearbeitet, die ihre Daten per 433 Mhz an einen Arduino Ethernet schicken.
Dieser soll die Daten an ein PHP-Webscript übergeben.

Die Nodes funktioniert fehlerfrei. Ich habe mir also einzeln zwei Sketches gebaut; einmal um die Daten per 433 zu empfangen - das funktioniert - und einen Sketch mit einer Funktion, um diese empfangenen Daten an das PHP-Script zu übergeben.

Beide einzeln funktionieren prima - führe ich beide in einem Sketch zusammen funktioniert die GET-Anfrage über die Funktion connect() nicht mehr.

Ich habe nach und nach einzelne Zeilen auskommentiert und festgestellt: Sobald die Zeilen:

  vw_setup(2000);                   // Bits per sec
  vw_set_rx_pin(RX_Pin);           // We will be receiving on pin 4 i.e the RX pin from the module connects to this pin. 
  vw_rx_start();                      // Start the receiver

auskommentiert sind, werden meine Daten auch wieder an das Webscript übergeben.

Mal der ganze Code - ich hoffe gut kommentiert. Die Zeilen die auskommentiert sind gehören zur VirtualWire-Lib; wenn auskommentiert, funktioniert die Übergabe an das PHP-Script:

// https://gist.github.com/SergXIIIth/1628715

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

// PIN, an den der 433 Mhz Empfänger angeschlossen ist
int RX_Pin = 8;

byte mac[] = { 
  0x00, 0xAB, 0xCB, 0xCD, 0xDE, 0x02 };
byte ip[] = { 192,168,2,177 };
byte subnet[] = { 255,255,255,0 };
byte gateway[] = { 192,168,2,1 };
byte dns1[] = { 192,168,2,1 };

char server[] = "www.3bm.de";

EthernetClient client;

void setup() {
  Serial.begin(9600);

  Serial.print("Setup LAN ... ");
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Ethernet.begin(mac, ip, dns1, gateway, subnet);
  Serial.println("ok");

  // VirtualWire initialisieren
  //vw_setup(2000);                   // Bits per sec
  //vw_set_rx_pin(RX_Pin);           // We will be receiving on pin 4 i.e the RX pin from the module connects to this pin. 
  //vw_rx_start();                      // Start the receiver 
}

void loop(){
  //uint8_t buf[VW_MAX_MESSAGE_LEN];
  //uint8_t buflen = VW_MAX_MESSAGE_LEN;

  // Daten-Variable, die die empfangenen Daten hält
  //char SensorDataTemp[24];

  // Daten-Variable, zum Übergeben der Daten an die WebRequest-Funktion
  //String SensorData;

  // Empfang von Daten prüfen
//  if (vw_get_message(buf, &buflen)) {
//    int i;
//    // Datagramm zusammensetzen
//    for (i = 0; i < buflen; i++) {
//      // Serial.print((char)buf[i]);                     // the received data is stored in buffer
//      SensorDataTemp[i] = (char)buf[i];
//    }
//
//    // Char-Variable terminieren
//    SensorDataTemp[buflen] = '\0';
//    //    Serial.print("Daten empfangen: ");
//    //    Serial.print(SensorDataTemp);
//    //    Serial.println();
//
//    // Char to String-Konvertierung zur Übergabe an die connect()-Funktion 
//    SensorData = SensorDataTemp;
//
//    // Console-Output zum Debuggen
//    Serial.print("Daten empfangen: ");
//    Serial.print(SensorData);
//    Serial.println();
//  }

  Serial.println("Daten schicken...");
  String dataTEST = "1;TEMP1:88.00";
  connect(dataTEST);
  
  delay(60000);
}


void connect(String payload){
  if (client.connect(server, 80)) {
    Serial.print("Anfrage senden an ");
    Serial.print(server);

    client.println("GET /xxx/index.php?data="+payload+"&key=xxx HTTP/1.0");
    client.println("HOST: 3bm.de");
    client.println();

    client.stop();
    Serial.println();
    Serial.println("Daten uebermittelt.");
    Serial.println("--- Getrennt ---");
    Serial.println();

  }
  else {
    // kf you didn't get a connection to the server:
    Serial.println("!!! Verbindung fehlgeschlagen");
  }


  // DEBUGGING!  
//     while (client.available()) {
//      char c = client.read();
//      Serial.print(c);
//   }
  //
  //  // if the server's disconnected, stop the client:
//   if (!client.connected()) {
//      Serial.println();
//      Serial.println("Trennen.");
//      Serial.println("----------------------");
//      client.stop();
//    }
}

Ich habe nach folgenden Zeilen ein Serial.println("Geht los"); hinzugefügt:

void connect(String payload){

Und genau soweit komme ich - bis zu client.connect() - danach passiert auch nichts mehr; d.h. der SerialOutput hört einfach auf.

Ich meine soetwas ähnliches schonmal gehabt zu haben - und vermute, dass der RAM voll ist? Wenn ja, was kann ich da den jetzt tun?!

Mach mal alle Strings bei println() mit dem F-Makro, so dass das im Flash steht:
Serial.println(F("text"));

Bei dem dynamisch zusammengesetzten String geht das natürlich nicht, wobei du da vielleicht auch einfach 3 mal print() aufrufen kannst.

Den RAM Verbrauch kannst du hiermit testen:

int freeMemory() 
{
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Und dann einfach:
Serial.println(freeMemory());

Habe überall mal F() hinzugefügt und an mehreren Stellen den freeMemory() ausgeben lassen.
Auch meine GET-Anfrage habe ich aufgesplittet:

void connect(String payload){
  Serial.println(freeMemory());
  if (client.connect(server, 80)) {
    Serial.print(F("Anfrage senden an "));
    Serial.print(server);

    client.print(F("GET /xxx/index.php?data="));
    client.print(payload);
    client.print(F("&key=xxxx HTTP/1.0"));
    client.println(F("HOST: 3bm.de"));
    client.println();

    client.stop();
    Serial.println();
    Serial.println(F("Daten uebermittelt."));
    Serial.println();

  }

Beim Aufruf obiger Funktion aus der loop() bekomme ich als FreeMemory-Wert VOR dem Aufruf von client.connect() 1335 Byte als freien Speicher ausgegeben - und dann passiert wieder nichts mehr.

Kann ich davon ausgehen, dass es sich tatsächlich um ein RAM-Problem handelt? Was beansprucht den "viel RAM" in meinem Script? Also, das Schreiben auf die serielle Konsole, o.ä.? Also Dinge, die ich ggf. irgendwie "optimieren" kann?

Wenn ich mir die Doku der VirtualWire Lib (VirtualWire: VirtualWire library for Arduino and other boards) so anschaue, scheint diese per default pin 11 und pin 12 für RX und TX zu verwenden, die aber vom Ethernetshield verwendet werden.
Du setzt explizit RX auf Pin 8, aber veränderst TX nicht. Evtl. wird ja beim Initialisieren der VirtualWire Lib auf Pin 11 zugegriffen, was wiederum die SPI Konfig für die Ethernetlib durcheinanderbringen könnte.
Versuch mal zusätzlich auch TX für die VirtualWire Lib auf ein anderes Pin zu setzen (z.B. 7). Evtl. hilft das ja schon.
Mario.

Mit 1,3k RAM hast du noch viel frei. Sollte nicht daran liegen :slight_smile:

neovanmatix:
//uint8_t buflen = VW_MAX_MESSAGE_LEN;
...
//char SensorDataTemp[24];
...
// SensorDataTemp[buflen] = '\0';

Pufferüberläufe sind schnell programmiert.

Und wenn Du mit einem zu großen Index auf ein Array zugreifst und "hinter" dem deklarierten Array irgendwelche Werte im RAM-Speicher veränderst, brauchst Du Dich nicht zu wundern, wenn ein Programm nicht mehr läuft oder ein unerwartetes Verhalten zeigt.

VW_MAX_MESSAGE_LEN ist größer als 23 oder?
In dem Fall schreibst Du "irgendwo im RAM hinter dem SensorDataTemp-Array" eine Null ins RAM.
Wahrscheinlich dort, wo keine Null hingehört.

jurs:

neovanmatix:
//uint8_t buflen = VW_MAX_MESSAGE_LEN;
...
//char SensorDataTemp[24];
...
// SensorDataTemp[buflen] = '\0';

Pufferüberläufe sind schnell programmiert.

Und wenn Du mit einem zu großen Index auf ein Array zugreifst und "hinter" dem deklarierten Array irgendwelche Werte im RAM-Speicher veränderst, brauchst Du Dich nicht zu wundern, wenn ein Programm nicht mehr läuft oder ein unerwartetes Verhalten zeigt.

VW_MAX_MESSAGE_LEN ist größer als 23 oder?
In dem Fall schreibst Du "irgendwo im RAM hinter dem SensorDataTemp-Array" eine Null ins RAM.
Wahrscheinlich dort, wo keine Null hingehört.

Okay, das ist ja schonmal ein Ansatz.
D.h. ich initialisiere meine SensorDataTemp-Variable (und die normale SensorData) mit Länge VW_MAX_MESSAGE_LEN+1 und schreibe in die letzte Stelle das \0 - wäre das dann so "richtig"?

VW_MAX_MESSAGE_LEN wurde von mir nicht manuell gesetzt; sollte also ein DefaultWert der VirtualWire-Lib sein?

Der Ansatz mit dem Standard-PIN für RX/TX hört sich sinnvoll an und würde erklären, weshalb entweder das eine, oder das andere funktioniert. Werde ich nachher gleich Testen - danke, da wäre ich nie drauf gekommen! :slight_smile:

neovanmatix:
Okay, das ist ja schonmal ein Ansatz.
D.h. ich initialisiere meine SensorDataTemp-Variable (und die normale SensorData) mit Länge VW_MAX_MESSAGE_LEN+1 und schreibe in die letzte Stelle das \0 - wäre das dann so "richtig"?

Also damit bist Du eher auf der sicheren Seite, um eine Null ins RAM zu schreiben:

  //char SensorDataTemp[VW_MAX_MESSAGE_LEN+1];
...
//    SensorDataTemp[VW_MAX_MESSAGE_LEN] = '\0';

Du kannst auch mit memset(SensorDataTemp, 0, VW_MAX_MESSAGE_LEN+1) das ganze Array auf 0 setzten, dann geht es auch wenn mal weniger drin steht.

Hallo,

die guten Ratschläge haben leider nicht geholfen :confused:

  1. Habe einen TX-Pin für die VirtualWire-Lib angegeben, der NICHT am Arduino gesteckt ist
  2. Habe das Char-Array mit der VW_MAX_MESSAGE_LEN initialisiert - viel hat sich am RAM-Verbrauch aber nicht getan
  3. Habe mal die VirtualWire-Lib VOR der Ethernet-Lib inkludiert - hätte ja sein können, dass sich da etwas überschreibt...

Hier mal mein aktuelles Script; habe einige Debugging-Kommentare rausgemacht, damit es etwas lesbarer ist:

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

int RX_Pin = 8; // PIN, an den der 433 Mhz Empfänger angeschlossen ist
int TX_Pin = 9; // Einen leeren PIN angeben, da die VirtualWire-Lib sonst einen Pin belegt, der für den Ethernet-Chip genutzt wird
int PTT_Pin = 7;

byte mac[] = { 
  0x00, 0xAB, 0xCB, 0xCD, 0xDE, 0x02 };
byte ip[] = { 192,168,2,177 };
byte subnet[] = { 255,255,255,0 };
byte gateway[] = { 192,168,2,1 };
byte dns1[] = { 192,168,2,1 };

char server[] = "www.3bm.de";

EthernetClient client;

void setup() {
  Serial.begin(9600);

  Serial.print(F("Setup LAN ... "));
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Ethernet.begin(mac, ip, dns1, gateway, subnet);
  Serial.println("ok");

  // VirtualWire initialisieren
  vw_setup(2000);                      // Bits per sec
  vw_set_ptt_pin(PTT_Pin);
  vw_set_rx_pin(RX_Pin);               // We will be receiving on pin 4 i.e the RX pin from the module connects to this pin. 
  vw_set_tx_pin(TX_Pin);
  vw_rx_start();                       // Start the receiver 
}

void loop(){
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;

  // Daten-Variable, die die empfangenen Daten hält
  char SensorDataTemp[VW_MAX_MESSAGE_LEN+1];

  // Daten-Variable, zum Übergeben der Daten an die WebRequest-Funktion
  String SensorData;

  // Empfang von Daten prüfen
  if (vw_get_message(buf, &buflen)) {
    int i;
    // Datagramm zusammensetzen
    for (i = 0; i < buflen; i++) {
      // Serial.print((char)buf[i]);                     // the received data is stored in buffer
      SensorDataTemp[i] = (char)buf[i];
    }

    // Char-Variable terminieren
    SensorDataTemp[VW_MAX_MESSAGE_LEN] = '\0';

    // Char to String-Konvertierung zur Übergabe an die connect()-Funktion 
    SensorData = SensorDataTemp;

    // Console-Output zum Debuggen
    Serial.print(F("Daten empfangen: "));
    Serial.print(SensorData);
    Serial.println();
    
    Serial.println(freeMemory());
    Serial.println(F("Daten schicken..."));

    connect(SensorData);
    
    Serial.println(F("Daten geschickt!"));
  }

  delay(30000);
}


void connect(String payload){
  Serial.println(freeMemory());
  if (client.connect(server, 80)) {
    Serial.print(F("Anfrage senden an "));
    Serial.print(server);

    client.print(F("GET /xxx/index.php?data="));
    client.print(payload);
    client.print(F("&key=xxx HTTP/1.0"));
    client.println(F("HOST: 3bm.de"));
    client.println();

    client.stop();
    Serial.println();
    Serial.println(F("Daten uebermittelt."));
    Serial.println();

  }
  else {
    Serial.println(F("!!! Verbindung fehlgeschlagen"));
  }
}

int freeMemory() 
{
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Der Output im Serial Monitor sieht so aus:

Setup LAN ... ok
Daten empfangen: 1;VCC:5004
1131
Daten schicken...
1109

Sprich, wie zuvor, springt er in die connect()-Funktion rein, scheitert aber vor oder bei dem client.connect().

Wenn's nicht am RAM liegt, und auch nicht der der VirtualWire-Lib - woran könnte es den noch liegen? Habe ich irgendwo grobe Schnitzer drin, weshalb sich der Ardu aufhängt?

Update: Habe etwas recherchiert und auch den Pin für PTT auf einen freien Abgeändert. Den Sketch oben habe ich aktualisiert. Funktioniert leider trotzdem nicht...

Update
Zeichen und Wunder... erklären kann ich's mir nicht; funktionieren tut's auch noch nicht wirklich; aber zumindest funktioniert der WebRequest nun.
Was ich getan habe? Ich habe einfach das Ethernet.begin HINTER das vw_rx_start() gesetzt. Und nun geht's auch weiter. Komische Sache :confused: