Links2004 / arduinoWebSockets

arduinoWebSockets
GNU Lesser General Public License v2.1
1.9k stars 555 forks source link

use web-socket as secure single connection #56

Closed mkeyno closed 8 years ago

mkeyno commented 8 years ago

hi Markus , I have webserver sketch which is serve about 500 kb html file in SPIFF, if I use the only HTTP webserver , the latency is long , the download is so slow and somehow the module cant understand which client is main and chocked by request of other clients, however your suggestion to using ESPAsyncTCP & ESPAsyncWebServer was not successful so I thought maybe we could use webserver to create the secure long stand single connection with client and whereas the connection is live the module can check sensor status in loop
any replay appreciated

Links2004 commented 8 years ago

sending the first html page to the browser can only be done by a HTTP server. if you have send the first page with some javascript code you can establish a web socket connection to the ESP and load other data if you like.

the first page need to come form a web server, if you not need a standalone system (only ESP) you may can host the page on a local server in the network or on the Internet.

mkeyno commented 8 years ago

Dear Markus , can you elaborate in details? I have webpages on the SPIFF , when it loaded in the browser, constantly send ajax request to ESP to report back one sensor status , I have slow download for other pages and sometime my poor ESP-01 get chocked and need hardware reset , can you send me example for first HTTP load and then establish websocket link

Links2004 commented 8 years ago

you can use as base: https://github.com/Links2004/arduinoWebSockets/blob/master/examples/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino

only change the loop too:

unsigned long previousMillis = 0;
const long interval = 250; // update interval

void loop() {
    webSocket.loop();
    server.handleClient();
 unsigned long currentMillis = millis();
  if(currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis; 
  String data = "SEND DATA to Clients";  
  webSocket.broadcastTXT(data);
  }

}

and it will send all 250ms some data to all connected clients.

mkeyno commented 8 years ago

Hi Markus @Links2004 , why do we need server.handleClient();? why webSocket.loop(); cant handle both server and client websocket request ? also has any one complain or declare any issue with this new last stable version , I've compile your example but it seems my module (nodemuc) never go to running mode , also do you know anyone links or example who elaborate true power of your code

Links2004 commented 8 years ago

there are two parts the webserver (http) and the websocket server (ws). the "WebSocketServer_LEDcontrol.ino" example has no websocket client init. I use the 2.1.0 of the ESP core and it running fine.

the websocket can do many thinks, on cool example is: https://twitter.com/ESP8266/status/684836968509734913 or http://adityatannu.com/blog/post/2016/01/24/ESP8266-Websockets-demo-using-NeoPixels.html

mkeyno commented 8 years ago

thanks Markus @Links2004 , one more question , can we establish one connection at time and certify this connection by special cookie and disable any other request to keep the connection healthy and fast?

BR

Links2004 commented 8 years ago

cookies are not supported, but you can use HTTP Authorization to allow only valide clients access. https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L81

and you can limit the server to one client: https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L31 but when you use Authorization the server will drop all not valide clients so not really needed to limit the client count.

mkeyno commented 8 years ago

thanks Markus @Links2004 wish you could publish more example of your codes features,

mkeyno commented 8 years ago

I used following example of platform Arduino 1.64 with last stable version , however nor ESP-12 nor ESP-01 working and issue following error successful software rest

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#include <NeoPixelBus.h>
#include <FS.h>

#define pixelCount 16
#define colorSaturation 255
NeoPixelBus strip = NeoPixelBus(pixelCount, 8, NEO_GRB | NEO_KHZ800);
RgbColor red = RgbColor(colorSaturation, 0, 0);
RgbColor green = RgbColor(0, colorSaturation, 0);
RgbColor blue = RgbColor(0, 0, colorSaturation);
RgbColor white = RgbColor(colorSaturation);
RgbColor black = RgbColor(0);

#define USE_SERIAL Serial

const char* ssid     = "my SSID";
const char* password = "012345678";
const char* mDNSid   = "myWebsocketsTest";

ESP8266WiFiMulti WiFiMulti;

ESP8266WebServer server(80);
//holds the current upload
File fsUploadFile;
WebSocketsServer webSocket = WebSocketsServer(81);

//format bytes
String formatBytes(size_t bytes){
  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";
  } else {
    return String(bytes/1024.0/1024.0/1024.0)+"GB";
  }
}

String getContentType(String filename){
  if(server.hasArg("download")) return "application/octet-stream";
  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/x-pdf";
  else if(filename.endsWith(".zip")) return "application/x-zip";
  else if(filename.endsWith(".gz")) return "application/x-gzip";
  return "text/plain";
}

bool handleFileRead(String path){
  Serial.println("handleFileRead: " + path);
  if(path.endsWith("/")) path += "index.htm";
  String contentType = getContentType(path);
  String pathWithGz = path + ".gz";
  if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
    if(SPIFFS.exists(pathWithGz))
      path += ".gz";
    File file = SPIFFS.open(path, "r");
    size_t sent = server.streamFile(file, contentType);
    file.close();
    return true;
  }
  return false;
}

void handleFileUpload(){
  if(server.uri() != "/edit") return;
  HTTPUpload& upload = server.upload();
  if(upload.status == UPLOAD_FILE_START){
    String filename = upload.filename;
    if(!filename.startsWith("/")) filename = "/"+filename;
    Serial.print("handleFileUpload Name: "); Serial.println(filename);
    fsUploadFile = SPIFFS.open(filename, "w");
    filename = String();
  } else if(upload.status == UPLOAD_FILE_WRITE){
    //Serial.print("handleFileUpload Data: "); Serial.println(upload.currentSize);
    if(fsUploadFile)
      fsUploadFile.write(upload.buf, upload.currentSize);
  } else if(upload.status == UPLOAD_FILE_END){
    if(fsUploadFile)
      fsUploadFile.close();
    Serial.print("handleFileUpload Size: "); Serial.println(upload.totalSize);
  }
}

