AsyncWebServer_Teensy41 for QNEthernet

khoih-prog

Well-known member
AsyncWebServer_Teensy41 Library

How To Install Using Arduino Library Manager


Why do we need this Async AsyncWebServer_Teensy41 Library

This library is based on, modified from Hristo Gochkov's ESPAsyncWebServer to apply the better and faster asynchronous feature into Teensy 4.1 using built-in QNEthernet

Why Async is better

- Using asynchronous network means that you can handle more than one connection at the same time
- You are called once the request is ready and parsed
- When you send the response, you are immediately ready to handle other connections while the server is taking care of sending the response in the background
- Speed is OMG
- Easy to use API, HTTP Basic and Digest MD5 Authentication (default), ChunkedResponse
- Easily extendible to handle **any type of content**
- Supports Continue 100%
- Async WebSocket plugin offering different locations without extra servers or ports
- Async EventSource (Server-Sent Events) plugin to send events to the browser
- URL Rewrite plugin for conditional and permanent url rewrites
- ServeStatic plugin that supports cache, Last-Modified, default index and more
- Simple template processing engine to handle templates


Currently Supported Boards

1. Teensy 4.1 using QNEthernet Library


Initial Releases v1.4.1

1. Initial porting and coding for Teensy 4.1 using built-in QNEthernet.


Debug Terminal Output Samples

1. Following are debug terminal output and screen shot when running example Async_AdvancedWebServer on Teensy4.1 using Built-in

Code:
Start Async_AdvancedWebServer on TEENSY 4.1 with Teensy4.1 QNEthernet
AsyncWebServer_Teensy41 v1.4.1
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.107
HTTP EthernetWebServer is @ IP : 192.168.2.107


Async_AdvancedWebServer.png
 
hello,
nice work thanks !
But a question i want to ask it it posible to handle files from SD Card (sdfat2) or Flash (LittleFs ) ?
Greedings
jake
 
Hello,
thanks for the hints, but sorry after all i don't get the point.
I need to load the file from the filesystem LittleFs form Teensy . But how do i use the EthernetWebServer to send a file from Filesystem ?
Because there is no easy function like server.serveStatic for others than ESP CPU !


But i try the Second one, now i used with Qnethernet.. and i removed all teh ESP32 specials.. but one killed my work.

server.serveStatic("/", FileFS, "/page1.html");

That was only usable on ESP, and this is the function that handles the Files from the ESP Filesystem partiontion right?

FS_EthernetWebServer: In function 'void initWebserver()':
FS_EthernetWebServer:416: error: 'class EthernetWebServer' has no member named 'serveStatic'
server.serveStatic("/", FileFS, "/page1.html");


I checked the library but i do not understand the code here. What is to to do on a non ESP CPU? With high power a la Potenta H7 or Teensy ?
I also search for a Exambe that sends files form a non ESP from a SDCard i could find only Sending websites from the Programm per function.

Thank you
Jake
 
Hello,
I think to have an Async Server on Teensy would be a great thing. I think Teensy projects could benefit a lot from this! And to me it would also be worth a few beers.
But i can't reach that goal alone, i need help.

Since last post i found a Esp32 Solution with an example here:
https://github.com/G6EJD/G6EJD-ESP-File-Server
Thanks to David, i liked this example very much and it is working on ESP.

I changed some code to make it work with T.41 /LittleFs / QNethernet .
What i need the Download and Upload and last but not least the async server to serve the files from the Littlefs on the Teensy.
The T4.1 examble now install an LittleFS and generates 3 Files a bigger , middle and a smaler one.
Delete, Rename, Dir list is working now.

But now i am stuck because the i need Help on this points:

1. Download : Working but if there is much data in the Download file it was filled with some dump.
The smal/medium file is only filled with "s" and that worked.
The big and sbig files have some anditional text.
n: close
Location: /dir
Accept-Ranges: none

pe?" plication/octet-stream
Connection: close
Accept-Ranges: none
BBBBBBBB Removed Contend BBBBBBBBBBBBBBBBBBBBBBBBBHTTP/1.1 302 Found
Content-Length: 0
Connection: close
Location: /dir
Accept-Ranges: none


2. Upload : No solution seen for that.

3. Html webserver : files for Filesystem as normal webserver, i think will fixed if download works.


Hopefully someone can help and have a nice ester!
Jake
 
It's great you've made some progress.

I'm sorry I can help you now. Please have a look at the similar example

1. ESP32_FS_EthernetWebServer

then adapt the code to work with Teensy 4.1 LittleFS

I also have similar request for Portenta_H7 in this Unable upload file issue, you can coordinate your project with that of person.

It's better if you open the issue in the related library, so that there are more people's attention.
 
Hello,
thanks for ansywer, and for the build of the Lib's you do for Teensy ! Thanks for your tipps,too! :)
I have a question is it reachable with your lib and steams ?

But i want an working Async Webserver it think that is the futuere for Teensy 4 !
I have to ask David to put my code here! I do not want to geht a problem if i share without allow.

I think some "Arduino" Programmes like me find that very usefull.

And the Teensy Lib of the Webserver did not support that "serverStatic" argument on the Teensy version, right ?
Yes i am the type of User that needs a example go geht the usage for that lib, so i did not understand what to to an Teensy. Maybe it can be very easy but i do not see the way.

Code:
 // Web Page handlers
  server.serveStatic("/", FileFS, "/page1.html");
  server.serveStatic("/page2", FileFS, "/page2.html");


That was the problem here, but i think and i think you also an Async Soulution was a greeat deal.

I am also very disappointed that no one else is interested in this, too bad.


Greedings
Jake
 
Here my code any help welcome

Code:
/*
  This software, the ideas and concepts is Copyright (c) David Bird 2021
  All rights to this software are reserved.
  It is prohibited to redistribute or reproduce of any part or all of the software contents in any form other than the following:
  1. You may print or download to a local hard disk extracts for your personal and non-commercial use only.
  2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material.
  3. You may not, except with my express written permission, distribute or commercially exploit the content.
  4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes.
  5. You MUST include all of this copyright and permission notice ('as annotated') and this shall be included in all copies or substantial portions of the software
     and where the software use is visible to an end-user.
  6. *** DONT USE THE SOFTWARE IF YOU DONT LIKE THE LICNCE CONDITIONS ***
  THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT.
  FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
  A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OR
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  See more at http://dsbird.org.uk

  16.4.2022  Changed some code to rund with QNethernet / LittleFS / Asybc Webserver thanks to all that build that Library.
             Thanks  to David to give allow to repost the code.

             
*/

//############## Open Points
//   Download have additional Content ond big and sbig file
//   No solution for uplad
//  Want to akt as Webserver form Filesystem (LittelFS / Sdfat )
//  Helpp welcome !

#include "arduino.h"
// innclude "credentials.h"


// Comment out for NO access controls
//#define AccessControl

// Set your server access username and password here (OPTIONAL:)
const char* http_username = "admin";
const char* http_password = "admin";

// Set a server logical name, this gets translated to the physical IP address e.g. http://192.168.0.22
// NOTE: ONLY works if your browser supports this function
const char* ServerName    = "T4.1 Fileserver";


//#include <sdfat.h>
#include <LittleFS.h>            // Built-in

      #include <QNEthernet.h>       // https://github.com/ssilverman/QNEthernet
      using namespace qindesign::network;
      #define SHIELD_TYPE           "using QNEthernet"  

#include <Teensy41_AsyncTCP.h>          // https://github.com/me-no-dev/AsyncTCP
#include <AsyncWebServer_Teensy41.h>


 LittleFS_Program FS   ;

    // Set the static IP address to use if the DHCP fails to assign
    IPAddress myIP       (192, 168, 33 , 111);
    IPAddress myNetmask  (255, 255, 255, 0);
    IPAddress myGW       (192, 168, 33 , 1);
    IPAddress mydnsServer(192, 168, 33 , 1);

 

AsyncWebServer server(80);

