T4.1 Ethernet Library

I tried many different combinations, and the behaviour is always the same. The first write() goes in the first package. Then the next few writes() are packed together in the second package, and its transmitted around 40ms after the first. Then the third is typically the same. And after this, all seems to stabilize and there are no more delays. Even if i only do 2 small writes(), in the Teensy prints it says they took like 10uS, but in Wireshark I see the the second package being sent 40ms after the first one. Could the library maybe be waiting for more data to fill a frame or something like that? And when it actually has enough data it does not realize it and only sends the frame after a timeout? Now I'm just guessing... :)

Let me know your thoughts, and if you'd like to make any other tests let me know.
you didn't provide all of your sketch, so one can't tell if you are using TCP or UDP. TCP has several algorithms that affect data clustering (slow-start, Nagle, delayed ACK). At start of TCP connection, TCP slowly increases the window size to avoid flooding the network, eventually the TCP "flow-control" will stabilize around the bandwidth-delay product. TCP uses Nagle to avoid sending lots of tiny packets. With a small amount of data to send, TCP will often wait a bit to see if app is going to send more data. So what you are observing is probably normal TCP behavior. UDP will allow you to avoid such delays, but at the cost of reliability. UDP packets can be lost, duplicated, arrive out of order, ...
 
Last edited:
you didn't provide all of your sketch, so one can't tell if you are using TCP or UDP.

Oh right sorry wrote so much stuff and forgot to specify the protocol. It's using TCP. The sketch is basically the ChatServer from the examples, and when a client connects and writes something the server responds using the functions I quoted, performing many writes() really fast.

TCP has several algorithms that affect data clustering (slow-start, Nagle, delayed ACK)

I did some more tests just now and realizing that what is causing the delay most times is in fact the ACK from the first package is not send instantly. So I guess it is normal behaviour. If I do writes() in 1460bytes blocks to fill the frame it usually sends the ACK instantly. That should work for me in most of the cases. Is there a way we can request the receiver to send the acks asap?
 
Is there a way we can request the receiver to send the acks asap?

It depends on the library. You may need to explore the FNET lib and look for TCP_NODELAY. The BSD socket lib has a socket option for TCP_NODELAY that disables Nagle. lwIP lib has a config option for TCP_NODELAY.
 
I have some problems with OSC (over UDP). As long, as the packet size is <1024 Byte, I can receive the data (as long as I send them 1 message per ms). If the packet size is greater, then I receive nothing. I've the buffer-size etc. accordingly:
Code:
  Ethernet.setStackHeap(1024 * 128);
  Ethernet.setSocketSize(1460 * 8); //Set buffer size
  Ethernet.setSocketNum(2); //Change number of allowed sockets 

in loop:
  if ( (size = Udp.parsePacket()) > 0)
  {
    while (size--)
    {
      byte r = Udp.read();
      inmsg.fill(r);
    }
  }
'size' is 0, when message length is >1024. Any idea, what's happening? I did the same transfer on a Bela system, without any troubles.
 
I have successfully sent and received UDP packets up to 2000 bytes, so I think FNET/NativeEthernet is doing IP fragmentation. I used the following function to send to a UDP echo server.
Code:
void udp_echo(int n) {
  uint32_t  t1, t2;
  static int lost = 0; // for static ip, first pkt not sent?
  t1 = micros();
  Udp.beginPacket(MyServer, dstport);   //uechosrv
  Udp.write(packetBuffer, n);
  Udp.endPacket();

  while (!Udp.parsePacket()) {
    // wait to see if a reply is available
    if (micros() - t1 > 1000000) {
      lost++;
      Serial.print("lost "); Serial.println(lost);
      return;
    }
  }
  // We've received a packet, read the data from it
  int bytes = Udp.read(packetBuffer, n); // read the packet into the buffer
  t2 = micros() - t1;
  Serial.printf("%d bytes  RTT %d us\n", bytes, t2);
}

you might use wireshark to confirm size of incoming pkts. Did you setSocketSize() before Ethernet.begin()?
 
Last edited:
I used tcpview and pktmon to verify the packet size. And yes, I called setSocketsize upfront. Today I tested and found the max size as of 1576. 2048 crashed the Teensy
 
You're right. Pure message length of 2028 works for me too. I forgot, that OSC has some overhead bytes, which caused the crash.
 
Code:
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>


/*
  UDPReceiveOSC
  Set a tone according to incoming OSC control
                            Adrian Freed
*/

//UDP communication


EthernetUDP Udp;
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
}; // you can find this written on the board of some Arduino Ethernets or shields

//the Arduino's IP
IPAddress ip(192, 168, 1, 177);
const unsigned int inPort = 8888;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Ethernet.setStackHeap(1024 * 128);
  Ethernet.setSocketSize(16000); //Set buffer size
  Ethernet.setSocketNum(2); //Change number of allowed sockets to 2
  Ethernet.begin(mac, ip);
  Udp.begin(inPort);
  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.");
  }
  Serial.println("UDP server running");
}
void loop() {
  int size;
  if ( (size = Udp.parsePacket()) > 0)
  {
    Serial.println("received " + String(size));
  }
}
With this code, the outer limits are at 10212 Byte per message. 10214 don't work!
 
Alright it was a config issue where it was exceeding the max packet size, I pushed an update to increase it to 32KB instead of 10KB. Seeing as I believe this is a UDP only issue I refrained from setting it the max value of 64KB-1, but you are free to set it to that should you so need it.
https://github.com/vjmuzik/FNET
 
I just ported an app from a DUE/Ethernet shield combo to the Teensy 4.1. Except for me causing my own problems by putting a lib in the wrong place, the port went extremely smoothly. The NativeEthernet lib worked perfectly without any changes to my program except for the include statement. Thanks for the quality code vjmuzik, I really appreciate it! I am a hobbyist, not an Engineer, so I a really at the mercy of the quality of the libraries.
Thanks,
Len
 
Depending on your modem/router it can do both 100Mbit and 10Mbit, I believe the speed tests that we’ve done show it achieving 70-90Mbit after overheads.
 
intentare probar con otro Router.

Por otro lado. existe alguna libreria que me permita conectar 2 Teensy41 via ethernet en la capa fisica ?
 
This should be possible with FNET, but I haven't tested it nor have I made an easy way to do it with the NativeEthernet library.
 
hola...

he estado realizando pruebas con la libreria <NativeEthernet.h> y <NativeEthernetUdp.h>.
he notado que el Teensy41 se ocupa en algunos procesos internos de la librería.
el tiempo medido en un osciloscopio es aproximadamente 50uS.

esta linea de codigo parece estar generando el salto.
Udp.begin(localPort);

el codigo de prueba es:

void loop()
{
digitalToggleFast(13);
delayMicroseconds(10);
}

se supone que ese codigo deberia generar una onda cuadrada de 10uS en bajo y 10uS en alto, pero aveces tarda 50uS. parece ser que el Teensy41 sale a atender alguna interrupcion.

¿alguien tiene idea de como puedo evitar esos cortes de 50uS?
 
With the NativeEthernet library by default it's setup to interrupt every millisecond to attend to the stack, this is most likely what's causing the extra time taken between square waves, ideally for a square wave you would want to use PWM anyways without the use of delays. If you really needed something time critical you could just create an interrupt yourself for every 10 microseconds with a higher priority so that it doesn't get overridden.
 
Hi, I was looking through NativeEthernet.cpp and found this function:

void EthernetClass::setSocketNum(uint8_t _socket_num){
if(socket_num != 0) return;
if(socket_num > FNET_CFG_SOCKET_MAX) socket_num = FNET_CFG_SOCKET_MAX;
else socket_num = _socket_num;
}

it seems in order to get past the first if statement, socket_num must be 0, so then why does the second if statement check if socket_num > FNET_CFG_SOCKET_MAX, if the value is already known to be zero? Is the second if supposed to be _socket_num instead of socket_num? Sorry if I'm completely misunderstanding the code.

Thanks
 
Hi, I was looking through NativeEthernet.cpp and found this function:

void EthernetClass::setSocketNum(uint8_t _socket_num){
if(socket_num != 0) return;
if(socket_num > FNET_CFG_SOCKET_MAX) socket_num = FNET_CFG_SOCKET_MAX;
else socket_num = _socket_num;
}

it seems in order to get past the first if statement, socket_num must be 0, so then why does the second if statement check if socket_num > FNET_CFG_SOCKET_MAX, if the value is already known to be zero? Is the second if supposed to be _socket_num instead of socket_num? Sorry if I'm completely misunderstanding the code.

Thanks

_socket_num and socket_num are entirely different things, note. This looks like the code has been updated without realizing
it can be simplified. The zero test is to prevent multiple calls re-setting the value I think, probably a later addition to the codebase
 
_socket_num and socket_num are entirely different things, note. This looks like the code has been updated without realizing
it can be simplified. The zero test is to prevent multiple calls re-setting the value I think, probably a later addition to the codebase

I think you're right about the history of the code. But it feels like perhaps the intent was to do limit-checking on the input parameter to the function, and so this is a typo? Perhaps such limit-checking is not necessary, and always choosing the 'else' path is perfectly fine in all situations.
 
The first if statement is to make sure you the number you entered is not 0 because you need to have at least 1 socket. The second if statement makes sure you haven’t entered more sockets than FNET is configured to use since it wouldn’t work if you tried to. So if you make it past those checks it’ll set the number of available sockets to the value you entered.
 
Back
Top