void handleFileDelete(){
  if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS");
  String path = server.arg(0);
  Serial.println("handleFileDelete: " + path);
  if(path == "/")
    return server.send(500, "text/plain", "BAD PATH");
  if(!SPIFFS.exists(path))
    return server.send(404, "text/plain", "FileNotFound");
  SPIFFS.remove(path);
  server.send(200, "text/plain", "");
  path = String();
}

void handleFileCreate(){
  if(server.args() == 0)
    return server.send(500, "text/plain", "BAD ARGS");
  String path = server.arg(0);
  Serial.println("handleFileCreate: " + path);
  if(path == "/")
    return server.send(500, "text/plain", "BAD PATH");
  if(SPIFFS.exists(path))
    return server.send(500, "text/plain", "FILE EXISTS");
  File file = SPIFFS.open(path, "w");
  if(file)
    file.close();
  else
    return server.send(500, "text/plain", "CREATE FAILED");
  server.send(200, "text/plain", "");
  path = String();
}

void handleFileList() {
  if(!server.hasArg("dir")) {server.send(500, "text/plain", "BAD ARGS"); return;}

  String path = server.arg("dir");
  Serial.println("handleFileList: " + path);
  Dir dir = SPIFFS.openDir(path);
  path = String();

  String output = "[";
  while(dir.next()){
    File entry = dir.openFile("r");
    if (output != "[") output += ',';
    bool isDir = false;
    output += "{\"type\":\"";
    output += (isDir)?"dir":"file";
    output += "\",\"name\":\"";
    output += String(entry.name()).substring(1);
    output += "\"}";
    entry.close();
  }

  output += "]";
  server.send(200, "text/json", output);
}

void setColor(RgbColor color, int pixel) {

  for (int i=0; i<pixelCount; i++) {
    strip.SetPixelColor(i, black);
  }
  strip.SetPixelColor(pixel, color);
  strip.Show();
}

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) {

    switch(type) {
        case WStype_DISCONNECTED:
            Serial.printf("[%u] Disconnected!\n", num);
            break;
        case WStype_CONNECTED: {
            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);

            // send message to client
            webSocket.sendTXT(num, "Connected");
        }
            break;
        case WStype_TEXT:

            Serial.printf("[%u] get Text: %s\n", num, payload);

            if(payload[0] == '#') {
                // we get RGB data
                // decode rgb data
                uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);

                // NeoPixels
                for (int i=0; i<pixelCount; i++) {
                  strip.SetPixelColor(i, RgbColor(((rgb >> 16) & 0xFF), ((rgb >> 8) & 0xFF),((rgb >> 0) & 0xFF) ));
                }
                strip.Show();
            }

            if(payload[0] == '*') {
                // we get Pixel number
                uint32_t PixelNumber = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
                // NeoPixels
                for (int i=0; i<pixelCount; i++) {
                  strip.SetPixelColor(i, RgbColor(0x00, 0x00,0x00));
                }
                strip.SetPixelColor(PixelNumber, RgbColor(0xff, 0xff,0xff));
                strip.Show();
            }

            break;
    }

}

void setup() {
    Serial.begin(115200);
    Serial.println();
    Serial.println();
    Serial.println("Starting...........");

    for(uint8_t t = 4; t > 0; t--) {
        Serial.printf("[SETUP] BOOT WAIT %d...\n", t);
        Serial.flush();
        delay(1000);
    }

  SPIFFS.begin();
  {
    Dir dir = SPIFFS.openDir("/");
    while (dir.next()) {    
      String fileName = dir.fileName();
      size_t fileSize = dir.fileSize();
      Serial.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
    }
    Serial.printf("\n");
  }

  WiFiMulti.addAP(ssid, password);
  while(WiFiMulti.run() != WL_CONNECTED) {
    delay(100);
  }

  Serial.println("");
  Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP());

  // start webSocket server
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);

  // Set up mDNS responder:
  if (!MDNS.begin(mDNSid)) {
    Serial.println("Error setting up MDNS responder!");
    while(1) { 
      delay(1000);
    }
  }
    Serial.println("mDNS responder started");

  Serial.print("Open http://");
  Serial.print(mDNSid);
  Serial.println(".local/edit to see the file browser");

  //SERVER INIT
  //list directory
  server.on("/list", HTTP_GET, handleFileList);
  //load editor
  server.on("/edit", HTTP_GET, [](){
    if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound");
  });
  //create file
  server.on("/edit", HTTP_PUT, handleFileCreate);
  //delete file
  server.on("/edit", HTTP_DELETE, handleFileDelete);
  //first callback is called after the request has ended with all parsed arguments
  //second callback handles file uploads at that location
  server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload);

  //called when the url is not defined here
  //use it to load content from SPIFFS
  server.onNotFound([](){
    if(!handleFileRead(server.uri()))
      server.send(404, "text/plain", "FileNotFound");
  });

  server.begin();
  Serial.println("HTTP server started");

  // Add service to MDNS
  MDNS.addService("http", "tcp", 80);
  MDNS.addService("ws", "tcp", 81);

  // Initialize NeoPixel Strip
  strip.Begin();
  strip.Show();
}

void loop() {
    webSocket.loop();
    server.handleClient();
}

`

error 

`
ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1264, room 16 
tail 0
chksum 0x42
csum 0x42
~ld