//################  VERSION  ###########################################
String    Version = "1.0";   // Programme version, see change log at end
//################ VARIABLES ###########################################

typedef struct
{
  String filename;
  String ftype;
  String fsize;
} fileinfo;

String   webpage, MessageLine;
fileinfo Filenames[200]; // Enough for most purposes!
bool     StartupErrors = false;
uint32_t      start, downloadtime = 1, uploadtime = 1, downloadsize, uploadsize, downloadrate, uploadrate, numfiles;
float    Temperature = 21.34; // for example new page, amend in a snesor function if required
String   Name = "Dave";





void initFS()
{
  // Initialize LittleFS/SPIFFS file-system


// t4.1 nur einmal FileFS.quickFormat();
    if (!FS.begin(2048000))
    {
        Serial.printf("Error starting %s\n", "PROG/Flash RAM DISK");
        while (1) {
            // Error, so don't do anything more - stay stuck here
        }
    }
    Serial.println("LittleFS initialized.");

   //Remove after files shuld stay after reboot
    FS.quickFormat();


  
}


void setup() {
  Serial.begin(115200);
 // while (!Serial);
  Serial.println(__FILE__);
  /*
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("STA: Failed!\n");
    WiFi.disconnect(false);
    delay(500);
    WiFi.begin(ssid, password);
  }
  Serial.println("IP Address: " + WiFi.localIP().toString());
  if (WiFi.scanComplete() == -2) WiFi.scanNetworks(true); // Complete an initial scan for WiFi networks, otherwise = 0 on first display!
  if (!StartMDNSservice(ServerName)) {
    Serial.println("Error starting mDNS Service...");;
    StartupErrors = true;
  }
*/

    Ethernet.begin(myIP, myNetmask, myGW);
    Ethernet.setDNSServerIP(mydnsServer);


  Serial.print(F("Initializing LittleFS ..."));
  initFS();
 
  Serial.println("LittleFS initialized.");
  Serial.println("\n---------------");
  Serial.printf("Bytes Used: %llu, Bytes Total:%u\n", FS.usedSize(), FS.totalSize());

//------------ Smallfile
  File file;
  
  char someData[10];
  char firstfile [] = "smallfile.txt";
  file =FS.open(firstfile, FILE_WRITE);  
  memset(someData, 's', 10);

  for (uint16_t j = 0; j < 10; j++)
  {
     file.write(someData, sizeof(someData));
    // file.write("\n",1 );
  }
  file.close();


//------------ midfile
  char someData2[10];
  memset(someData2, 'M', 10);
  file = FS.open("midfile.txt", FILE_WRITE);
  
  for (uint16_t j = 0; j < 100; j++)
  {
      file.write(someData2, sizeof(someData2));
     // file.write("\n",1 );
  }  
  file.close();


  
//------------ Bigfile
  char someData3[10];
  memset(someData3, 'B', 10);
  file = FS.open("bigfile.txt", FILE_WRITE);

  for (uint16_t j = 0; j < 1000; j++)
  {
      file.write(someData3, sizeof(someData3));
      //file.write("\n", 1);
  }
  file.close();

  
//------------ SuperBigfile
  char someData4[10];
  memset(someData4, 'S', 10);
  file = FS.open("sbigfile.txt", FILE_WRITE);

  for (uint16_t j = 0; j < 10000; j++)
  {
      file.write(someData4, sizeof(someData4));
    //  file.write("\n", 1);
  }
  file.close();







//------------

  // We can also get the size of the file just created.  Note we have to open and 
  // thes close the file unless we do file size before we close it in the previous step
  file = FS.open("smallfile.txt", FILE_READ);
  Serial.printf("File Size of smalfile.txt (bytes): %u\n", file.size());
  file.close();

  file = FS.open("midfile.txt", FILE_READ);
  Serial.printf("File Size of bigfile.txt (bytes): %u\n", file.size());
  file.close();
  
  file = FS.open("bigfile.txt", FILE_READ);
  Serial.printf("File Size of bigfile.txt (bytes): %u\n", file.size());
  file.close();


  file = FS.open("sbigfile.txt", FILE_READ);
  Serial.printf("File Size of bigfile.txt (bytes): %u\n", file.size());
  file.close();


//-- Print files

char firstfile1 [] = "smallfile.txt";
  file =FS.open(firstfile1, FILE_READ);  

int data =0;
    while ((data = file.read()) >= 0) 
    {
     Serial.write(data);
     }
   file.close();
Serial.println("########");

data =0;
char  firstfile2 [] = "midfile.txt";
  file =FS.open(firstfile2, FILE_READ);  

    while ((data = file.read()) >= 0) 
    {
     Serial.write(data);
     }
   file.close();

Serial.println("########");

 data =0;
char  firstfile3 [] = "bigfile.txt";
  file =FS.open(firstfile3, FILE_READ);  

    while ((data = file.read()) >= 0) 
    {
     Serial.write(data);
     }
   file.close();

Serial.println("########");
 data =0;
 char  firstfile4 [] = "sbigfile.txt";
  file =FS.open(firstfile4, FILE_READ);  

    while ((data = file.read()) >= 0) 
    {
     Serial.write(data);
     }
   file.close();
Serial.println("########");







  // ##################### HOMEPAGE HANDLER ###########################
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Home Page...");
  #ifdef AccessControl
      if (!request->authenticate(http_username, http_password)) // Comment out to remove need for login username & password
       return request->requestAuthentication();                // Comment out to remove need for login username & password
 #endif
    Home(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });


  // ##################### DIR HANDLER ###############################
  server.on("/dir", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("File Directory...");
    Dir(request); // Build webpage ready for display
    request->send(200, "text/html", webpage);

   Serial.println("File Directory...  END");
    
  });



  // ##################### LOGOUT HANDLER ############################
  server.on("/logout", HTTP_GET, [](AsyncWebServerRequest * request) {
    LogOut();
    request->send(200, "text/html", webpage);
  });

  // ##################### DOWNLOAD HANDLER ##########################
  server.on("/download", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Downloading file...");
    Select_File_For_Function("[DOWNLOAD]", "downloadhandler"); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });

  // ##################### UPLOAD HANDLERS ###########################
  server.on("/upload", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Uploading file...");
    UploadFileSelect(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });

  // Set handler for '/handleupload'
