Teensy 4.1 with ESP32 (AirLift): Very Low Wi-Fi Transfer Speeds

i everyone,

I'm facing an issue integrating the Teensy 4.1 with an ESP32 (AirLift) for Wi-Fi communication, and I’d like to know if anyone has managed to achieve reasonable transfer speeds with this setup.

  • Current setup:
    • Teensy 4.1 connected to an ESP32 (AirLift) via SPI.
    • Using the WiFiNINA library to handle communication.
    • Ping works fine, with low latency.
  • Goal:
    • Transfer files from the Teensy’s SD card to another device over Wi-Fi.
  • Results obtained:
    • Using either HTTP or FTP servers over Wi-Fi, transfer speeds don’t exceed 100 KB/s.
    • In many cases, the communication unexpectedly freezes.

Observations:​

  • Replacing the ESP32 with the Teensy 4.1’s native Ethernet (using the Ethernet_native library), I can achieve transfer speeds close to 10 MB/s.
  • The issue seems to be with Wi-Fi communication, as the hardware works well with Ethernet.
  • I’ve tried adjusting the buffer size (testing values like 64, 128, 256 bytes), but I noticed that buffers larger than 128 cause transfer failures, such as interruptions or freezes.

Question:​

  • Has anyone successfully used the Teensy 4.1 with a Wi-Fi interface (like the AirLift) and achieved satisfactory file transfer speeds?
  • Are there any tips to optimize Wi-Fi communication in this setup?
  • Could this be an inherent limitation of the ESP32 in AirLift mode, or might I be overlooking something?
I’d really appreciate any help or insights you can share!

Thanks
Daniel
 
My code using NativeEthernet.h

Code:
#include <Arduino.h>
#include <SPI.h>
#include <NativeEthernet.h>
#include <SdFat.h>
// Settings for the Teensy 4.1
#define SD_CONFIG SdioConfig(FIFO_SDIO)  // Configuration for SDIO with FIFO
SdFs sd;                                // SdFat file system
FsFile file;                            // File type from the SdFat library
const size_t BUFFER_SIZE = 2048*2;  // Read buffer size (512 bytes)
// Ethernet configuration
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 2, 200);
EthernetServer server(80);
void setup() {
  // Serial initialization
  Serial.begin(9600);
  while (!Serial) {
    ;  // Waits for USB connection
  }
  Serial.println("Ethernet WebServer with SdFat");
  // Ethernet initialization
  Ethernet.begin(mac, ip);
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet hardware not found.");
    while (true) {
      delay(1);
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable not connected.");
  }
  server.begin();
  Serial.print("Server started at: ");
  Serial.println(Ethernet.localIP());
  // SD initialization
  Serial.println("Initializing SD card...");
  if (!sd.begin(SD_CONFIG)) {
    Serial.println("Failed to initialize the SD card!");
    while (true) {
      delay(1);
    }
  }
  Serial.println("SD card initialized successfully.");
}
void loop() {
  // Waits for client connections
  EthernetClient client = server.available();
  if (client) {
    client.setTimeout(1000);  // 10-second timeout
    Serial.println("Client connected");
    // Sends the file to the client
    sendFile(client, "/file.exe");
    delay(1);
    client.stop();  // Closes the connection
    Serial.println("Client disconnected");
  }
}
void sendFile(EthernetClient &client, const char *filePath) {
  // Opens the file on the SD
  if (!file.open(filePath, O_RDONLY)) {
    Serial.println("Error: File not found");
    client.println("HTTP/1.1 404 Not Found");
    client.println("Content-Type: text/plain");
    client.println();
    client.println("File not found");
    return;
  }
  // Sends HTTP headers
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: application/octet-stream");
  client.println("Content-Disposition: attachment; filename=\"259781k.zip\"");
  client.println("Content-Length: " + String(file.size()));
  client.println();
  // Read buffer
  uint8_t buffer[BUFFER_SIZE];
  size_t bytesRead;
  // Loop to read the file and send it in chunks
  while ((bytesRead = file.read(buffer, sizeof(buffer))) > 0) {
    client.write(buffer, bytesRead);  // Sends the chunk to the client
  }
  file.close();  // Closes the file
  Serial.println("Transfer complete");
}

