Speicherproblem (?) bei Ethernet Modul ENC28J60

Hallo!

ich habe den folgenden Beispielcode aus der Ethershield-Bibliothek ; mein großes Problem damit: Wenn die anzuzeigende WebSite zu groß wird, d.h. wenn print_webpage einen Wert > 1500 zurückgibt, wird die Seite im Browser nicht angezeigt, sondern es kommt zum Timeout. Der Sketch läuft jedoch weiter, das hab ich im Terminalfenster beobachtet. buffer_size habe ich hochgesetzt, das hat nur eine Auswirkung bis 1500. Wenn plen > 1500 ist und ich buffer_size analog erhöhe, wird die Seite trotzdem nicht angezeigt. Hat jemand eine Idee, was das sein könnte? Ich benutze einen Mega 2560.

Vielen Dank für Tipps...

#include "etherShield.h"

// please modify the following two lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24}; 
static uint8_t myip[4] = {192,168,0,15};
static char baseurl[]="http://192.168.0.15/";
static uint16_t mywwwport =80; 				// listen port for tcp/www (max range 1-254)

#define BUFFER_SIZE 500
static uint8_t buf[BUFFER_SIZE+1];
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];

EtherShield es=EtherShield();

// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage(uint8_t *buf);
int8_t analyse_cmd(char *str);
void setup(){
  
	/* Disable SD card */
  	pinMode(4, OUTPUT);
  	digitalWrite(4, HIGH);
  	
   	/*initialize enc28j60*/
	es.ES_enc28j60Init(mymac);
   	es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
   	delay(10);
        
	/* Magjack leds configuration, see enc28j60 datasheet, page 11 */
	// LEDA=greed LEDB=yellow
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
	// 0x880 is PHLCON LEDB=on, LEDA=on
	// enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x880);
	delay(500);
	//
	// 0x990 is PHLCON LEDB=off, LEDA=off
	// enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
	es.ES_enc28j60PhyWrite(PHLCON,0x990);
	delay(500);
	//
  // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
	delay(100);
        
  //init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);


}

void loop(){
  uint16_t plen, dat_p;
  int8_t cmd;

  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

	/*plen will ne unequal to zero if there is a valid packet (without crc error) */
  if(plen!=0){
	           
    // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
    if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
      es.ES_make_arp_answer_from_request(buf);
      return;
    }

    // check if ip packets are for us:
    if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
      return;
    }
    
    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
      es.ES_make_echo_reply_from_request(buf,plen);
      return;
    }
    
    // tcp port www start, compare only the lower byte
    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==mywwwport){
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
         es.ES_make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack
         return;     
      }
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
        es.ES_init_len_info(buf); // init some data structures
        dat_p=es.ES_get_tcp_data_pointer();
        if (dat_p==0){ // we can possibly have no data, just ack:
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
            es.ES_make_tcp_ack_from_any(buf);
          }
          return;
        }
        if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
          	// head, post and other methods for possible status codes see:
            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
            goto SENDTCP;
        }
 	if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
                plen=print_webpage(buf);
            goto SENDTCP;
         }
        cmd=analyse_cmd((char *)&(buf[dat_p+5]));
        if (cmd==1){
             plen=print_webpage(buf);
        }
SENDTCP:  es.ES_make_tcp_ack_from_any(buf); // send ack for http get
           es.ES_make_tcp_ack_with_data(buf,plen); // send data       
      }
    }
  }

uint16_t print_webpage(uint8_t *buf)
{
        uint16_t plen;
     		plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Welcome to Arduino Ethernet Shield V1.0  </h1></p> "));
       	plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<hr>
 <h2><font color=\"blue\">-- Put your ARDUINO online -- "));
 				plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("
 Control digital outputs "));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("
 Read digital analog inputs HERE "));
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("  
</font></h2> ") );
        plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</center><hr>  V1.0 <a href=\"http://www.ekitszone.com\">www.ekitszone.com<a>"));
  
        return(plen);
 }        
}

Dein Problem ist die MTU. (Maximum Transmission Unit – Wikipedia)
Also die maximale Größe der Nutzdaten in deinem Paket. Da Dein Sketch das komplette TCP-Handling übernehmen muss, mußte Du mehr als ein Paket an den Client (Browser) schicken. Sprich die Daten in 1500 Byte große Stücke zerlegen und diese dann verschicken.
Grüße,
Mario.