/*
  server.on("/handleupload", HTTP_POST, [](AsyncWebServerRequest * request) {},
    [](AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t *data,
     size_t len, bool final) {
    handleFileUpload(request, filename, index, data, len, final);
  });
*/
 
  // ##################### FS Format HANDLER ############################
  /*   Not need
  // Set handler for '/handleformat'
  server.on("/handleformat", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Processing Format Request of File System...");
    if (request->getParam("format")->value() == "YES") {
      Serial.print("Starting to Format Filing System...");
     // FS.end();
      
      bool formatted = FS.format();
      if (formatted) {
        Serial.println(" Successful Filing System Format...");
      }
      else         {
        Serial.println(" Formatting Failed...");
      }
    }
    request->redirect("/dir");
  });
  */

  // ##################### STREAM HANDLER ############################
 /*
  server.on("/stream", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Streaming file...");
    Select_File_For_Function("[STREAM]", "streamhandler"); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });
*/

  // ##################### RENAME HANDLER ############################
 
  server.on("/rename", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Renaming file...");
    File_Rename(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });


  // ##################### DELETE HANDLER ############################
  server.on("/delete", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Deleting file...");
    Select_File_For_Function("[DELETE]", "deletehandler"); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });

  // ##################### FORMAT HANDLER ############################
  server.on("/format", HTTP_GET, [](AsyncWebServerRequest * request) {
    Serial.println("Request to Format File System...");
    Format(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });

  // ##################### SYSTEM HANDLER ############################
  server.on("/system", HTTP_GET, [](AsyncWebServerRequest * request) {
    Display_System_Info(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });
  

  // ##################### IMAGE HANDLER ############################
//  server.on("/icon", HTTP_GET, [](AsyncWebServerRequest * request) {
  //  request->send(FS, "/icon.gif", "image/gif");
 // });

  //        That Function Rerquest Handler is not implemente
  /*
  server.on("/icon.gif", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(FS, "/icon.gif", "image/gif");
  });
    server.on("/drive.htm", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(FS, "/drive.htm", "text/html");
  });


    server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(FS, "/style.css", "text/css");
  });
    */
  // ##################### NOT FOUND HANDLER #########################
  server.onNotFound(notFound);

  // TO add new pages add a handler here, make  sure the HTML Header has a menu item for it, create a page for it using a function. Copy this one, rename it.
  // ##################### NEW PAGE HANDLER ##########################
  server.on("/newpage", HTTP_GET, [](AsyncWebServerRequest * request) {
    Display_New_Page(); // Build webpage ready for display
    request->send(200, "text/html", webpage);
  });

  server.begin();  // Start the server
  if (!StartupErrors)
    Serial.println("System started successfully...");
  else
    Serial.println("There were problems starting all services...");
  Directory();     // Update the file list
}

void loop() {
  // Nothing to do here yet ... add your requirements to do things!
}

//#############################################################################################
void Dir(AsyncWebServerRequest * request)
{
  String Fname1, Fname2; 
  int index = 0;
  Directory(); // Get a list of the current files on the FS

  webpage="";
  webpage  = HTML_Header();
  webpage += "<h3>Filing System Content</h3><br>";
  if (numfiles > 0)
  {
    webpage += "<table class='center'>";
    webpage += "<tr><th>Type</th><th>File Name</th><th>File Size</th><th class='sp'></th><th>Type</th><th>File Name</th><th>File Size</th></tr>";
    while (index < numfiles) {
      Fname1 = Filenames[index].filename;
      Fname2 = Filenames[index + 1].filename;
      webpage += "<tr>";
      webpage += "<td style = 'width:5%'>" + Filenames[index].ftype + "</td><td style = 'width:25%'>" + Fname1 + "</td><td style = 'width:10%'>" + Filenames[index].fsize + "</td>";
      webpage += "<td class='sp'></td>";
      if (index < numfiles - 1) {
        webpage += "<td style = 'width:5%'>" + Filenames[index + 1].ftype + "</td><td style = 'width:25%'>" + Fname2 + "</td><td style = 'width:10%'>" + Filenames[index + 1].fsize + "</td>";
      }
      webpage += "</tr>";
      index = index + 2;
    }
    webpage += "</table>";
    webpage += "<p style='background-color:yellow;'><b>" + MessageLine + "</b></p>";
    MessageLine = "";
  }
  else
  {
    webpage += "<h2>No Files Found</h2>";
  }
  webpage += HTML_Footer();

 // Serial.println(webpage);
 // request->send(200, "text/html", webpage);   //Not needed here 
}

//#############################################################################################
void Directory() {
    Serial.println("DIRectory ");
  numfiles  = 0; // Reset number of FS files counter
  File root = FS.open("/", FILE_READ);
  if (root)
  {
    root.rewindDirectory();
    File file = root.openNextFile();

    while (file) { // Now get all the filenames, file types and sizes
      Filenames[numfiles].filename = (String(file.name()).startsWith("/") ? String(file.name()).substring(1) : file.name());
      Filenames[numfiles].ftype    = (file.isDirectory() ? "Dir" : "File");
      Filenames[numfiles].fsize    = ConvBinUnits(file.size(), 1);
      Serial.print(Filenames[numfiles].filename);
      Serial.print ("    ");
      Serial.println(numfiles);
      file = root.openNextFile();
      numfiles++;
      
    }
    root.close();
  }
     Serial.println("Directory end");
}
//#############################################################################################
void UploadFileSelect() {
  webpage  = HTML_Header();
  webpage += "<h3>Select a File to [UPLOAD] to this device</h3>";
  webpage += "<form method = 'POST' action = '/handleupload' enctype='multipart/form-data'>";
  webpage += "<input type='file' name='filename'><br><br>";
  webpage += "<input type='submit' value='Upload'>";
  webpage += "</form>";
  webpage += HTML_Footer();
}
//#############################################################################################
void Format() {
  webpage  = HTML_Header();
  webpage += "<h3>***  Format Filing System on this device ***</h3>";
  webpage += "<form action='/handleformat'>";
  webpage += "<input type='radio' id='YES' name='format' value = 'YES'><label for='YES'>YES</label><br><br>";
  webpage += "<input type='radio' id='NO'  name='format' value = 'NO' checked><label for='NO'>NO</label><br><br>";
  webpage += "<input type='submit' value='Format?'>";
  webpage += "</form>";
  webpage += HTML_Footer();
}
//#############################################################################################
void handleFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
/*  if (!index) {
     // String to char 
    if (!filename.startsWith("/")) filename = "/" + filename;
     
     int str_len = filename.length() + 1;
     char file [ str_len ]   ;
     filename.toCharArray(file, str_len);
    //################################


    
    request->_tempFile = FS.open(file, FILE_WRITE);
    if (!request->_tempFile) Serial.println("Error creating file for upload...");
    start = millis();
  }
  
  if (request->_tempFile) {
    if (len) {
      request->_tempFile.write(data, len); // Chunked data
      Serial.println("Transferred : " + String(len) + " Bytes");
    }
    if (final) {
      uploadsize = request->_tempFile.size();
      request->_tempFile.close();
      uploadtime = millis() - start;
      request->redirect("/dir");
    }
  }
 */
}
//#############################################################################################
void File_Stream() {
  SelectInput("Select a File to Stream", "handlestream", "filename");
}
//#############################################################################################
void File_Delete() {
  SelectInput("Select a File to Delete", "handledelete", "filename");
}
//#############################################################################################
void Handle_File_Delete(String filename) { // Delete the file
  webpage = HTML_Header();
  if (!filename.startsWith("/")) filename = "/" + filename;

           //################################
       // String to char 
        uint16_t strlen1 = filename.length() + 1;
        char  ch_filename[strlen1];

        filename.toCharArray(ch_filename, strlen1);
       //################################

  
  File dataFile = FS.open(ch_filename, FILE_WRITE); // Now read FS to see if file exists
  if (dataFile) {  // It does so delete it
    FS.remove(ch_filename);
    webpage += "<h3>File '" + filename.substring(1) + "' has been deleted</h3>";
    webpage += "<a href='/dir'>[Enter]</a><br><br>";
  }
  else
  {
    webpage += "<h3>File [ " + filename + " ] does not exist</h3>";
    webpage += "<a href='/dir'>[Enter]</a><br><br>";
  }
  webpage  += HTML_Footer();
  
}
//#############################################################################################
void File_Rename() { // Rename the file
  Directory();
  webpage = HTML_Header();
  webpage += "<h3>Select a File to [RENAME] on this device</h3>";
  webpage += "<FORM action='/renamehandler'>";
  webpage += "<table class='center'>";
  webpage += "<tr><th>File name</th><th>New Filename</th><th>Select</th></tr>";
  int index = 0;
  while (index < numfiles) {
    webpage += "<tr><td><input type='text' name='oldfile' style='color:blue;' value = '" + Filenames[index].filename + "' readonly></td>";
    webpage += "<td><input type='text' name='newfile'></td><td><input type='radio' name='choice'></tr>";
    index++;
  }
  webpage += "</table><br>";
  webpage += "<input type='submit' value='Enter'>";
  webpage += "</form>";
  webpage += HTML_Footer();
  
}
//#############################################################################################
void Handle_File_Rename(AsyncWebServerRequest *request, String filename, int Args) { // Rename the file
  String newfilename;

  //int Args = request->args();
  webpage = HTML_Header();
  for (int i = 0; i < Args; i++) {
    if (request->arg(i) != "" && request->arg(i + 1) == "on") {
      filename    = request->arg(i - 1);
      newfilename = request->arg(i);
    }
  }
  if (!filename.startsWith("/"))    filename = "/" + filename;
  if (!newfilename.startsWith("/")) newfilename = "/" + newfilename;

           //################################
       // String to char 
        uint16_t strlen1 = filename.length() + 1;
        char  ch_filename[strlen1];

        filename.toCharArray(ch_filename, strlen1);
       //################################
       
        //################################
       // String to char 
        strlen1 = newfilename.length() + 1;
        char ch_newfilename[strlen1];

        newfilename.toCharArray(ch_newfilename, strlen1);
       //################################
  
  File CurrentFile = FS.open(ch_filename, FILE_WRITE);    // Now read FS to see if file exists
  if (CurrentFile && filename != "/" && newfilename != "/" && (filename != newfilename)) { // It does so rename it, ignore if no entry made, or Newfile name exists already
 

        
  //  if (FS.rename(filename, newfilename)) {
      if (FS.rename(ch_filename, ch_newfilename)) {
    
      filename    = filename.substring(1);
      newfilename = newfilename.substring(1);
      webpage += "<h3>File '" + filename + "' has been renamed to '" + newfilename + "'</h3>";
      webpage += "<a href='/dir'>[Enter]</a><br><br>";
    }
  }
  else
  {
    if (filename == "/" && newfilename == "/") webpage += "<h3>File was not renamed</h3>";
    else webpage += "<h3>New filename exists, cannot rename</h3>";
    webpage += "<a href='/rename'>[Enter]</a><br><br>";
  }
  CurrentFile.close();
  webpage  += HTML_Footer();
}
String processor(const String& var) {
  if (var == "HELLO_FROM_TEMPLATE") return F("Hello world!");
  return String();
  
}