(edited to add code formatting)
 
Last edited by a moderator:
and my code using WiFiNINA

Code:
#include <Arduino.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include <SdFat.h>
#define SPIWIFI       SPI   // The SPI port
#define SPIWIFI_SS 10       // Chip select pin
#define SPIWIFI_ACK 9       // a.k.a BUSY or READY pin
#define ESP32_RESETN 8      // Reset pin
#define ESP32_GPIO0 7       // gpio0
//#define WIFI_MODE_CLIENT
#define WIFI_MODE_AP
#define USE_WIFI_NINA
#define ssid "Network123"
#define pass "Network123"
#define ssid_ap "Teensy_AP"
#define pass_ap "123456789"
int status = WL_IDLE_STATUS;
// Settings for the Teensy 4.1
#define SD_CONFIG SdioConfig(FIFO_SDIO)  // Configuration for SDIO with FIFO
SdFs sd;                                // SdFat file system
FsFile file;                            // File type from the SdFat library
const size_t BUFFER_SIZE = 64;          // Read buffer size (512 bytes)
WiFiServer server(80);
void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("WiFi Pins Configured");
  WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI);
 
  while (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    delay(1000);
  }
  String fv = WiFi.firmwareVersion();
  if (fv < "1.0.0") {
    Serial.println("Please upgrade the firmware");
  }
  Serial.print("Found firmware version: ");
  Serial.println(fv);
  // By default, the local IP address will be 192.168.4.1
  // You can override it with the following:
  WiFi.config(IPAddress(192, 168, 88, 143));
#ifdef WIFI_MODE_CLIENT
    // Connect to the Wi-Fi network as a client (STA mode)
    while (status != WL_CONNECTED) {
      Serial.print("Attempting to connect to WPA SSID: ");
      Serial.println(ssid);
      status = WiFi.begin(ssid, pass);  // Connect to the Wi-Fi network
      delay(2000);                      // Wait for connection
    }
    Serial.println("Connected to Wi-Fi network as Client");
#else
    // Create an Access Point (AP)
    Serial.print("Creating access point named: ");
    Serial.println(ssid_ap);  // AP name
    status = WiFi.beginAP(ssid_ap, pass_ap);  // Start the Access Point
    if (status != WL_AP_LISTENING) {
      Serial.println("Creating access point failed");
      while (true);  // If failed, halt the program
    }
    Serial.println("Access Point created successfully");
#endif
  Serial.print("You're connected to the network.");
  server.begin();
  Serial.print("Server started at: ");
  Serial.println(WiFi.localIP());
 
  // SD card initialization
  Serial.println("Initializing SD card...");
  if (!sd.begin(SD_CONFIG)) {
    Serial.println("Failed to initialize the SD card!");
    while (true) {
      delay(1);
    }
  }
  Serial.println("SD card initialized successfully.");
}
void loop() {
  // Wait for client connections
  WiFiClient client = server.available();
  if (client) {
    client.setTimeout(10000);  // 10-second timeout
    Serial.println("Client connected");
    // Send the file to the client
    sendFile(client, "/file.exe");
    delay(1);
    client.stop();  // Close the connection
    Serial.println("Client disconnected");
  }
}
void sendFile(WiFiClient &client, const char *filePath) {
  // Open the file on the SD
  if (!file.open(filePath, O_RDONLY)) {
    Serial.println("Error: File not found");
    client.println("HTTP/1.1 404 Not Found");
    client.println("Content-Type: text/plain");
    client.println();
    client.println("File not found");
    return;
  }
  // Send HTTP headers
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: application/octet-stream");
  client.println("Content-Disposition: attachment; filename=\"259781k.zip\"");
  client.println("Content-Length: " + String(file.size()));
  client.println();
  // Read buffer
  uint8_t buffer[BUFFER_SIZE];
  size_t bytesRead;
  // Loop to read the file and send it in chunks
  while ((bytesRead = file.read(buffer, sizeof(buffer))) > 0) {
    client.write(buffer, bytesRead);  // Send the chunk to the client
  }
  file.close();  // Close the file
  Serial.println("Transfer complete");
}
void printWifiStatus() {
  // Print the SSID of the network you're connected to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  // Print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  // Print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI): ");
  Serial.print(rssi);
  Serial.println(" dBm");
}

(edited to add code formatting)
 
Last edited by a moderator:
Tip: you can make your code much more readable by putting them in a code block. See the “</>” button. Personally, I don’t often dive into long code snippets if they’re not formatted in an easy-to-read way.
 
What is the default SPI bitrate ??? It looks like you don't change it in the code. You setup the Wifi, but no bitrate change.
 
Tip: you can make your code much more readable by putting them in a code block. See the “</>” button. Personally, I don’t often dive into long code snippets if they’re not formatted in an easy-to-read way.
Hello Shawn,
Sorry about the formatting of my post. I'm new to the forum and to the Teensy world, but I'm really excited about including the Teensy 4.1 in my new project. My goal is to acquire data via a multi-channel ADC, store it on an SD card, and then remotely download the data over Wi-Fi at decent speeds. I have files with 50 ~ 100MB for downloading

I did try to edit or delete my post to add the proper code formatting, but I couldn’t find the option. Could you kindly let me know if that’s possible, or should I repost the code with the correct formatting?

Thank you for your help, and I appreciate your understanding!

Daniel
 
What is the default SPI bitrate ??? It looks like you don't change it in the code. You setup the Wifi, but no bitrate change.
Hello Angelo,

Thank you for pointing that out!

The default SPI bitrate for the WiFiNINA library is set to 8 MHz in the spiSlaveSelect() function of the spi_drv.cpp file. Here's the relevant snippet:

WIFININA_SPIWIFI->beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));

I tried increasing the SPI clock speed to 24 MHz, but it caused the system to lose communication with the WiFi module. Since 8 MHz is the default value, I suspect it was chosen to ensure stable communication across various setups.

Do you think there are other adjustments I could make to improve performance without compromising communication stability? For example, would changing SPI modes or experimenting with shorter cable lengths help in this case?

To dig a bit deeper, I compiled the NINA-FW on a separate ESP32 and even tried tweaking some functions to optimize performance. Unfortunately, the issue persisted. The firmware version I'm using is NINA-FW 1.7.8.

At the moment, I'm getting transfer rates of around 60-70 kB/s, which is, well... let’s just say I might have to wait until the next ice age to download my data. 😅 Ideally, I’d love to hit at least 1 MB/s or 2 MB/s to make things a bit more practical.

I’m open to using any WiFi device that could achieve those speeds. If anyone has used a different module with Teensy for similar purposes, I’d really appreciate a point in the right direction!

One alternative I’m considering is using the ESP32 with RMII to connect directly to the ENET2 interface on the Teensy. With an Olimex ESP32-POE and the eth2ap example from ESP-IDF, I’ve managed to get transfer rates over 1 MB/s. The downside? A whole lot of extra hardware and dealing with PHY layers... fun times ahead. 😄

If anyone has a better suggestion or experience with similar setups, I’m all ears. Thanks in advance!
 
70kBytes/s is about 560kbits/s, far from 8Mbits/s.
I don't know what is involved, but you have probably a lot of software overhead.
Is the ESP32 able to receive at rates higher than 8Mbits/s ??? Not the bitrate set in the registers, but the rate of received bytes.
 
Which ESP32 module are you using?

Or to ask another way, if I wanted to set this up on my workbench to reproduce the slow speed using the same hardware as you have, which ESP32 should I order?
 
Can the ESP32 even be used as an external PHY over RMII? I haven’t heard of this, but I’d like to learn. @danielroitman do you have a link for this?

