Arduino Server Works-Wemos LOLIN D32 PRO

TMS Web Core is running a $9 Arduino board as a server. The server boots, connected to the home wifi access point and is ready in 2 seconds. The board isn't Arduino branded but programmed in the Arduino IDE. Way to go TMS Software and contributors with this exciting new Web Core project!


My test Wemos board has wifi and SD card reader. I'll try later with a $5 Wemos D1 Mini pro with Wifi and on board SD type memory (16 meg of SPIFFS).
In my example I did the following in Delphi:
Create New TMS Web Application.
Save All and name the files..
Unit Name: Small1 <Default extension>
Project name: SmallP <Default extension>
Project name: Small1.htm  <- Must add .htm to the end to make it a 3 char extension else it will be html
Project Group: SmallP <Default extension>

For my first test I added 2 edit boxes, a button and a webpaintbox.
Made a set of points then called pb1.Canvas.Polygon(aPtSet);

Compile and copy Small1.htm & smallp.js to a FAT32 micro SD card.
--
From the Arduino IDE.
Install the Wemos board software. See the Wemos web site for this.
Open the Example Project SDWebServer.
Change the following:
const char* ssid = "plctim";
const char* password = "Voyager6779";
The Wemos uses Pin 4 as chip select for the SD card and not the default from the example.
Find   if (SD.begin(SS)) {  
change to   if (SD.begin(4)) { 
Compile & Upload to the Wemos and open the serial monitor to see the web server address.

Be amazed at what TMS has put together.

What to look out for:
Wait for the web page to display. It takes me about 5 seconds for the load. Then everything is a fast as your computer.

Make your Project name and the html file name 8 chars at most.
If you saved the first time as an ".html" file extension, click on the html file and change the file name in the properties in Delphi and recompile, or change it in explorer.

If you get SD card not mounted on the Wemos, Look for the SD.Begin(4) , make sure it is NOT exFat or NTFS formatted.

The Wemos product page. This item is nice because it has everything. There are other companies making the same board and even using the Wemos name. If you look on ebay, Alliexpress, Alibaba, Amazon you will find a range from $4 to $15. I have used over 100 Wemos Mini Pros while sponsoring school projects. Mostly ordered from the "official" Wemos sales site on Aliexpress.
Picture of the minimal TMS Web Core server in the forground:

Wow, this is really really awesome!


I'm wondering, for the initial hit, is this the web server that needs to kick in, is this the size of the web client app (what size is it now? Did you try to reduce it with a js minify that typically achieves a 40% -  50% reduction)

I think it will be really nice if we can make this an article / showcase that we can put in our blog.


Very Nice!!!
I am working a lot with these Wemos D1 pro versions of the esp8266 and I am very interested to know if you managed to program that one as well. Would be a nice replacement for the ESPEasy firmware I am running now.
I did the test with the D1 mini Pro V2.0 and it renders the TMS web core generated page within 3 seconds. The CPU is set to 160mhz and the TMS web core files are on SPIFFS memory onboard. I believe this memory is much faster than the SD card I originally used on the Wemos D32/ESP-32. Also the file names can end in html and not limited to 8.3 like the SD I had formatted for original testing on the D32.
I did get some help from TMS on loading speed and using js minify. I also compressed the resulting js file to a gz file. The server code below will looks for files ending with gz first.

The Arduino IDE code for the server is in progress with the web service development. 
I expect the example to include the WEMOS Mini Pro($5), temp&humidity shield($2), 8x8 led shield($2), and 128x128 1.44" TFT shield($4),Tripple base ($1). The sample prices are from the Wemos store.


// This is copied from A-Beginner's-Guide-to-the-ESP8266.pdf
// It should use this...
// Extension of ESP8266WebServer with SPIFFS handlers built in
// #include <SPIFFSReadServer.h> //http://ryandowning.net/SPIFFSReadServer
// upload data folder to chip with Arduino ESP8266 filesystem uploader
// https://github.com/esp8266/arduino-esp8266fs-plugin




#include  <ESP8266WiFi.h> 
#include  <ESP8266WiFiMulti.h> 
#include  <ArduinoOTA.h> 
#include  <ESP8266WebServer.h> 
#include  <ESP8266mDNS.h> 
#include  <FS.h> 
#include  <WebSocketsServer.h> 
#include <Adafruit_GFX.h>
#include <WEMOS_Matrix_GFX.h>
//#include <WiFi.h>
MLED matrix(7); //set intensity=7 (maximum)


static const uint8_t PROGMEM
  smile_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10100101,
    B10011001,
    B01000010,
    B00111100 },
  neutral_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10111101,
    B10000001,
    B01000010,
    B00111100 },
  frown_bmp[] =
  { B00111100,
    B01000010,
    B10100101,
    B10000001,
    B10011001,
    B10100101,
    B01000010,
    B00111100 };