Vielen Dank, ja das ist es, nachvollziehbar. Aber die Pakete zerlegen, hat das hier schonmal jemand gemacht? Ich schaue gerade wie der Hamster ins Uhrwerk :wink:

Also als erstes würde ich den Code für das Abschicken der TCP Pakete in die Funktion print_webpage() verlagern, denn dort kannst Du schon beim Füllen des Puffers mitzählen wie groß der ist. Ist "plen" plus die Länge der nächsten zu schreibenden Daten > 1500, dann Paket abschicken und den Puffer von vorne Füllen.
Damit würde auch das aus meiner Sicht unsinnige "Goto" verschwinden. Denn eigentlich scheint es egal was im "GET" Parameter steht, es wird immer die gleiche Seite ausgegeben.
Leider postest Du nur den Beispielcode, nicht Deinen eigenen, daher ist es schwierig, Empfehlungen zu geben wo man das am besten unterbringt.
Mario.

Ich habe mehrere Web-Seiten, und somit mehrere Funktionen wie print_webpage(). Je nach Programmzustand wird eine davon aufgerufen und danach die beiden Befehle:

        if (mode == 9) plen = print_timeset(buf);
        else if (mode == 8) plen = print_slumberset(buf);
        else if (mode == 7) plen = print_setDcf77(buf);
        else if (mode == 6) plen = print_settings(buf);
        else plen = print_webpage(buf);
SENDTCP:  
        es.ES_make_tcp_ack_from_any(buf); // send ack for http get
        es.ES_make_tcp_ack_with_data(buf,plen); // send data

Ich habe jetzt mal probiert, provisorisch buf zu füllen, dann mit den beiden Befehlen es.ES_... zu schreiben, danach buf wieder zu füllen und den Rest zu schreiben. Das wirkt sich dann so aus, dass der erste Teil im Browser geladen wird, beim zweiten Teil passiert im Browser nichts mehr. Ich nehme an, dass mit dem Befehl ES_make_tcp_ack_with_data(buf,plen); der Request beantwortet ist und keine Daten mehr hinterhergeschickt werden können.

Das geht zurück auf die Funktion make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen) aus ip_arp_udp_tcp.c. Da steht im Kommentar auch etwas dazu:

// you must have called init_len_info at some time before calling this function
// dlen is the amount of tcp data (http data) we send in this packet
// You can use this function only immediately after make_tcp_ack_from_any
// This is because this function will NOT modify the eth/ip/tcp header except for
// length and checksum
void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen)
{
        uint16_t j;
        // fill the header:
        // This code requires that we send only one data packet
        // because we keep no state information. We must therefore set
        // the fin here:
        buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V;

...

Was Du leider machen musst ist die Datenübertragung selbst steuern. Das ist leider bei TCP recht komplex. (Transmission Control Protocol – Wikipedia)
Poste mal bitte einen Link zu der Bibliothek die Du benutzt, evtl. gibst das schon was passendes.
Mario.

Hallo :slight_smile:

Von hier habe ich die Bibliothek:

http://www.ekitszone.com/Products/8-enc28j60-ethernet-shield-for-arduino-mega-mega2560.aspx

Kennst Du eine Bibliothek, wo schon implementiert ist dass bei einer Datenmenge > 1500 Byte mehrere Pakete verschickt werden? Letztlich bin ich bei dieser Bibliothek gelandet, weil keine andere mit dem enc28j60-Breakout auf dem Mega funktioniert hat, obwohl ich immer die Pins, die beim Mega anders sind als beim "normalen" Arduino, angepasst habe. Ich bin jetzt nicht zuhause, heute abend kann ich mal schreiben wie ich ihn angeschlossen habe.

Das ist das Modul, der Vollständigkeit halber:

Das Anpassen der Lib könnte eine recht umfangreiche Aufgabe werden. Soweit ich sehen konnte, gibt es noch keinen Code der mehr als ein TCP-Paket als Antwort schickt.
Auch wenn es blöd klingt, aber evtl. kannst Du Dich ja doch zum folgendem Produkt durchringen:
http://www.komputer.de/zen/index.php?main_page=product_info&cPath=22&products_id=140
Der Wiz5100 Chip bringt nämlich den kompletten TCP/IP Stack schon mit.
Grüße,
Mario.

Nein, das klingt nicht blöd, ich denke Du hast Recht. Ich werde in dieser Richtung weitermachen! Vielen Dank für die Meinung :slight_smile:

Gruß Thomas