Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 12 of 12

Thread: w550 + Teensy LC Max speed

  1. #1
    Junior Member
    Join Date
    Aug 2017
    Posts
    12

    w550 + Teensy LC Max speed

    Hi,
    I've got a Teensy LC connected to a wiznet W550 using the Ethernet library.

    I've edited the library slightly so when I call client.read everything happens as it should, except I've commented out the actual data read.
    This is because I'm trying to get the max speed the network can provide.

    I'm getting some interesting results and I was wondering if anyone could help please?

    When I host a http server on my laptop, and download a 10MB test file from the local network, I get the following results:

    4 sockets (small buffer) 4181kbps
    1 socket (biggest buffer) 11766kbps

    Which obviously shows the buffer size is affecting the max speeds. From this I would assume that I could download a file from the internet, and my internet connection speed of max 1000kbps (i know it's cheap) would be the bottleneck, not the W550.
    However, when I download a file from a number of servers hosting the same 10MB test file I get the following results NOTE my laptop maxes out as expected at 1000kbps from these servers:

    4 sockets 91kbps
    1 max socket 500kbps

    This shows the W550 is the bottleneck, and the connection speed is limited by the buffer size and Teensy speed.

    Isn't this weird? If the W550 can download 10mbps on the local network, why is it bottlenecking at 500kbps when downloading from the internet? How are these situations changing its max download?

    I'd appreciate it if anyone could shed some light on the situation.

    Code:
    /*
      Web client
    
     This sketch connects to a website (http://www.google.com)
     using an Arduino Wiznet Ethernet shield.
    
     Circuit:
     * Ethernet shield attached to pins 10, 11, 12, 13
    
     created 18 Dec 2009
     by David A. Mellis
     modified 9 Apr 2012
     by Tom Igoe, based on work by Adrian McEwen
    
     */
    
    #include <SPI.h>
    #include <Ethernet.h>
    
    // Enter a MAC address for your controller below.
    // Newer Ethernet shields have a MAC address printed on a sticker on the shield
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
    
    // if you don't want to use DNS (and reduce your sketch size)
    // use the numeric IP instead of the name for the server:
    //https://www.futurehosting.com/network-test-files/
    //IPAddress server(87,76,31,34);  // numeric IP for futurehosting
    //IPAddress server(192,168,1,248);
    //char server[] = "speedtest.tele2.net";    // name address for Google (using DNS)
    //char server[] = "london.futurehosting.com";
    //char server[] = "www.google.com";
    char server[] = "www.ovh.net";
    
    // Set the static IP address to use if the DHCP fails to assign
    IPAddress ip(192, 168, 0, 177);
    IPAddress myDns(192, 168, 0, 1);
    
    // Initialize the Ethernet client library
    // with the IP address and port of the server
    // that you want to connect to (port 80 is default for HTTP):
    EthernetClient client;
    
    // Variables to measure the speed
    unsigned long beginMicros, endMicros;
    unsigned long byteCount = 0;
    bool printWebData = true;  // set to false for better speed measurement
    
    void setup() {
      // You can use Ethernet.init(pin) to configure the CS pin
      //Ethernet.init(10);  // Most Arduino shields
      //Ethernet.init(5);   // MKR ETH shield
      //Ethernet.init(0);   // Teensy 2.0
      //Ethernet.init(20);  // Teensy++ 2.0
      //Ethernet.init(15);  // ESP8266 with Adafruit Featherwing Ethernet
      //Ethernet.init(33);  // ESP32 with Adafruit Featherwing Ethernet
    
      // Open serial communications and wait for port to open:
      Serial.begin(9600);
      while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
      }
    
    
    
      // start the Ethernet connection:
      Serial.println("Initialize Ethernet with DHCP:");
      if (Ethernet.begin(mac) == 0) {
        Serial.println("Failed to configure Ethernet using DHCP");
        // Check for Ethernet hardware present
        if (Ethernet.hardwareStatus() == EthernetNoHardware) {
          Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
          while (true) {
            delay(1); // do nothing, no point running without Ethernet hardware
          }
        }
        if (Ethernet.linkStatus() == LinkOFF) {
          Serial.println("Ethernet cable is not connected.");
        }
        // try to congifure using IP address instead of DHCP:
        Ethernet.begin(mac, ip, myDns);
      } else {
        Serial.print("  DHCP assigned IP ");
        Serial.println(Ethernet.localIP());
      }
    
      // give the Ethernet shield a second to initialize:
      delay(1000);
      Serial.print("connecting to ");
      Serial.print(server);
      Serial.println("...");
    
      // if you get a connection, report back via serial:
      if (client.connect(server, 80)) {
        Serial.print("connected to ");
        Serial.println(client.remoteIP());
        // Make a HTTP request:
        client.println("GET /files/100Mb.dat");
        //client.println("Host: speedtest.tele2.net");
        //client.println("Host: 192.168.1.248");
        client.println("Host: www.ovh.com");
        client.println("Connection: close");
        client.println();
      } else {
        // if you didn't get a connection to the server:
        Serial.println("connection failed");
      }
      beginMicros = micros();
    }
    
    void loop() {
    
      // if there are incoming bytes available
      // from the server, read them and print them:
      if(client.available()) {
        uint16_t byteLen = client.readNoRead();
        //Serial.println(byteLen);
        byteCount = byteCount + byteLen;
        //Serial.write(client.read());
        //cpuLoadSleep();
      }
    
      
    
      // if the server's disconnected, stop the client:
      if (!client.connected()) {
        endMicros = micros();
        Serial.println();
        Serial.println("disconnecting.");
        client.stop();
        Serial.print("Received ");
        Serial.print(byteCount);
        Serial.print(" bytes in ");
        float seconds = (float)(endMicros - beginMicros) / 1000000.0;
        Serial.print(seconds, 4);
        float rate = (float)byteCount / seconds / 1000.0;
        Serial.print(", rate = ");
        Serial.print(rate);
        Serial.print(" kbytes/second");
        Serial.println();
    
        // do nothing forevermore:
        while (true) {
          delay(1);
        }
      }
    }
    ethernetclient.cpp implements new function

    Code:
    int EthernetClient::readNoRead()
    {
    	return Ethernet.socketRecvNoRead(sockindex);
    }
    and socket.cpp implements recvnoread

    Code:
    int EthernetClass::socketRecvNoRead(uint8_t s)
    {
    	uint16_t len = 65535;
    	// Check how much data is available
    	int ret = state[s].RX_RSR;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (ret < len) {
    		uint16_t rsr = getSnRX_RSR(s);
    		ret = rsr - state[s].RX_inc;
    		state[s].RX_RSR = ret;
    		//Serial.printf("Sock_RECV, RX_RSR=%d, RX_inc=%d\n", ret, state[s].RX_inc);
    	}
    	if (ret == 0) {
    		// No data available.
    		uint8_t status = W5100.readSnSR(s);
    		if ( status == SnSR::LISTEN || status == SnSR::CLOSED ||
    		  status == SnSR::CLOSE_WAIT ) {
    			// The remote end has closed its side of the connection,
    			// so this is the eof state
    			ret = 0;
    		} else {
    			// The connection is still up, but there's no data waiting to be read
    			ret = -1;
    		}
    	} else {
    		if (ret > len) ret = len; // more data available than buffer length
    		uint16_t ptr = state[s].RX_RD;
    		//if (buf) read_data(s, ptr, buf, ret);
    		ptr += ret;
    		state[s].RX_RD = ptr;
    		state[s].RX_RSR -= ret;
    		uint16_t inc = state[s].RX_inc + ret;
    		if (inc >= 250 || state[s].RX_RSR == 0) {
    			state[s].RX_inc = 0;
    			W5100.writeSnRX_RD(s, ptr);
    			W5100.execCmdSn(s, Sock_RECV);
    			//Serial.printf("Sock_RECV cmd, RX_RD=%d, RX_RSR=%d\n",
    			//  state[s].RX_RD, state[s].RX_RSR);
    		} else {
    			state[s].RX_inc = inc;
    		}
    	}
    	SPI.endTransaction();
    	//Serial.printf("socketRecv, ret=%d\n", ret);
    	return ret;
    }
    To mess with the buffer sizes, you need to edit Ethernet.h

    and uncomment

    Code:
    #define ETHERNET_LARGE_BUFFER
    and change the max socket num define to 1 or 4 as necessary

  2. #2
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,623
    you might want to use wireshark to watch TCP traffic from your W5500 for different W5500 socket buffer size, and read about TCP delay-bandwidth product.

    throughput = TCP buffersize/RTT

    so for your non-local tests, throughput will be affected by round-trip time to server. A long RTT means you'll need really big buffer.

    for 2KB buffer size and 80 ms RTT, max throughput would be about 205 kbps

    congestion and packet loss over the Internet will further degrade throughput, and you are guaranteed not to go faster than the max speed of SPI on your LC. For small data transfers, TCP slow-start will limit throughput.
    Last edited by manitou; 02-08-2020 at 05:54 PM.

  3. #3
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    Thanks for the info! That's all exactly what I needed to know

    If I can't change the round trip or the receive buffer. I guess there's nothing I can do to bottleneck the network over the controller. Except by maybe faking the RWIN? and heavily messing with the W550 to send the number of bytes received to Teensy.

    Cheers for the help

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,093
    You can edit the ethernet library to use larger buffers. W5500 has 16K for each direction, and by default supports up to 8 simultaneous sockets. That's why the default buffer size is 2K. Larger buffers mean fewer sockets, which usually isn't an issue if you're only using 2 of them (for client and DHCP).

  5. #5
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    Cheers for the info Paul,
    Yeah I've set the max socket number to 1 to use max buffer size, achieving 500kbps.

    I'd like to hit 10mbps ideally. Maybe I need an ethernet controller with a bigger buffer. Microchip do one with 24Kb buffer..

  6. #6
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    Reading about the Round trip time and max tcp buffer size. Could I get round this problem by using UDP? Then the buffer wouldn't be the bottleneck but the network would be

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,093
    Using UDP would involve crafting your own protocol to manage the speed. You won't escape the fundamental problem of window size buffering. Odds are slim you'll reinvent a wheel anywhere near as good as TCP.

    I've only worked with Microchip's parts briefly. The one I tried didn't do TCP internally, so that's left to library code on the microcontroller. Again, the window size issue is a fundamental issue, and Teensy LC has very little memory. If you go down that path, you'll probably need a board with more memory.

  8. #8
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,623
    Quote Originally Posted by Rotario View Post
    Reading about the Round trip time and max tcp buffer size. Could I get round this problem by using UDP? Then the buffer wouldn't be the bottleneck but the network would be
    UDP can be effective on a local ethernet. On the Internet, blasting UDP packets is "socially unacceptable" -- your LC/host may be considered mounting a denial-of-service attack! Also UDP is unreliable -- packets can be lost, re-ordered, duplicated, damaged. TCP provides a reliable data stream across the Internet. As Paul notes, creating a rate-controlled reliable UDP stream of packets is a wheel you probably don't want to re-invent.

  9. #9
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    Quote Originally Posted by PaulStoffregen View Post
    Using UDP would involve crafting your own protocol to manage the speed. You won't escape the fundamental problem of window size buffering. Odds are slim you'll reinvent a wheel anywhere near as good as TCP.

    I've only worked with Microchip's parts briefly. The one I tried didn't do TCP internally, so that's left to library code on the microcontroller. Again, the window size issue is a fundamental issue, and Teensy LC has very little memory. If you go down that path, you'll probably need a board with more memory.
    Hi Paul,
    The max buffer size gets me about 430kBps, which is also close to the theoretical limit

    16000(kB buf) * 35(ms round trip) ~= 430

    So either I need to reduce round trip (not possible if I want to make this portable physically) or increase buffer size. 16kb is max buffer size for w5500 right?

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,093
    Yes, 16K is the max buffer size for W5500.

  11. #11
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    And I guess the receive window parameter sent during the TCP handshake can't be fudged at all to artificially increase the bandwidth limit?

  12. #12
    Junior Member
    Join Date
    Aug 2017
    Posts
    12
    Quote Originally Posted by manitou View Post
    UDP can be effective on a local ethernet. On the Internet, blasting UDP packets is "socially unacceptable" -- your LC/host may be considered mounting a denial-of-service attack! Also UDP is unreliable -- packets can be lost, re-ordered, duplicated, damaged. TCP provides a reliable data stream across the Internet. As Paul notes, creating a rate-controlled reliable UDP stream of packets is a wheel you probably don't want to re-invent.
    Thanks for the info - Really helpful!

    If I control the server and the client (LC) then is it still considered suspect? Obviously the ISP can see the massive amount of packets I'm sending between them.

    Would another way be to fudge the Receive Window (RWIN) greater than the actual buffer size available? The LC can read off the data much faster than the packets can be ACK'd, so a larger RWIN would increase the theoretical max bandwidth. I could do an ICMP ping, get the round trip time (RTT) then calculate a reasonable RWIN value that the data can be read off, and the bottleneck is no longer the TCP receive buffer size.

    Would this work? Or am I misunderstanding the TCP process. I don't know if the RWIN can be manually changed on the W5500. Maybe I could at least test it by intercepting the packet and changing the value in something like wireshark?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •