Using the ASIX USB Ethernet controller with the Teensy 4.1

This blog post documents how a ASIX USB Ethernet adapter can be connected to a Teensy 4.1 board. As USB ethernet adapter, the widely supported (Linux drivers exist) adapter from Olimex is used. It contains the ASIX USB ethernet controller chip.

The Teensy board contains native ethernet support. This article is not about the native ethernet on the teensy but rather about the native USB host controller support that the teensy has. This article describes how to connect a ASIX USB Ethernet adapter to the USB controller on the teensy 4.1 to send ethernet packets to an IP address.

The FNET library contains an example called ASIXEthernet_Test.ino. This example can be executed on the Teensy 4.1 via the Arduino IDE.

The FNET example imports the ASIX driver for the Teensy which is contained in this repository. So first, install the FNET library and the TeensyASIXEthernet library via .zip or via the library manager into your Arduino IDE.

The teensy 4.1 comes in several offers. One option is to buy a Teensy that already has pin headers soldered in by a professional so you do not have to solder anything. If you have a teensy 4.1 that has no pins soldered to the USB contacts on the board, solder five pins in. This page contains pictures on where the USB cables are connected on the Teensy 4.1 and this shows you where to solder the USB pin header in. If you turn the Teensy 4.1 around, the bottom of the PCB contains the pin names on the bottom silk screen. USB On the Go added a fifth pin. This fifth pin is just connected to ground on the host side of a normal USB connection. For this example, only four of the five pins are required. The pins are 5V (red), D- (white), D+ (green) and ground (black).

As usual soldering makes better contact than just loosely sticking pin headers into the holes. The contact is only really established through the solder! If your setup does not work (No USB or ethernet connection), check your soldering again! For myself, it only worked after fixing my solder work once.

The next step is to connect the ASIX USB ethernet controller to the USB pins. For that, you can purchase USB adapter cables that have headers on one side and a USB port connector (MAKE SURE TO NOT ACCIDENTLY BUY A USB PLUG CABLE BUT A USB PORT CABLE!) on the other side. A depiction of such a cable is given here. Plug in the ASIX USB ethernet adapter in either directly or indirectly via a USB hub if you like. Connect a ethernet wire to the adapter and connect it to your home network that should contain a DHCP server so that the FNET stack can retrieve an IP address.

In the Arduino IDE open the FNET example sketch ASIXEthernet_Test.ino. Compile it and upload it to the teensy 4.1. Do not forget to change the target hardware to the Teensy 4.1 board in your Arduino IDE. If you target the wrong board, the headers used in the example sketch will not be found.

Unplug and reconnect the Teensy to your computer via USB to give it power. Open the serial monitor and wait. The example sketch will start the FNET stack in the background, this will take some time and there is no output whatsoever that tells you to wait. Once the stack is done, it will retrieve a IP address via DHCP and output something similar to this:

SetMACAddress: 00001001B3BD
netif Initialized
Initializing DHCP
DHCP initialization done!
IPAddress: 0.0.0.0
SubnetMask: 0.0.0.0
Gateway: 0.0.0.0
DHCPServer: 0.0.0.0
State: 2
IPAddress: 192.168.0.10
SubnetMask: 255.255.255.0
Gateway: 192.168.0.1
DHCPServer: 192.168.0.1
State: 5

The LEDs inside the USB Ethernet adapter casing should begin to light up and flicker.

Once you see this output, it is time to instruct the Teensy to send ethernet packets. In order to do that, the example sketch uses the FNET benchmark code. You can start the benchmark by sending a command to the Teensy via the Serial Monitor’s send feature. The benchmark has several options so here is one example command:

benchtx -a 192.168.0.234 udp -m 1272

First of all: You have to send a space after the last character. So before sending this command, make sure there is a trailing space! The parser of the FNET benchmark code is very peculiar about the input it wants to accept! If there is no space, the command is rejected!

The benchmark tools command line is:

benchtx -a <remote_ip> [tcp|udp] [-m <message size>] [-mn <number of messages>]

For me, the last optional flag -mn never was accepted by the parser hence the example command above does not specify a message number. The default message number of 10000 messages is used. The message size default value is 1272. The example command uses UDP for no specific reason. The remote IP is the IP of the device in your local network that should receive the ethernet packets.

The FNET benchmark code will blast out 10000 packets as fast as it can not careing about packet loss or anything. It will not wait in between packets and it will output a single line of statistics about the burst send after it is done.

Megabytes: 0.587664  Seconds: 0.1250  KBits/Sec: 37610.4960

In a running instance of wireshark on the remote machine, you can see some of the 10000 packets arrive. If you have a means to count the incoming packets, you could check how many of the UDP packets actually made it and you could compute the success rate of the burst transfer.

Receiving Ethernet Packets

For receiving ethernet packets over the ASIX USB Ethernet Adapter, the code in the ASIX example has to be modified! The code contains a variable called MacAddress.

uint8_t MacAddress[6] = {0x00,0x50,0xB6,0xBE,0x8B,0xB4};

The MacAddress variable contains the six byte long MacAddress that is used to participate in an Ethernet Network. The MacAddress uniquely identifies the Ethernet Adapter in an Ethernet Network just as the IP-Address identifies a node in an IP Network.

In contrast to an IP Address, which has local scope (per subnet and local LAN) and which can be dynamically leased by a DHCP server and hence is not necessarily unique globally, a MacAddress is a globally unique identifier.

Every Ethernet capable device gets a unique MacAddress assigned by the vendor or manufacturer of the device. The Olimex Adapter has a unique MacAddress. This MacAddress differs from the MacAddress that is part of the ASIX example code!

Using the MacAddress from the ASIX example without change causes the example code to send Ethernet frames into the network that contain this incorrect MacAddress. Communication partners will then answer using that incorrect MacAddress. The Olimex Adapter will see the ethernet frames and it will compare it’s own MacAddress to the one contained in the frames. It will decide that those frames are directed at another participant because the MacAddresses do not match! It will then discard those frames and your code will not ever receive even a single frame!

To solve the problem, determine the MacAddress of your particular Olimex adapter and update the example code with that MacAddress. One way to determine the MacAddress is to plug the adapter into a windows host and on the command line, execute the ipconfig /all command with lists the MacAddress.

One tip for safety, keep you MacAddresses off the Internet, that means do not check in your MacAddress into a git repository, just as you do not check in any passwords into git repositories. If someone really wants to hurt you, commiting your MacAddress to git connects the MacAddress to your person or git account which opens up a way for attackers.

Sending and Receiving Speeds

For sending out Ethernet frames, your code can send as fast as possible because the performance of the Teensy 4.1 CPU and the speed of the USB controller and ethernet PHY naturally limit the amount of packets send. Packets will most likely not be lost during the send operation. The chips will process the packets when they get to it.

On the receiving side, I encountered problems. Sending from a MAC Book Pro which has a fast CPU and a really high quality ethernet chip can easily overpower the Teensy 4.1 with the Olimex Adapter attached. Packets where dropped and not received. I assume that because USB is a polled bus that if your system does not poll fast enough, ethernet frames are just dropped.

Another test showed that when connecting the Olimex Adapter to a powerfull business laptop running windows, the same effect can be observed. Even on capable hardware, the Olimex Adapter drops frames.

To test the situation, I used the code fromĀ https://www.vankuik.nl/2012-02-09_Writing_ethernet_packets_on_OS_X_and_BSD

Here, a byte array of data is sent to a MacAddress of your choice, which is the Olimex adapter’s MacAddress in this test. The byte array is larger that the maximum allowed length of a single ethernet frame hence the code constructs several frames and sends them out. In this test, four frames are send. The variable in the test is the amount of time theĀ  code sleeps (usleep(uint microseconds)) between each of the four ethernet frames.

Using wireshark, on the sender’s side, it is checked how many frames are really sent by the test program. Using wireshark on the receiver’s side, it is checked how many packets are actually received.

The breaking point for the Olimex adapter seems to be a sleep time of 1 ms. Sleeping more than 1 ms causes the Windows Machine to receive every single frame correctly.

Going down to 1 ms and lower, causes the Windows machine to not receive all packets. Because this effect shows on the windows machine and on the Teensy, I have to assume that it is a problem with the Olimex USB adapter and not in the example code or in windows. It might also be because USB is a polled bus, but the Business laptop should be able to poll the USB Adapter faster than the Teensy but still the same effect is noticeable.

With a lossless protocol such as TCP, lost packets will be retransmitted and the transfer should be possible even if the sender overwhelmes the receiver. With a lossy ethernet or UDP connection, this is a real problem!

Conclusion

The amazing thing about all this is that you have the code for an open source TCP/IP stack (FNET) and a working ASIX driver (https://github.com/vjmuzik/TeensyASIXEthernet) that works over USB.

For learning about the USB protocol, I personally feel that this setup is a very motivating one because it allows you to learn about USB, TCP/IP and the low-level driver development in one go. This might be too much at once and become overwhelming pretty quickly but I think being overwhelmed is better than loosing interest because of a lack of exiting experiments.

Another big plus is that the USB connection to the ethernet adapter allows you to port the ASIX driver to any embedded system that has a USB host controller and that you want to connect to ethernet. This will work even if the embedded device has no native ethernet support or even if it has native ethernet support but there is no open source driver for the ethernet controller chip or if you do not understand the open source driver yet.

Anyways, I hope you took something away from reading this article. Thank you for your interest in my page.


Leave a Reply