ESP8266WiFiMulti  wifiMulti;              //  Create  an  instance  of  the ESP8266WiFiMulti  class,  called  'wifiMulti' 
ESP8266WebServer  server(80);             //  Create  a webserver object  that  listens for HTTP  request on  port  80 
WebSocketsServer webSocket(6834);        //  create  a websocket server  on  port  81 
File fsUploadFile;                                 //  a File  variable  to  temporarily store the received  file 


//const  char  *ssid = "ESP8266  Access  Point"; //  The name  of  the Wi-Fi network that  will  be  created const char  *password = "thereisnospoon";     //  The password  required  to  connect to  it, leave blank for an  open  network 
const char* ssid = "esptim";
const char* password = "Voyager6779";


//const char  *OTAName  = "ESP8266";                      //  A name  and a password  for the OTA service 
//const char  *OTAPassword  = "esp8266";
const char*  OTAName  = "ESP8266";                      //  A name  and a password  for the OTA service 
const char*  OTAPassword  = "esp8266";
//#define LED_RED         15                        //  specify the pins  with  an  RGB LED connected 
//#define LED_GREEN     12 
//#define LED_BLUE        13 


const char* mdnsName  = "esp8266";  //  Domain  name  for the mDNS  responder 




void setup() {
  // put your setup code here, to run once:
//  pinMode(LED_RED,  OUTPUT);        //  the pins  with  LEDs  connected are outputs   
//  pinMode(LED_GREEN,  OUTPUT);    
//  pinMode(LED_BLUE, OUTPUT);    
  Serial.begin(115200);               //  Start the Serial  communication to  send  messages  to  the computer    
  delay(10);    
  Serial.println("\r\n");   
  Serial.println("SPIFFSServer V1.0.4");   
  Serial.println("\r\n");   
  startWiFi();                                  //  Start a Wi-Fi access  point,  and try to  connect to  some  given access  points. Then  wait  for either  an  AP  or  STA connection        
  startOTA();                                   //  Start the OTA service       
  startSPIFFS();                              //  Start the SPIFFS  and list  all contents    
  startWebSocket();                       //  Start a WebSocket server        
  startMDNS();                                  //  Start the mDNS  responder   
  startServer();                              //  Start a HTTP  server  with  a file  read  handler and an  upload  handler
  DrawNeutral();
}


bool  rainbow = false;                          //  The rainbow effect  is  turned  off on  startup 
unsigned  long  prevMillis  = millis(); 
int hue = 0; 


void loop() {
  // put your main code here, to run repeatedly:
  webSocket.loop();                                                      //  constantly  check for websocket events    
  server.handleClient();                                            //  run the server    
  ArduinoOTA.handle();                                                //  listen  for OTA events    
  if(rainbow) {                                                             //  if  the rainbow effect  is  turned  on        
      if(++hue  ==  360)                                                //  Cycle through the color wheel (increment  by  one degree  every 32  ms)               
      hue = 0;            
      setHue(hue);                                                        //  Set the RGB LED to  the right color           
  } 
}


String  formatBytes(size_t  bytes)  { //  convert sizes in  bytes to  KB  and MB    
  if  (bytes  < 1024) {       
    return  String(bytes) + "B";    
  } else  if  (bytes  < (1024 * 1024))  {       
    return  String(bytes  / 1024.0) + "KB";   
  } else  if  (bytes  < (1024 * 1024  * 1024))  {       
    return  String(bytes  / 1024.0  / 1024.0) + "MB";   
  } 
} 
String  getContentType(String filename) { //  determine the filetype  of  a given filename, based on  the extension   
  if(filename.endsWith(".src")) return filename.substring(0, filename.lastIndexOf("."));
  else if(filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".html")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".js")) return "application/javascript";
  else if(filename.endsWith(".png")) return "image/png";
  else if(filename.endsWith(".gif")) return "image/gif";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  else if(filename.endsWith(".ico")) return "image/x-icon";
  else if(filename.endsWith(".xml")) return "text/xml";
  else if(filename.endsWith(".pdf")) return "application/pdf";
  else if(filename.endsWith(".zip")) return "application/zip";
  else if(filename.endsWith(".gz")) return "application/x-gzip";
  return  "text/plain"; 
}