I.e. I think of the Teensy’s Ethernet hardware as controlling a PHY and not being able to be controlled as a PHY.
 
@danielroitman - A couple quick questions....

For WiFiNINA.h, which library are you using? Arduino's original WiFiNINA library doesn't have any setPins() function, so you're certainly not using that one. Maybe you have Adafruit's version? Searching also turned up this generic version which claims Teensy compatibility, but I can't find a setPins() function anywhere.

Please also tell me exactly what hardware you're using. A photo showing how it's really connected would also help.

FWIW, I found this generic ESP32 module. Not sure if this can run nina-fw (which Arduino says runs on u-Blox NINA-W10 modules), but might give it a try.

1737225836878.png


Please take a moment to give me much more specific info about exactly which hardware and software you're really using. Especially with problems where things generally work but performance is poor, small details can really matter.
 
After much fiddling, I got the ESP32 programmed with NINA-FW version 1.6.0 and connected to a Teensy 4.0 on breadboards (the ESP32 board is too wide for use on 1 breadboard).

1737256947527.png


Happy to report Adafruit's WiFiNINA library ScanNetworks example runs and detects nearby networks.

1737257008496.png



Now that I have hardware up and running, will look at performance measurements tomorrow or maybe Monday.
 
Hello @PaulStoffregen,

Apologies for the delayed response! I’ve been away for a few days and just got back. I also want to extend my thanks for the time and effort in setting up a similar system for testing—your help is greatly appreciated.

Here’s what I’ve done so far:

  1. Hardware:
    • I tested with generic ESP32-WROVER and ESP32-WROOM modules.
    • To adapt these modules for my setup, I modified the firmware from the following repository to configure the pins correctly:Adafruit NINA-FW. ()
      • I used version 1.7.8 of the firmware.
  2. Library:
  3. Results:
    • The results with both libraries have been very similar.
    • I noticed that the NINA-FW firmware configures the ESP32 with a clock speed of 80 MHz, likely for power-saving reasons. I tested with clock speeds of 160 MHz and 240 MHz but observed no significant improvement in performance.
    • Transfer rates remain low, with frequent communication losses, whether using web server or FTP server examples.
If there’s anything specific you’d like me to test or if you need more details about my setup, I’d be happy to provide that. Additionally, if there’s a better way to optimize the firmware or library for Teensy compatibility, I’m open to suggestions.

P.S.: Unfortunately, I don’t have a photo of my setup right now, as I’ve disassembled everything to test other functionalities with my Teensy. I’ll return to this project soon and perhaps even try other WiFi modules like the ATWINC1500 (though I do love the ESP32 solution).

Thanks again for your patience and help! 😊
 
Can the ESP32 even be used as an external PHY over RMII? I haven’t heard of this, but I’d like to learn. @danielroitman do you have a link for this?

I.e. I think of the Teensy’s Ethernet hardware as controlling a PHY and not being able to be controlled as a PHY.


Thanks for your question, Shawn!

I’m actually working on this as well, though programming isn’t exactly my strongest skill. Since I need the Teensy’s native ENET interface for my project but also want a high-quality WiFi connection in parallel, I’ve been trying to adapt the QNEthernet driver to utilize the ENET2 interface on the Teensy 4.1.

At the moment, I have my Teensy 4.1 connected to a DP83848 module, but so far without success. If this works, I plan to test connecting an ESP32 to the ENET2 with RMII interface as another option.

Currently, I have my 50 MHz clock routed to the SD_B0_01 pin for testing, and MDIO communication configured on GPIO_B0_00 and GPIO_B0_01. Its work fine, but i dont have RMII comunication. On the oscilloscope, I can see signals on RX_EN, RX0, and RX1, and everything seems okay. However, I’m not getting any interrupt triggers. The attachInterruptVector(IRQ_ENET2, enet_isr); function doesn’t seem to fire, and nothing happens.

I’m considering starting a new topic to explain everything I’ve tried so far—it might make troubleshooting easier. 😊
 
Back
Top