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;
}
}