//#############################################################################################
// Not found handler is also the handler for 'delete', 'download' and 'stream' functions
void notFound(AsyncWebServerRequest *request) { // Process selected file types
  String filename;

  

  if (request->url().startsWith("/downloadhandler") ||
      request->url().startsWith("/streamhandler")   ||
      request->url().startsWith("/deletehandler")   ||
      request->url().startsWith("/renamehandler"))
  {
    // Now get the filename and handle the request for 'delete' or 'download' or 'stream' functions
    if (!request->url().startsWith("/renamehandler")) 
        filename = request->url().substring(request->url().indexOf("~/") + 1);
        
        //################################
       // String to char 
         uint16_t strlen1 = filename.length() + 1;
        char CS_file [strlen1];

        filename.toCharArray(CS_file, strlen1);
       //################################
  

    start = millis();


      //-------------------------------------
    if (request->url().startsWith("/downloadhandler"))
    {
      Serial.println("Download handler started...");
      MessageLine = "";

      
      File file1 = FS.open(CS_file, FILE_READ);


    Serial.println("Serial out File to be shure what contnend it haves -------------------------");
    int data;
    while ((data = file1.read()) >= 0) 
    {
     Serial.write(data);
     }
   file1.close();
   Serial.println("-------------------------");


// Real download section
 
  File dlfile = FS.open(CS_file, FILE_READ);
  
  //Content Type
  String contentType = getContentType("download");
  Serial.println(contentType);
  
         AsyncWebServerResponse* response = request->beginResponse(contentType, dlfile.size(), [dlfile](uint8_t* buffer, size_t maxLen, size_t total) mutable -> size_t
             {
             File filecopy = dlfile;

            uint32_t bytes = dlfile.read(buffer, maxLen);
             Serial.print(bytes);
  
        
            if (bytes + total == filecopy.size()) filecopy.close();

            return max(0, bytes); 
           
        });
    

          request->send(response);
dlfile.close();
     
     // downloadtime = millis() - start;
     // downloadsize = GetFileSize(CS_file);
      request->redirect("/dir");                //Redirect to the Directory Site.
    }




    //-------------------------------------
    if (request->url().startsWith("/streamhandler"))
    {
      Serial.println("Stream handler started...");
      String ContentType = getContentType(filename);
  //    AsyncWebServerResponse *response = request->beginResponse(FS, filename, ContentType);
  //    request->send(response);
      downloadsize = GetFileSize(CS_file);
      downloadtime = millis() - start;
      request->redirect("/dir");
    }
      //-------------------------------------
    if (request->url().startsWith("/deletehandler"))
    {
      Serial.println("Delete handler started...");
      Handle_File_Delete(filename); // Build webpage ready for display
      request->send(200, "text/html", webpage);
    }
    //-----------------------------------------
    if (request->url().startsWith("/renamehandler"))
    {
      Handle_File_Rename(request, filename, request->args()); // Build webpage ready for display
      request->send(200, "text/html", webpage);
    }
  }
  else
  {
    Page_Not_Found();
    request->send(200, "text/html", webpage);
  }
}
//#############################################################################################
void Handle_File_Download() {
  String filename = "";
  int index = 0;
  Directory(); // Get a list of files on the FS
  webpage = HTML_Header();
  webpage += "<h3>Select a File to Download</h3>";
  webpage += "<table>";
  webpage += "<tr><th>File Name</th><th>File Size</th></tr>";
  while (index < numfiles) {
    webpage += "<tr><td><a href='" + Filenames[index].filename + "'></a><td>" + Filenames[index].fsize + "</td></tr>";
    index++;
  }
  webpage += "</table>";
  webpage += "<p>" + MessageLine + "</p>";
  webpage += HTML_Footer();
}
//#############################################################################################
String getContentType(String filenametype) { // Tell the browser what file type is being sent
  if (filenametype == "download") {
    return "application/octet-stream";
  } else if (filenametype.endsWith(".txt"))  {
    return "text/plain";
  } else if (filenametype.endsWith(".htm"))  {
    return "text/html";
  } else if (filenametype.endsWith(".html")) {
    return "text/html";
  } else if (filenametype.endsWith(".css"))  {
    return "text/css";
  } else if (filenametype.endsWith(".js"))   {
    return "application/javascript";
  } else if (filenametype.endsWith(".png"))  {
    return "image/png";
  } else if (filenametype.endsWith(".gif"))  {
    return "image/gif";
  } else if (filenametype.endsWith(".jpg"))  {
    return "image/jpeg";
  } else if (filenametype.endsWith(".ico"))  {
    return "image/x-icon";
  } else if (filenametype.endsWith(".xml"))  {
    return "text/xml";
  } else if (filenametype.endsWith(".pdf"))  {
    return "application/x-pdf";
  } else if (filenametype.endsWith(".zip"))  {
    return "application/x-zip";
  } else if (filenametype.endsWith(".gz"))   {
    return "application/x-gzip";
  }
  return "text/plain";
}
//#############################################################################################
void Select_File_For_Function(String title, String function) {
  String Fname1, Fname2;
  int index = 0;
  Directory(); // Get a list of files on the FS
  webpage = HTML_Header();
  webpage += "<h3>Select a File to " + title + " from this device</h3>";
  webpage += "<table class='center'>";
  webpage += "<tr><th>File Name</th><th>File Size</th><th class='sp'></th><th>File Name</th><th>File Size</th></tr>";
  while (index < numfiles) {
    Fname1 = Filenames[index].filename;
    Fname2 = Filenames[index + 1].filename;
    if (Fname1.startsWith("/")) Fname1 = Fname1.substring(1);
    if (Fname2.startsWith("/")) Fname1 = Fname2.substring(1);
    webpage += "<tr>";
    webpage += "<td style='width:25%'><button><a href='" + function + "~/" + Fname1 + "'>" + Fname1 + "</a></button></td><td style = 'width:10%'>" + Filenames[index].fsize + "</td>";
    webpage += "<td class='sp'></td>";
    if (index < numfiles - 1) {
      webpage += "<td style='width:25%'><button><a href='" + function + "~/" + Fname2 + "'>" + Fname2 + "</a></button></td><td style = 'width:10%'>" + Filenames[index + 1].fsize + "</td>";
    }
    webpage += "</tr>";
    index = index + 2;
  }
  webpage += "</table>";
  webpage += HTML_Footer();
}
//#############################################################################################
void SelectInput(String Heading, String Command, String Arg_name) {
  webpage = HTML_Header();
  webpage += "<h3>" + Heading + "</h3>";
  webpage += "<form  action='/" + Command + "'>";
  webpage += "Filename: <input type='text' name='" + Arg_name + "'><br><br>";
  webpage += "<input type='submit' value='Enter'>";
  webpage += "</form>";
  webpage += HTML_Footer();
}
//#############################################################################################
int GetFileSize( const char filename) {
 uint32_t filesize;

  File CheckFile = FS.open(filename, FILE_READ);
 
  filesize = CheckFile.size();
  CheckFile.close();
  return filesize;
}
//#############################################################################################
void Home() {
  webpage = HTML_Header();
  webpage += "<h1>Home Page</h1>";
  webpage += "<h2>ESP Asychronous WebServer Example</h2>";
  webpage += "<img src = 'icon' alt='icon'>";
  webpage += "<h3>File Management - Directory, Upload, Download, Stream and Delete File Examples</h3>";
  webpage += HTML_Footer();
}
//#############################################################################################
void LogOut() {
  webpage = HTML_Header();
  webpage += "<h1>Log Out</h1>";
  webpage += "<h4>You have now been logged out...</h4>";
  webpage += "<h4>NOTE: On most browsers you must close all windows for this to take effect...</h4>";
  webpage += HTML_Footer();
}
//#############################################################################################
void Display_New_Page() {
  webpage = HTML_Header();                                                                          // Add HTML headers, note '=' for new page assignment
  webpage += "<h1>My New Web Page</h1>";                                                            // Give it a heading
  webpage += "<h3>Example entries for the page</h3>";                                               // Set the scene
  webpage += "<p>NOTE:</p>";
  webpage += "<p>You can display global variables like this: " + String(Temperature, 2) + "°C</p>"; // Convert numeric variables to a string, use 2-decimal places
  webpage += "<p>Or, like this, my name is: " + Name + "</p>";                                      // no need to convert string variables
  webpage += HTML_Footer();                                                                         // Add HTML footers, don't forget to add a menu name in header!
}
//#############################################################################################
void Page_Not_Found() {
  webpage = HTML_Header();
  webpage += "<div class='notfound'>";
  webpage += "<h1>Sorry</h1>";
  webpage += "<p>Error 404 - Page Not Found</p>";
  webpage += "</div><div class='left'>";
  webpage += "<p>The page you were looking for was not found, it may have been moved or is currently unavailable.</p>";
  webpage += "<p>Please check the address is spelt correctly and try again.</p>";
  webpage += "<p>Or click <b><a href='/'>[Here]</a></b> for the home page.</p></div>";
  webpage += HTML_Footer();
}
//#############################################################################################
void Display_System_Info() {
 // esp_chip_info_t chip_info;
//  esp_chip_info(&chip_info);
//  if (WiFi.scanComplete() == -2) WiFi.scanNetworks(true, false); // Scan parameters are (async, show_hidden) if async = true, don't wait for the result
  webpage = HTML_Header();
  webpage += "<h3>System Information</h3>";
  webpage += "<h4>Transfer Statistics</h4>";
  webpage += "<table class='center'>";
  webpage += "<tr><th>Last Upload</th><th>Last Download/Stream</th><th>Units</th></tr>";
  webpage += "<tr><td>" + ConvBinUnits(uploadsize, 1) + "</td><td>" + ConvBinUnits(downloadsize, 1) + "</td><td>File Size</td></tr> ";
  webpage += "<tr><td>" + ConvBinUnits((float)uploadsize / uploadtime * 1024.0, 1) + "/Sec</td>";
  webpage += "<td>" + ConvBinUnits((float)downloadsize / downloadtime * 1024.0, 1) + "/Sec</td><td>Transfer Rate</td></tr>";
  webpage += "</table>";
 /// webpage += "<h4>Filing System</h4>";
  //webpage += "<table class='center'>";
 /* webpage += "<tr><th>Total Space</th><th>Used Space</th><th>Free Space</th><th>Number of Files</th></tr>";
  webpage += "<tr><td>" + ConvBinUnits(FS.totalBytes(), 1) + "</td>";
  webpage += "<td>" + ConvBinUnits(FS.usedBytes(), 1) + "</td>";
  webpage += "<td>" + ConvBinUnits(FS.totalBytes() - FS.usedBytes(), 1) + "</td>";
  webpage += "<td>" + (numfiles == 0 ? "Pending Dir or Empty" : String(numfiles)) + "</td></tr>";
  webpage += "</table>";
  webpage += "<h4>CPU Information</h4>";
  webpage += "<table class='center'>";
  webpage += "<tr><th>Parameter</th><th>Value</th></tr>";
  webpage += "<tr><td>Number of Cores</td><td>" + String(chip_info.cores) + "</td></tr>";
  webpage += "<tr><td>Chip revision</td><td>" + String(chip_info.revision) + "</td></tr>";
  webpage += "<tr><td>Internal or External Flash Memory</td><td>" + String(((chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "Embedded" : "External")) + "</td></tr>";
  webpage += "<tr><td>Flash Memory Size</td><td>" + String((spi_flash_get_chip_size() / (1024 * 1024))) + " MB</td></tr>";
*/
//  webpage += "<tr><td>Current Free RAM</td><td>" + ConvBinUnits(ESP.getFreeHeap(), 1) + "</td></tr>";
  webpage += "</table>";
  webpage += "<h4>Network Information</h4>";
  webpage += "<table class='center'>";
  webpage += "<tr><th>Parameter</th><th>Value</th></tr>";
// webpage += "<tr><td>LAN IP Address</td><td>"              + String(WiFi.localIP().toString()) + "</td></tr>";
 // webpage += "<tr><td>Network Adapter MAC Address</td><td>" + String(WiFi.BSSIDstr()) + "</td></tr>";
//  webpage += "<tr><td>WiFi SSID</td><td>"                   + String(WiFi.SSID()) + "</td></tr>";
 // webpage += "<tr><td>WiFi RSSI</td><td>"                   + String(WiFi.RSSI()) + " dB</td></tr>";
 // webpage += "<tr><td>WiFi Channel</td><td>"                + String(WiFi.channel()) + "</td></tr>";
//  webpage += "<tr><td>WiFi Encryption Type</td><td>"        + String(EncryptionType(WiFi.encryptionType(0))) + "</td></tr>";
  webpage += "</table> ";
  webpage += HTML_Footer();
}
//#############################################################################################
String ConvBinUnits(size_t bytes, byte resolution) {
  if      (bytes < 1024)                 {
    return String(bytes) + " B";
  }
  else if (bytes < 1024 * 1024)          {
    return String(bytes / 1024.0, resolution) + " KB";
  }
  else if (bytes < (1024 * 1024 * 1024)) {
    return String(bytes / 1024.0 / 1024.0, resolution) + " MB";
  }
  else return "";
}
//#############################################################################################
/*String EncryptionType(wifi_auth_mode_t encryptionType) {
  switch (encryptionType) {
    case (WIFI_AUTH_OPEN):
      return "OPEN";
    case (WIFI_AUTH_WEP):
      return "WEP";
    case (WIFI_AUTH_WPA_PSK):
      return "WPA PSK";
    case (WIFI_AUTH_WPA2_PSK):
      return "WPA2 PSK";
    case (WIFI_AUTH_WPA_WPA2_PSK):
      return "WPA WPA2 PSK";
    case (WIFI_AUTH_WPA2_ENTERPRISE):
      return "WPA2 ENTERPRISE";
    case (WIFI_AUTH_MAX):
      return "WPA2 MAX";
    default:
      return "";
  }
}
*/
//#############################################################################################
bool StartMDNSservice(const char* Name) {
  //esp_err_t err = mdns_init();             // Initialise mDNS service
  //if (err) {
   // printf("MDNS Init failed: %d\n", err); // Return if error detected
   // return false;
  //}
 // mdns_hostname_set(Name);                 // Set hostname
  return true;
}
//#############################################################################################
String HTML_Header() {
  String page;
  page  = "<!DOCTYPE html>";
  page += "<html lang = 'en'>";
  page += "<head>";
  page += "<title>Web Server</title>";
  page += "<meta charset='UTF-8'>"; // Needed if you want to display special characters like the ° symbol
  page += "<style>";
  page += "body {width:75em;margin-left:auto;margin-right:auto;font-family:Arial,Helvetica,sans-serif;font-size:16px;color:blue;background-color:#e1e1ff;text-align:center;}";
  page += "footer {padding:0.08em;background-color:cyan;font-size:1.1em;}";
  page += "table {font-family:arial,sans-serif;border-collapse:collapse;width:70%;}"; // 70% of 75em!
  page += "table.center {margin-left:auto;margin-right:auto;}";
  page += "td, th {border:1px solid #dddddd;text-align:left;padding:8px;}";
  page += "tr:nth-child(even) {background-color:#dddddd;}";
  page += "h4 {color:slateblue;font:0.8em;text-align:left;font-style:oblique;text-align:center;}";
  page += ".center {margin-left:auto;margin-right:auto;}";
  page += ".topnav {overflow: hidden;background-color:lightcyan;}";
  page += ".topnav a {float:left;color:blue;text-align:center;padding:0.6em 0.6em;text-decoration:none;font-size:1.3em;}";
  page += ".topnav a:hover {background-color:deepskyblue;color:white;}";
  page += ".topnav a.active {background-color:lightblue;color:blue;}";
  page += ".notfound {padding:0.8em;text-align:center;font-size:1.5em;}";
  page += ".left {text-align:left;}";
  page += ".medium {font-size:1.4em;padding:0;margin:0}";
  page += ".ps {font-size:0.7em;padding:0;margin:0}";
  page += ".sp {background-color:silver;white-space:nowrap;width:2%;}";
  page += "</style>";
  page += "</head>";
  page += "<body>";
  page += "<div class = 'topnav'>";
  page += "<a href='/'>Home</a>";
  page += "<a href='/dir'>Directory</a>";
  page += "<a href='/upload'>Upload</a> ";
  page += "<a href='/download'>Download</a>";
 // page += "<a href='/stream'>Stream</a>";
  page += "<a href='/delete'>Delete</a>";
  page += "<a href='/rename'>Rename</a>";
  page += "<a href='/system'>Status</a>";
  //page += "<a href='/format'>Format FS</a>";
  page += "<a href='/newpage'>New Page</a>";
  page += "<a href='/logout'>[Log-out]</a>";
  page += "</div>";
  return page;
}
//#############################################################################################
String HTML_Footer() {
  String page;
  page += "<br><br><footer>";
  page += "<p class='medium'>Teensy Asynchronous WebServer Example</p>";
  page += "<p class='ps'><i>Copyright © D L Bird 2021 Version " +  Version + "</i></p>";
  page += "</footer>";
  page += "</body>";
  page += "</html>";
  return page;
}
/*
   Version-1 of teensy
   */
 
I am also very disappointed that no one else is interested in this, too bad.
I am interested, but at present I have no Server knowledge so can be of little help to you.
I have a project that I am working on that may benefit in the future.
 
Hi Jakespeed and BriComp,

I just spent some time to port / add the features to support T41 QNEthernet, FS (SD, PSRAM, SPI, QSPI, etc.). The new example code, adapted from Async_ESP32_FSWebServer example, compiles and runs OK, but I just couldn't test upload / download the files yet, because I don't have now the (Q)SPI Flash, PSRAM, etc. to have real test.

Just ordered some chips, but it'll take quite a long time to get the parts. I also don't have time to spend on this not-popular enhancement anymore.

If you'd like to take over the development, debug, fix, I can create a new GitHub repo within several days as the code is still very messy now.

Just let me know how you'd like to proceed.

PS:

I also tried the ESP32 repo (https://github.com/G6EJD/G6EJD-ESP-File-Server), but it's still buggy and can't deal with files larger than ~5K. But this is also a good alternate route to go. It's possible the recent ESP32 core has introduced some bugs and many of my ESP32 AsyncFS-related examples can't run correctly now. I don't like to spend time to investigate what's wrong yet. Possibly going back to ESP32 core v1.0.6???
 
hello khoih,
I have SD, PSRAM ,SPI Flash and QSPI here on my site. I think the moste used will be SD and FS on the internal Flash. That what i tryed to get running on my post.
If LittleFs was working on all Flash the same way it should not be da big deal to save the files to the other Flash types.
Personaly i have ony worked with SDfat/LittleFs as i started with the Async Server.

I can do Testing/Debuging and try to fix errors, but be realistic i do not fully understand the code form the Async Server at the moment. But sill i am here to learn.

This mean i can take the callange, but i think i could not do this without support (everyone is welcome).
So if it is possible that you risk a look from time to time and maybe give some help that would be great.


Also yes i wold like to proceed!

Thanks for start port this features.
greedings
Jake
 
Hi Jakespeed, BriComp, everybody interested in this project,

Please find the new FS-not-working-yet AsyncFSWebServer_Teensy41 releases v1.4.2-alpha-0

As noted, you can try this library using either SD, PSRAM, (Q)SPI Flash, etc. by selecting a type in the code

Code:
#define USING_PSRAM                 true
#define USING_SPI_FLASH             false
#define USING_SPI_FRAM              false
#define USING_PROGRAM_FLASH         false
#define USING_QSPI_FLASH            false
#define USING_SPI_NAND              false
#define USING_QPI_NAND              false

To Jakespeed,

I can't be sure this library will work finally and everything will depend on you and the support of many community members. If your project is important and urgent, I suggest you try using other simpler ways, such as Synchronous WebServer, EthernetWebServer, etc. The Async seems to me over-complicated and only necessary in certain use-cases.

I'm not sure I can help to fix the issue even I'll try my best if possible.

Please start with the AsyncFSWebServer example to get some preliminary feelings.

Good Luck to this project,
 
Here I have some code with webserver, websocket and SD card.
It's based on the websockets2_Generic library and example 'SocketAndHttp_Teensy41_Server

I added SD card and MDNS features.
You can place your website onto the card, including subfolders, js and css and images and whatever. It doesn't handle a default document yet but that's easy to do yourself.
The code is not clean but it works.

I noticed that the reading of the pages- if you have some included javascripts and css files- (initially without cache at the first read) are sequential and will slow things down bigtime in a browser like chrome.
This is because the teensycode can only read one file at the time of the sdcard, and will meanwhile stall the other file requests that it wants to load.

To solve this, you can dynamically load the include files with the javascript example I added below the teensycode.

Code:
#if !(defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41))
  #error This is designed only for Teensy 4.1. Please check your Tools-> Boards
#endif

#define WEBSOCKETS_USE_ETHERNET     true
#define USE_NATIVE_ETHERNET         true
#define REQ_BUF_SZ   50

#include <SPI.h>
#include <WebSockets2_Generic.h>
#include <EthernetWebServer.h>
#include <TeensyID.h>
#include <SD.h>

using namespace websockets2_generic;

byte mac[6];
const uint16_t websocketsPort = 3000;
const byte maxSocketClients = 4;

WebsocketsClient socketClients[maxSocketClients];
WebsocketsServer socketServer;

#define WEBSERVER_PORT      80
EthernetServer httpServer(WEBSERVER_PORT);
#define USING_DHCP            false

#if !USING_DHCP
  // Set the static IP address to use if the DHCP fails to assign
  IPAddress myIP(192, 168, 2, 222);
  IPAddress myNetmask(255, 255, 255, 0);
  IPAddress myGW(192, 168, 2, 254);
  IPAddress mydnsServer(8, 8, 8, 8);
#endif

File webFile;
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer
const int chipSelect = BUILTIN_SDCARD;
int led = 13;
void setup(){
  pinMode(led, OUTPUT);
  if (!SD.begin(chipSelect)) { return;}    // init failed
  if (!SD.exists("index.html")) { return;}  // can't find index file

  teensyMAC(mac);
  #if USING_DHCP  
    if (Ethernet.begin(mac)){ } // Serial.print(Ethernet.localIP());}
    delay(1000);
  #else
    Ethernet.begin(mac, myIP, mydnsServer);
    delay(2000);
  #endif
  MDNS.begin("MYTEENSY", 2); //.local Domain name and number of services
  MDNS.setServiceName("MYTEENSY_webservices"); //Uncomment to change service name
  MDNS.addService("_http._tcp", 3000);
  MDNS.addService("_http._tcp", 80);
  socketServer.listen(websocketsPort);
  httpServer.begin();
}

int8_t getFreeSocketClientIndex(){
  for (byte i = 0; i < maxSocketClients; i++){
    if (!socketClients[i].available())
      return i;
    }
  return -1;
}

void handleMessage(WebsocketsClient &client, WebsocketsMessage message){
  auto data = message.data();
  // client.send("Echo: " + data);
}

void handleEvent(WebsocketsClient &client, WebsocketsEvent event, String data){
  if (event == WebsocketsEvent::ConnectionClosed){
    // Serial.println("Connection closed");
  }
}

void listenForSocketClients(){
  if (socketServer.poll()){
    int8_t freeIndex = getFreeSocketClientIndex();
    if (freeIndex >= 0){
      WebsocketsClient newClient = socketServer.accept();
      newClient.onMessage(handleMessage);
      newClient.onEvent(handleEvent);
      newClient.send("{\"pm\":2}"); //just a random json message to the client.
      socketClients[freeIndex] = newClient;
    }
  }
}

void pollSocketClients(){
  for (byte i = 0; i < maxSocketClients; i++)  {
    socketClients[i].poll();
  }
}

void listenForHttpClients(){
  EthernetClient client = httpServer.available();
  if (client){
    boolean currentLineIsBlank = true;
      while (client.connected()) {
        if (client.available()) {   // client data available to read
          char c = client.read(); // read 1 byte (character) from client
          if (req_index < (REQ_BUF_SZ - 1)) {
            HTTP_req[req_index] = c;          // save HTTP request character
            req_index++;
          }
          if (c == '\n' && currentLineIsBlank) {
            String thereq = String(HTTP_req);
            int theFileLeftPos = thereq.indexOf(" ");
            int theFileRightPos = thereq.indexOf(" ",theFileLeftPos+2);
            String theFile = thereq.substring(theFileLeftPos+2,(theFileRightPos));
            String contentType; 
            if (StrContains(HTTP_req, ".html")) { contentType = "Content-Type: text/html";}
            if (StrContains(HTTP_req, ".js")  ) { contentType = "Content-Type: text/javascript";}
            if (StrContains(HTTP_req, ".css") ) { contentType = "Content-Type: text/css";}
            if (StrContains(HTTP_req, ".svg") ) { contentType = "Content-Type: image/svg+xml";}
            if (StrContains(HTTP_req, ".png") ) { contentType = "Content-Type: image/png";}
            if (StrContains(HTTP_req, ".gif") ) { contentType = "Content-Type: image/gif";}
            if (StrContains(HTTP_req, ".jpg") ) { contentType = "Content-Type: image/jpeg";}
            if (StrContains(HTTP_req, ".ico") ) { contentType = "Content-Type: image/x-icon";}
            
            client.println("HTTP/1.1 200 OK");
            client.println(contentType);
            client.println("Cache-Control: public, max-age=31536000");
            //client.println("Connnection: close");
            client.println();
            digitalWrite(led,HIGH);
            webFile = SD.open(theFile.c_str(),O_READ);
            
            if (webFile) {
              const int bufSize = 1024; 
              byte clientBuf[bufSize];
              int clientCount = 0;
              char tBuf[513];
              while (webFile.available()){
                clientCount = webFile.read(tBuf,512);
                client.write((byte*)tBuf,clientCount);
              }
              webFile.close();
            }
            //client.close();
            digitalWrite(led,LOW);
            req_index = 0;
            StrClear(HTTP_req, REQ_BUF_SZ);
            break;
          }
      if (c == '\n') { currentLineIsBlank = true;}
      else if (c != '\r') { currentLineIsBlank = false;}
      } // end if (client.available())
    } // end while (client.connected())
    //delay(10);      // give the web browser time to receive the data
    client.stop();
  }
}

void loop(){
  listenForSocketClients();
  pollSocketClients();
  listenForHttpClients();
}

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length){
  for (int i = 0; i < length; i++) {str[i] = 0;}
}

char StrContains(char *str, char *sfind){
  char found = 0;
  char index = 0;
  char len;
  len = strlen(str);
  if (strlen(sfind) > len) { return 0;}
  while (index < len) {
    if (str[index] == sfind[found]) {
        found++;
        if (strlen(sfind) == found) {return 1;}
    }
    else {found = 0;}
    index++;
  }
  return 0;
}

Here is my javascript to load files sequentially within the html page, and do a body onload event for loadup() function:
It makes sure it loads the next file only when the previous file is fully done.

Code:
function loadup(){
	loadcss();
	document.getElementById('csa').onload = () =>{
		loadscript('js/jquery-3.5.0.js','asa');
		document.getElementById('asa').onload = () => {
			loadscript('js/jquery-ui.min.js','asb');
			document.getElementById('asb').onload = () => {
				loadscript('js/Chart.min.js','asc');
				document.getElementById('asc').onload = () => {
					loadscript('js/utils.js','asd');
					document.getElementById('asd').onload = () => {
						loadscript('js/jquery.knob.js','ase'); 
						document.getElementById('ase').onload = () => {
							init();
						}
					}
				}  
			}      
		}
	}      
}

function loadscript(thescript,theid){
	let script = document.createElement('script');
	script.setAttribute('src', thescript);
	script.setAttribute('id', theid);
	document.body.appendChild(script) ;
}

function loadcss(){
	let link = document.createElement('link');
	link.setAttribute('rel', 'stylesheet');
	link.setAttribute('id', 'csa');
	link.setAttribute('type','text/css');
	link.setAttribute('href', 'style.css');
	document.head.appendChild(link) ;
  
}
function init() {    
	
	
	ws = new WebSocket("ws://192.168.2.222:3000/"); // Your webservice address
	document.getElementById("log").innerText = "Loading ";

	ws.onopen = function() { 
		document.getElementById("log").innerText = "OK";
		ws.send("hello");
	};
	ws.onmessage = function(e) {updatevalues(e.data);document.getElementById("log").innerText = e.data;};
	ws.onclose = function() { setTimeout(init(),100);};
	ws.onerror = function(e) { document.getElementById("log").innerText = "Load Error ";};
}
 
Last edited:
Note: Chrome has a little bug that a css file takes a longer time to initialize before loading. firefox and safari don't have that issue. Also, a good tip is that if you have static images, put them as base64 encoded in your html page, also your favicon.
 
@Fertje: Dankjewel!!

Your code finally got my project up and running. I'm still learning and figuring out but this has helped me a very great deal, so thank you for sharing.
 
Happy new Year to you all!

I can´t use WebSockets with Async_WebServer_Teensy41.

I adopted this Sketch from ESP32 where it works well.


With Teensy 4.1 the Connection establishes but send no Data to the Client (HRESULT: 0x80072F78).

Code:
/****************************************************************************************************************************
  Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for Teensy41 QNEthernet

  For Teensy41 with QNEthernet

  AsyncWebServer_Teensy41 is a library for the Teensy41 with QNEthernet

  Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer)
  Built by Khoi Hoang https://github.com/khoih-prog/AsyncWebServer_Teensy41
  Licensed under GPLv3 license

  Copyright (c) 2015, Majenko Technologies
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:

  Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

  Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.

  Neither the name of Majenko Technologies nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************************************************************************/

#if !( defined(CORE_TEENSY) && defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) )
#error Only Teensy 4.1 supported
#endif

// Debug Level from 0 to 4
#define _TEENSY41_ASYNC_TCP_LOGLEVEL_       1
#define _AWS_TEENSY41_LOGLEVEL_             1