void DrawSmile(){
  matrix.clear();
  matrix.drawBitmap(0, 0, smile_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
}


void DrawClearDisplay(){
  matrix.clear();
  matrix.writeDisplay();
}


void DrawNeutral(){
  matrix.clear();
  matrix.drawBitmap(0, 0, neutral_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
}


void DrawFrown(){
  matrix.clear();
  matrix.drawBitmap(0, 0, frown_bmp, 8, 8, LED_ON);
  matrix.writeDisplay();
}


void  setHue(int  hue)  { //  Set the RGB LED to  a given hue (color) (0° = Red,  120°  = Green,  240°  = Blue)   
  hue %=  360;                                      //  hue is  an  angle between 0 and 359°    
  float radH  = hue*3.142/180;      //  Convert degrees to  radians   
  float rf, gf, bf;       
  if(hue>=0 &&  hue<120){               //  Convert from  HSI color space to  RGB                                   
    rf  = cos(radH*3/4);        
    gf  = sin(radH*3/4);        
    bf  = 0;    
  } else  if(hue>=120 &&  hue<240){       
    radH  -=  2.09439;        
    gf  = cos(radH*3/4);        
    bf  = sin(radH*3/4);        
    rf  = 0;    
  } else  if(hue>=240 &&  hue<360){       
    radH  -=  4.188787;       
    bf  = cos(radH*3/4);        
    rf  = sin(radH*3/4);        
    gf  = 0;    
  }   
  int r = rf*rf*1023;   
  int g = gf*gf*1023;   
  int b = bf*bf*1023;       
//  analogWrite(LED_RED,      r);       //  Write the right color to  the LED output  pins    
//  analogWrite(LED_GREEN,  g);   
//  analogWrite(LED_BLUE,   b); 
 
}


void  startWiFi() { //  Start a Wi-Fi access  point,  and try to  connect to  some  given access  points. Then  wait  for either  an  AP  or  STA connection    
  WiFi.softAP(ssid, password);                          //  Start the access  point   
  Serial.print("Access  Point \"");   
  Serial.print(ssid);
  Serial.println("\"  started\r\n");    
  wifiMulti.addAP("plctim", "Voyager6779");      //  add Wi-Fi networks  you want  to  connect to    
  wifiMulti.addAP("PLC-1901", "B3@st@Plc1901");    
//  wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");    
  Serial.println("Connecting");   
  while (wifiMulti.run()  !=  WL_CONNECTED  &&  WiFi.softAPgetStationNum()  < 1)  {   //  Wait  for the Wi-Fi to  connect       
    delay(250);       
    Serial.print('.');    
  }   
  Serial.println("\r\n");   
  if(WiFi.softAPgetStationNum() ==  0)  {           //  If  the ESP is  connected to  an  AP        
    Serial.print("Connected to  ");       
    Serial.println(WiFi.SSID());                          //  Tell  us  what  network we're connected to        
    Serial.print("IP  address:\t");       
    Serial.print(WiFi.localIP());                       //  Send  the IP  address of  the ESP8266 to  the computer    
  } 
  else  
  {                                                                     //  If  a station is  connected to  the ESP SoftAP        
    Serial.print("Station connected to  ESP8266 AP");   
  }    
  Serial.println("\r\n"); 
} 


void  startOTA()  { //  Start the OTA service   
  ArduinoOTA.setHostname(OTAName);    
  ArduinoOTA.setPassword(OTAPassword);    
  ArduinoOTA.onStart([]() {       
    Serial.println("Start");        
//    digitalWrite(LED_RED, 0);       //  turn  off the LEDs        
//    digitalWrite(LED_GREEN, 0);       
//    digitalWrite(LED_BLUE,  0);   
    });   


    ArduinoOTA.onEnd([]() {       
      Serial.println("\r\nEnd");    
  });   
  ArduinoOTA.onProgress([](unsigned int progress, unsigned  int total)  {       
    Serial.printf("Progress:  %u%%\r",  (progress / (total  / 100)));   
  });   
  ArduinoOTA.onError([](ota_error_t error)  {       
    Serial.printf("Error[%u]: ",  error);       
    if  (error  ==  OTA_AUTH_ERROR) 
      Serial.println("Auth  Failed");       
    else  if  (error  ==  OTA_BEGIN_ERROR)  Serial.println("Begin Failed");       
    else  if  (error  ==  OTA_CONNECT_ERROR)  Serial.println("Connect Failed");       
    else  if  (error  ==  OTA_RECEIVE_ERROR)  Serial.println("Receive Failed");       
    else  if  (error  ==  OTA_END_ERROR)  Serial.println("End Failed");   
  });   
  ArduinoOTA.begin();   
  Serial.println("OTA ready\r\n"); 
} 


void startSPIFFS() { //  Start the SPIFFS  and list  all contents    
  SPIFFS.begin();                                                         //  Start the SPI Flash File  System  (SPIFFS)    
  Serial.println("SPIFFS  started.  Contents:");    
  {       
    Dir dir = SPIFFS.openDir("/");        
    while (dir.next())  {                                           //  List  the file  system  contents            
      String  fileName  = dir.fileName();           
      size_t  fileSize  = dir.fileSize();           
      Serial.printf("\tFS File: %s, size: %s\r\n",  fileName.c_str(), formatBytes(fileSize).c_str());       
    }       
    Serial.printf("\n");    
  } 
} 


void  startWebSocket()  { //  Start a WebSocket server    
  webSocket.begin();                                                    //  start the websocket server    
  webSocket.onEvent(webSocketEvent);                    //  if  there's an  incomming websocket message,  go  to  function  'webSocketEvent'    
  Serial.println("WebSocket server  started."); 
} 


void  startMDNS() { //  Start the mDNS  responder   
  MDNS.begin(mdnsName);                                               //  start the multicast domain  name  server    
  Serial.print("mDNS  responder started:  http://");    
  Serial.print(mdnsName);   
  Serial.println(".local"); 
  MDNS.addService("ws", "tcp", 81);
} 


void  startServer() { //  Start a HTTP  server  with  a file  read  handler and an  upload  handler   
  server.on("/edit.html",   HTTP_POST,  []()  {   //  If  a POST  request is  sent  to  the /edit.html  address,        
    server.send(200,  "text/plain", "");      
  },  handleFileUpload);                                              //  go  to  'handleFileUpload'    
  
  server.onNotFound(handleNotFound);   //  if  someone requests  any other file  or  page, go  to  function  'handleNotFound'                                                                                            
                                       //  and check if  the file  exists    
  server.begin();                                                         //  start the HTTP  server
  Serial.println("HTTP  server  started."); 
} 




void  handleNotFound(){ //  if  the requested file  or  page  doesn't exist,  return  a 404 not found error   
  if(!handleFileRead(server.uri())){                    //  check if  the file  exists  in  the flash memory  (SPIFFS), if  so, send  it        
    server.send(404,  "text/plain", "404: File  Not Found");    
  } 
} 


bool  handleFileRead(String path) { //  send  the right file  to  the client  (if it  exists)   
  Serial.println("handleFileRead: " + path);    
  if  (path.endsWith("/"))  path  +=  "index.html";                   //  If  a folder  is  requested,  send  the index file    
  String  contentType = getContentType(path);                         //  Get the MIME  type    
  String  pathWithGz  = path  + ".gz";    
  if  (SPIFFS.exists(pathWithGz)  ||  SPIFFS.exists(path))  { //  If  the file  exists, either  as  a compressed  archive,  or  normal        
    if  (SPIFFS.exists(pathWithGz))                                                 //  If  there's a compressed  version available           
      path  +=  ".gz";                                                                                  //  Use the compressed  verion        
      File  file  = SPIFFS.open(path, "r");                                       //  Open  the file        
      size_t  sent  = server.streamFile(file, contentType);       //  Send  it  to  the client        
      file.close();                                                                                   //  Close the file  again       
      Serial.println(String("\tSent file: ")  + path);        
      return  true;   
    }   
    Serial.println(String("\tFile Not Found:  ")  + path);      //  If  the file  doesn't exist,  return  false   return  false; 
} 


void handleFileUpload(){ //  upload  a new file  to  the SPIFFS    
  HTTPUpload& upload  = server.upload();    
  String  path;   
  if(upload.status  ==  UPLOAD_FILE_START){       
    path  = upload.filename;        
    if(!path.startsWith("/")) path  = "/"+path;       
    if(!path.endsWith(".gz")) {                                                   //  The file  server  always  prefers a compressed  version of  a file              
      String  pathWithGz  = path+".gz";                                       //  So  if  an  uploaded  file  is  not compressed, the existing  compressed            
      if(SPIFFS.exists(pathWithGz))                                           //  version of  that  file  must  be  deleted (if it  exists)                 
        SPIFFS.remove(pathWithGz);        
    }       
    
    Serial.print("handleFileUpload  Name: "); 
    Serial.println(path);       
    
    fsUploadFile  = SPIFFS.open(path, "w");                       //  Open  the file  for writing in  SPIFFS  (create if  it  doesn't exist)        
    path  = String();   
  } 
  else  if(upload.status  ==  UPLOAD_FILE_WRITE){       
    if(fsUploadFile)            
      fsUploadFile.write(upload.buf,  upload.currentSize);  //  Write the received  bytes to  the file    
  } else  if(upload.status  ==  UPLOAD_FILE_END){       
    if(fsUploadFile)  {                                                                       //  If  the file  was successfully  created           
      fsUploadFile.close();                                                             //  Close the file  again           
      Serial.print("handleFileUpload  Size: "); 
      Serial.println(upload.totalSize);           
      server.sendHeader("Location","/success.html");            //  Redirect  the client  to  the success page            
      server.send(303);       
    } else  {           
      server.send(500,  "text/plain", "500: couldn't  create  file");       
    }   
  } 
} 


void  webSocketEvent(uint8_t  num,  WStype_t  type, uint8_t * payload,  size_t  lenght) { //  When  a WebSocket message is  received    
  
  switch  (type)  {       
    case  WStype_DISCONNECTED:                          //  if  the websocket is  disconnected            
      Serial.printf("[%u] Disconnected!\n", num);           
      break;        
    case  WStype_CONNECTED: {                           //  if  a new websocket connection  is  established               
      IPAddress ip  = webSocket.remoteIP(num);                
      Serial.printf("[%u] Connected from  %d.%d.%d.%d url:  %s\n",  num,  ip[0],  ip[1],  ip[2],  ip[3],  payload);               
      rainbow = false;                                    //  Turn  rainbow off when  a new connection  is  established           
    }           
    break;        
  case  WStype_TEXT:                                          //  if  new text  data  is  received            
    Serial.printf("[%u] get Text: %s\n",  num,  payload);


    if  (payload[0] ==  '#')  {                       //  we  get RGB data                
      uint32_t  rgb = (uint32_t)  strtol((const char  *)  &payload[1],  NULL, 16);      //  decode  rgb data                
      int r = ((rgb >>  20) & 0x3FF);                                         //  10  bits  per color,  so  R:  bits  20-29               
      int g = ((rgb >>  10) & 0x3FF);                                         //  G:  bits  10-19               
      int b =                   rgb & 0x3FF;                                            //  B:  bits    0-9               
//      analogWrite(LED_RED,      r);                                                 //  write it  to  the LED output  pins                
//      analogWrite(LED_GREEN,  g);               
//      analogWrite(LED_BLUE,   b);           
    } else  if  (payload[0] ==  'R')  {                                           //  the browser sends an  R when  the rainbow effect  is  enabled               
        rainbow = true;           
    } else  if  (payload[0] ==  'N')  {                                           //  the browser sends an  N when  the rainbow effect  is  disabled                
      rainbow = false;            
    }           
    break;    
  } 
} 



I did the test with the D1 mini Pro V2.0 and it renders the TMS web core generated page within 3 seconds. The CPU is set to 160mhz and the TMS web core files are on SPIFFS memory onboard. I believe this memory is much faster than the SD card I originally used on the Wemos D32/ESP-32. Also the file names can end in html and not limited to 8.3 like the SD I had formatted for original testing on the D32.
I did get some help from TMS on loading speed and using js minify. I also compressed the resulting js file to a gz file. The server code below will looks for files ending with gz first.

The Arduino IDE code for the server is in progress with the web service development. 
I expect the example to include the WEMOS Mini Pro($5), temp&humidity shield($2), 8x8 led shield($2), and 128x128 1.44" TFT shield($4),Tripple base ($1). The sample prices are from the Wemos store.
- I'm replacing a prior post with server code in it.
Great work!
Thank you for sharing your code. I'll look in to it as soon as I have the time.
Normally I don't use these shields, but I'll order them to try and build your example.