#define SHIELD_TYPE     "Teensy4.1 QNEthernet"

#if (_AWS_TEENSY41_LOGLEVEL_ > 3)
#warning Using QNEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
#endif

#define USING_DHCP            true
//#define USING_DHCP            false

#if !USING_DHCP
// Set the static IP address to use if the DHCP fails to assign
IPAddress myIP(192, 168, 179, 8);
IPAddress myNetmask(255, 255, 255, 0);
IPAddress myGW(192, 168, 179, 1);
//IPAddress mydnsServer(192, 168, 2, 1);
IPAddress mydnsServer(8, 8, 8, 8);
#endif

#include "QNEthernet.h"       // https://github.com/ssilverman/QNEthernet
using namespace qindesign::network;

#include <AsyncWebServer_Teensy41.h>

AsyncWebServer    server(80);
AsyncWebSocket ws("/ws");

int reqCount = 0;                // number of requests received

const int led = 13;

void handleRoot(AsyncWebServerRequest *request)
{
  digitalWrite(led, 1);

#define BUFFER_SIZE     400

  char temp[BUFFER_SIZE];
  int sec = millis() / 1000;
  int min = sec / 60;
  int hr = min / 60;
  int day = hr / 24;

  snprintf(temp, BUFFER_SIZE - 1,
           "<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>AsyncWebServer-%s</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h2>AsyncWebServer_Teensy41!</h2>\
<h3>running on %s</h3>\
<p>Uptime: %d d %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60);

  request->send(200, "text/html", temp);

  digitalWrite(led, 0);
}

void handleNotFound(AsyncWebServerRequest *request)
{
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";

  message += "URI: ";
  message += request->url();
  message += "\nMethod: ";
  message += (request->method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += request->args();
  message += "\n";

  for (uint8_t i = 0; i < request->args(); i++)
  {
    message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
  }

  request->send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void drawGraph(AsyncWebServerRequest *request)
{
  String out;

  out.reserve(4000);
  char temp[70];

  out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"310\" height=\"150\">\n";
  out += "<rect width=\"310\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"2\" stroke=\"rgb(0, 0, 0)\" />\n";
  out += "<g stroke=\"blue\">\n";
  int y = rand() % 130;

  for (int x = 10; x < 300; x += 10)
  {
    int y2 = rand() % 130;
    sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"2\" />\n", x, 140 - y, x + 10, 140 - y2);
    out += temp;
    y = y2;
  }

  out += "</g>\n</svg>\n";

  request->send(200, "image/svg+xml", out);
}

void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {

  if (type == WS_EVT_CONNECT) {
    Serial6.println("Websocket client connection received");
    client->text("Hello from ESP32 Server");
  } else if (type == WS_EVT_DISCONNECT) {
    Serial6.println("Client disconnected");
  }
}

void setup()
{
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);

  Serial6.begin(115200);

  while (!Serial6 && millis() < 5000);

  delay(200);

  Serial6.print("\nStart Async_AdvancedWebServer on ");
  Serial6.print(BOARD_NAME);
  Serial6.print(" with ");
  Serial6.println(SHIELD_TYPE);
  Serial6.println(ASYNC_WEBSERVER_TEENSY41_VERSION);

  delay(500);

#if USING_DHCP
  // Start the Ethernet connection, using DHCP
  Serial6.print("Initialize Ethernet using DHCP => ");
  Ethernet.begin();
#else
  // Start the Ethernet connection, using static IP
  Serial6.print("Initialize Ethernet using static IP => ");
  Ethernet.begin(myIP, myNetmask, myGW);
  Ethernet.setDNSServerIP(mydnsServer);
#endif

  if (!Ethernet.waitForLocalIP(5000))
  {
    Serial6.println(F("Failed to configure Ethernet"));

    if (!Ethernet.linkStatus())
    {
      Serial6.println(F("Ethernet cable is not connected."));
    }

    // Stay here forever
    while (true)
    {
      delay(1);
    }
  }
  else
  {
    Serial6.print(F("Connected! IP address:"));
    Serial6.println(Ethernet.localIP());
  }

#if USING_DHCP
  delay(1000);
#else
  delay(2000);
#endif

  ws.onEvent(onWsEvent);
  server.addHandler(&ws);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    handleRoot(request);
  });

  server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request)
  {
    drawGraph(request);
  });

  server.on("/inline", [](AsyncWebServerRequest * request)
  {
    request->send(200, "text/plain", "This works as well");
  });

  server.onNotFound(handleNotFound);

  server.begin();

  Serial6.print(F("HTTP EthernetWebServer is @ IP : "));
  Serial6.println(Ethernet.localIP());
}

void loop()
{
}
 
I can´t use WebSockets with Async_WebServer_Teensy41. I adopted this Sketch from ESP32 where it works well. With Teensy 4.1 the Connection establishes but send no Data to the Client (HRESULT: 0x80072F78).

Have you tried posting a question to the library's author on github? I posted an issue to him once and he was quite responsive.
 
Hi, I thought about that too, however the Project is now archived. I hoped someone who knows this Library could know more.
 
So I have more time as normal to play I found out this:

Wireshark told me that the OPCODE for Websocket is not OK. It shoud be 10000001 but comes not to the Client as this.
In the last Function of "asyncWebserver_Teensy41" it seems to be ok -> Giving this Data to "QNEthernet" lwip-Function tcp_write.
Here Serial6.printLine() dont work any more, so I have to find out how CORE-Debug works :rolleyes:

Thats where i am with my limited Knowledge.
 
Hello All.
I'm using AsyncWebServer + Teensy 4.1 in many of my projects. Does anyone know why author has archived repositories?

I have encountered an issue:

I'm running Async_SimpleWebServer
When router DHCP lease time elapses for teensy board, this happens (debug lvl 4):

Code:
[T41_ASYNC] setCloseError() to: Connection aborted => -13
[T41_ASYNC] _error: ID = 3 , _pcb = OK
[T41_ASYNC] errorToString = Connection aborted , err = -13
After that, If I try to send new request to the server (from the browser) teensy board is completly freezed until reboot.
 
Hi Mike0675

I just adapted the https://github.com/khoih-prog/AsyncWebServer_RP2040W/tree/main/examples/Async_WebSocketsServer code and posted in this library as https://github.com/khoih-prog/AsyncWebServer_Teensy41/tree/main/examples/Async_WebSocketsServer for Teensy4_1

The WebSockets Server is working correctly, meaning the WS protocol is handled correctly in this library

Selection_259.png


---

The terminal debug is

Async_WebSocketsServer on TEENSY 4.1

Code:
Starting Async_WebSocketsServer on TEENSY 4.1
 with Teensy4.1 QNEthernet
AsyncWebServer_Teensy41 v1.6.1
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.119
ws[Server: /ws][ClientID: 1] WSClient connected
ws[Server: /ws][ClientID: 1] text-message[len: 13]: Hello, Server
ws[Server: /ws][ClientID: 1] WSClient disconnected
ws[Server: /ws][ClientID: 2] WSClient connected
ws[Server: /ws][ClientID: 2] text-message[len: 13]: Hello, Server
ws[Server: /ws][ClientID: 2] WSClient disconnected
ws[Server: /ws][ClientID: 3] WSClient connected
ws[Server: /ws][ClientID: 3] text-message[len: 13]: Hello, Server



The WebSockets Client code using python used to test

https://github.com/khoih-prog/Async...ketsServer/WSClient_Python/WSClient.py#L1-L15

Just modify to match the IP of the server. In this case 192.168.2.119

Check the new release v1.6.2

Releases v1.6.2

1. Add examples https://github.com/khoih-prog/AsyncWebServer_Teensy41/tree/main/examples/Async_WebSocketsServer to demo how to use Async_WebSockets
 
Hi, khoi-prog
Thank you for your work and quick reply.
This is more than anyone could await.

Greetings to Toronto from Germany.
 
Back
Top