Links2004 / arduinoWebSockets

arduinoWebSockets
GNU Lesser General Public License v2.1
1.89k stars 556 forks source link

sending html to clients of websocketserver #499

Open Aldrin-John-Olaer-Manalansan opened 4 years ago

Aldrin-John-Olaer-Manalansan commented 4 years ago

I am trying to send an html webpage to the clients of websocketserver, is this possible?

Links2004 commented 4 years ago

yes, you can send any data you like over the websocket this includes binary data. if this is a widely adopted practice is another question ;)

but you will still need to load a basic html / JS page that establish the websocket connection first. a typical webbrowser is not able to load a html page directly from a websocket, you will need custom Javascript code to do that.

Aldrin-John-Olaer-Manalansan commented 4 years ago

yes, you can send any data you like over the websocket this includes binary data. if this is a widely adopted practice is another question ;)

but you will still need to load a basic html / JS page that establish the websocket connection first. a typical webbrowser is not able to load a html page directly from a websocket, you will need custom Javascript code to do that.

Then Why is this not working???

#include <WiFi.h>
#include <WebSocketsServer.h>

#include <NTPClient.h>
#include <WiFiUdp.h>

#include <esp_camera.h>
#include "camera_pins.h"

#define WiFi_TryConnect_TimeOut 20000.0 // ilang  milliseconds maghihintay ang esp32 sa pag connect
#define builtin_serialkey "Qa!e6" // eto yung UNIQUE Serial Key ng Device nato
#define builtin_AP_SSID "IP Camera: Qa!e6" // eto yung SSID ng WiFi na gagawin ng device nato
#define builtin_AP_Password "Ba0!*sL8" // eto yung password ng WiFi na gagawin ng device nato

#define Port_WebSocket 80

#define standby_timer 2000

//#define tunnel_webserver "ws://0.tcp.jp.ngrok.io:13864"

#define flashlight 4
#define clientindicator_LED 12
#define lightdependentresistor 13
#define LDR_Sensitivity 2048

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 28800, 3600e3);

IPAddress staticip;
word customtimer = 0;
unsigned long timesincelastupdate = 0;

String ssid = "Manalansan";
String password = "Manalansan@123!";

/*
        <style> *{ margin:0; } img { display: absolute; margin: 0 !important; height: 100vh;  width: 100vw; } </style>

  "        <style>\n"
  "          img\n"
  "          {\n"
  "            display: block;\n"
  "            margin-left: auto;\n"
  "            margin-right: auto;\n"
  "            height: 100%;\n"
  "            width: auto;\n"
  "          }\n"
  "        </style>\n"
*/

char camerawebpage[] PROGMEM = R"=====(
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1370
Connection: closed

<html>
    <head>
        <title>Thesis Camera Test</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style> *{ margin:0; } img { display: absolute; margin: 0 !important; height: 100vh;  width: 100vw; } </style>
    <!--
        <style>
    img
          {
            display: block;
            margin-left: auto;
            margin-right: auto;
            height: 100%;
            width: auto;
          }
        </style>
    -->
    </head>
    <body style="background-color:#FFFFFF">
        <img src="">
        <script>
            const img = document.querySelector('img');
            const WS_URL = (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.hostname + '/ws';
            const ws = new WebSocket(WS_URL);
            ws.onerror = function() {
               location.reload(true);
            }
            let urlObject;
            ws.onmessage = message => {
                const arrayBuffer = message.data;
                if(urlObject){
                    URL.revokeObjectURL(urlObject);
                }
                urlObject = URL.createObjectURL(new Blob([arrayBuffer]));
                delete arrayBuffer;
                delete message;
                img.src = urlObject;
            }
        </script>
    </body>
</html>
)=====";

//initialize websocket
WebSocketsServer webSocket = WebSocketsServer(Port_WebSocket,"/ws");

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length)
{
    Serial.println("event happened");
    webSocket.sendTXT(num,camerawebpage);
}

void setup()
{
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Serial.println();

connecttowifi:
  WiFi.begin(ssid.c_str(), password.c_str());
  customtimer = millis();
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
    if ( (WiFi.status() == WL_CONNECT_FAILED) || (millis() - customtimer > WiFi_TryConnect_TimeOut) )
    {
      WiFi.softAPConfig(IPAddress(192, 168, 1, 184), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0)); // set ip infos
      if (WiFi.softAP(builtin_AP_SSID, builtin_AP_Password) == true); // create our own wifi
      {
        if (WiFi.softAPIP() != IPAddress(192, 168, 1, 184))
          WiFi.softAPConfig(IPAddress(192, 168, 1, 184), IPAddress(192, 168, 1, 1), IPAddress(255, 255, 255, 0)); // set ip infos
        // WiFi.softAP(ssid, password);
        Serial.println(String("Access Point created!"));
        Serial.println(String("SSID:") + builtin_AP_SSID);
        Serial.println(String("Password:") + builtin_AP_Password);
        break;
      }
    }
    if (Serial.available() > 0)
    {
      String dynamicstring = Serial.readString();
      Serial.print(dynamicstring);
      if (dynamicstring.indexOf("WiFi Setup") >= 0)
      {
        Serial.println("Type the SSID");
        while (Serial.available() <= 0) { }
        ssid = Serial.readString();
        if (ssid.endsWith("\r\n"))
          ssid.remove(ssid.length() - 2, 2);
        else if (ssid.endsWith("\r") || ssid.endsWith("\n"))
          ssid.remove(ssid.length() - 1, 1);

        Serial.println(String("SSID changed into ") + ssid);

        Serial.println("Type the Password");
        while (Serial.available() <= 0) { }
        password = Serial.readString();
        if (password.endsWith("\r\n"))
          password.remove(password.length() - 2, 2);
        else if (password.endsWith("\r") || password.endsWith("\n"))
          password.remove(password.length() - 1, 1);

        Serial.println(String("Password changed into ") + password);

        goto connecttowifi;
      }
    }
  }
  if (WiFi.status() == WL_CONNECTED )// kapag nakaconnect tayo sa ibang wifi, kelangan nakaconfigure ang static IP address
  {
    Serial.print(String("\nWiFi connected using:\nSSID:") + ssid + String("\nPassword:") + password + String("\n"));
    staticip = WiFi.localIP(); // get the current ip of this device assigned by the router
    staticip[3] = 184; // set the fourth octet to 184
    if (!WiFi.config(staticip, WiFi.gatewayIP(), WiFi.subnetMask(), IPAddress(8, 8, 8, 8), IPAddress(1, 1, 1, 1))) { // set the static ip address
      Serial.println("STA Failed to configure");
    }
  }
  else
    staticip = WiFi.softAPIP(); // get the current ip of this device assigned by the router

  timeClient.begin(); // Initialize a NTPClient to get time
  timeClient.update();
  Serial.println(timeClient.getHours());
  Serial.println(timeClient.getFormattedTime());

  webSocket.begin();
  webSocket.onEvent(webSocketEvent);

  Serial.println("Camera Ready! type this URL to access the Video Stream:");
  Serial.print(staticip);
  Serial.println(String(":") + Port_WebSocket);

  pinMode(clientindicator_LED, OUTPUT);
  pinMode(flashlight, OUTPUT);
  //pinMode(lightdependentresistor, INPUT_PULLUP); // set resolution of input from 0-4095

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;

  //config.xclk_freq_hz = 20000000;
  config.xclk_freq_hz = 10000000; // accourding sa research ko ito 10MHz ang nagbibigay ng best FPS

  config.pixel_format = PIXFORMAT_JPEG;
  //init with high specs to pre-allocate larger buffers
  if (psramFound()) {

    Serial.println("PSRAM was FOUND!!!");
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 10;

    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();

  s->set_framesize(s, FRAMESIZE_VGA); //drop down frame size for higher initial frame rate
  ////s->set_brightness(s,0);
  ////s->set_contrast(s,0);
  ////s->set_saturation(s, 2);
  ////s->set_special_effect(s,0);
  //s->set_whitebal(s, 1);
  //s->set_awb_gain(s, 1);
  ////s->set_wb_mode(s,0);
  //s->set_exposure_ctrl(s, 1);
  //s->set_aec2(s, 1);
  ////s->set_ae_level(s,0);
  //s->set_aec_value(s, 204);
  //s->set_gain_ctrl(s, 1);
  //s->set_agc_gain(s, 5);
  ////s->set_gainceiling(s,0);
  ////s->set_bpc(s,0);
  //s->set_wpc(s, 1);
  //s->set_raw_gma(s, 1);
  //s->set_lenc(s, 1);
  ////s->set_hmirror(s,0);
  ////s->set_vflip(s,0);
  //s->set_dcw(s, 1);
  ////s->set_colorbar(s,0); // recommend not to on, only turn this on when callibrating colors
  ////s->detection_enabled=0;
  ////s->recognition_enabled=0;

  Serial.println(String("framesize:") + String(s->status.framesize));
  Serial.println(String("quality:") + String(s->status.quality));
  Serial.println(String("brightness:") + String(s->status.brightness));
  Serial.println(String("contrast:") + String(s->status.contrast));
  Serial.println(String("saturation:") + String(s->status.saturation));
  Serial.println(String("sharpness:") + String(s->status.sharpness));
  Serial.println(String("special_effect:") + String(s->status.special_effect));
  Serial.println(String("wb_mode:") + String(s->status.wb_mode));
  Serial.println(String("awb:") + String(s->status.awb));
  Serial.println(String("awb_gain:") + String(s->status.awb_gain));
  Serial.println(String("aec:") + String(s->status.aec));
  Serial.println(String("aec2:") + String(s->status.aec2));
  Serial.println(String("ae_level:") + String(s->status.ae_level));
  Serial.println(String("aec_value:") + String(s->status.aec_value));
  Serial.println(String("agc:") + String(s->status.agc));
  Serial.println(String("agc_gain:") + String(s->status.agc_gain));
  Serial.println(String("gainceiling:") + String(s->status.gainceiling));
  Serial.println(String("bpc:") + String(s->status.bpc));
  Serial.println(String("wpc:") + String(s->status.wpc));
  Serial.println(String("raw_gma:") + String(s->status.raw_gma));
  Serial.println(String("lenc:") + String(s->status.lenc));
  Serial.println(String("vflip:") + String(s->status.vflip));
  Serial.println(String("hmirror:") + String(s->status.hmirror));
  Serial.println(String("dcw:") + String(s->status.dcw));
  Serial.println(String("colorbar:") + String(s->status.colorbar));

  //initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);//flip it back
    s->set_brightness(s, 1);//up the blightness just a bit
    s->set_saturation(s, -2);//lower the saturation
  }

  Serial.println(String("public ip:") + getIp());
}

void loop()
{
  webSocket.loop();

  if (webSocket.connectedClients(false) <= 0)
  {
    gostandbymode();
    return;
  }

  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb)
  {
    Serial.println("Camera capture failed");
    esp_camera_fb_return(fb);
    gostandbymode();
    return;
  }

  if (fb->format != PIXFORMAT_JPEG)
  {
    Serial.println("Non-JPEG data not implemented");
    gostandbymode();
    return;
  }

  webSocket.broadcastBIN((const uint8_t*) fb->buf, fb->len);
  esp_camera_fb_return(fb);

  if ((webSocket.connectedClients(false) > 0 || digitalRead(clientindicator_LED) == HIGH) && millis() - customtimer > 1000 / webSocket.connectedClients(false))
  {
    Serial.println(String("Connected Clients:") + String(webSocket.connectedClients(false)));

    if (millis() - timesincelastupdate >= 3600e3)
    {
      timeClient.update();
      timesincelastupdate = millis();
      Serial.println("Time was Updated!");
    }
    byte currenthour = timeClient.getHours();
    if ((currenthour < 22 && currenthour >= 6) && digitalRead(flashlight) == HIGH)
      digitalWrite(flashlight, LOW);
    else if ((currenthour >= 22 || currenthour < 6) && digitalRead(flashlight) == LOW)
      digitalWrite(flashlight, HIGH);

    if (digitalRead(clientindicator_LED) != HIGH)
    {
      digitalWrite(clientindicator_LED, HIGH);
      Serial.println("HIGH");
    }
    else
    {
      digitalWrite(clientindicator_LED, LOW);
      Serial.println("LOW");
    }
    customtimer = millis();
  }
  //webSocket.disconnect(); // disconnectclient
}

void gostandbymode()
{
  if (digitalRead(flashlight) == HIGH)
    digitalWrite(flashlight, LOW);
  if (digitalRead(clientindicator_LED) == HIGH)
    digitalWrite(clientindicator_LED, LOW);
  //delay(standby_timer);
}

String getIp()
{
  WiFiClient client;
  if (client.connect("api.ipify.org", 80))
  {
    client.println("GET / HTTP/1.0");
    client.println("Host: api.ipify.org");
    client.println();
  }
  else return String();

  while (!client.available()) {
    delay(500);
  }

  String line;

  while (client.available())
    line = client.readStringUntil('\n');

  return line;
}
Links2004 commented 4 years ago

you code does not include and initial html page delivery (a normal HTTP web server) like I stated above there is no browser that can directly pull a Wegpage via websocket.

you can use: https://github.com/Links2004/arduinoWebSockets/blob/master/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino as a example the includes a webserver for the initial html page deliver, with the send HTML / JS code a websocekt connection is opened.

Aldrin-John-Olaer-Manalansan commented 4 years ago

you code does not include and initial html page delivery (a normal HTTP web server) like I stated above there is no browser that can directly pull a Wegpage via websocket.

you can use: https://github.com/Links2004/arduinoWebSockets/blob/master/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino as a example the includes a webserver for the initial html page deliver, with the send HTML / JS code a websocekt connection is opened.

OK, so it worked when there is a webserver and websocket on different ports (80 and 81). But is there any way to ONLY USE 1 port? eg. only port 81.

Links2004 commented 4 years ago

if you are extending the WebSocketsServer class and then override the handleNonWebsocketConnection function it is possible to deliver a html page via the websocket lib.

https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L135

Aldrin-John-Olaer-Manalansan commented 4 years ago

if you are extending the WebSocketsServer class and then override the handleNonWebsocketConnection function it is possible to deliver a html page via the websocket lib.

https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSocketsServer.h#L135

OK, so I just changed that function into like this and it worked, I can see the image stream using the local ip address one one port:

virtual void handleNonWebsocketConnection(WSclient_t * client) {
                client->tcp->write(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: 1370\r\n"
        "Connection: closed\r\n"
        "\r\n"
        "<html>\n"
        "    <head>\n"
        "        <title>Thesis Camera Test</title>\n"
        "        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
        "    <style> *{ margin:0; } img { display: absolute; margin: 0 !important; height: 100vh;  width: 100vw; } </style>\n"
        "    <!--\n"
        "        <style>\n"
        "    img\n"
        "          {\n"
        "            display: block;\n"
        "            margin-left: auto;\n"
        "            margin-right: auto;\n"
        "            height: 100%;\n"
        "            width: auto;\n"
        "          }\n"
        "        </style>\n"
        "    -->\n"
        "    </head>\n"
        "    <body style=\"background-color:#FFFFFF\">\n"
        "        <img src=\"\">\n"
        "        <script>\n"
        "            const img = document.querySelector('img');\n"
        "            const WS_URL = (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.hostname + '/ws';\n"
        "            const ws = new WebSocket(WS_URL);\n"
        "            ws.onerror = function() {\n"
        "               location.reload(true);\n"
        "            }\n"
        "            let urlObject;\n"
        "            ws.onmessage = message => {\n"
        "                const arrayBuffer = message.data;\n"
        "                if(urlObject){\n"
        "                    URL.revokeObjectURL(urlObject);\n"
        "                }\n"
        "                urlObject = URL.createObjectURL(new Blob([arrayBuffer]));\n"
        "                delete arrayBuffer;\n"
        "                delete message;\n"
        "                img.src = urlObject;\n"
        "            }\n"
        "        </script>\n"
        "    </body>\n"
        "</html>");
    }

But "only two clients" can be handled at the same time. The third and above clients are being refused to be processed.

That is bad compared to the webserver+websocket with different ports that can handle almost 8 clients at a time.

Any workaround sir?

Links2004 commented 4 years ago

your replacement function misses the

clientDisconnect(client);

which frees the connections up.

Hint you can save some space be not sending the HTML comment ;)

Aldrin-John-Olaer-Manalansan commented 4 years ago

your replacement function misses the

clientDisconnect(client);

which frees the connections up.

Hint you can save some space be not sending the HTML comment ;)

Hello sorry for late reply, I just got some sleep.

Ok, adding the

clientDisconnect(client);

Does not let the clients load the html when They access the IP using any browser, its like it terminates the connection just before the tcp have the opportunity to send the html to the client. Why does this does not let me load the html?

virtual void handleNonWebsocketConnection(WSclient_t * client) {
                client->tcp->write(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "Content-Length: 1370\r\n"
        "Connection: closed\r\n"
        "\r\n"
        "<html>\n"
        "    <head>\n"
        "        <title>Thesis Camera Test</title>\n"
        "        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
        "    <style> *{ margin:0; } img { display: absolute; margin: 0 !important; height: 100vh;  width: 100vw; } </style>\n"
        "    <!--\n"
        "        <style>\n"
        "    img\n"
        "          {\n"
        "            display: block;\n"
        "            margin-left: auto;\n"
        "            margin-right: auto;\n"
        "            height: 100%;\n"
        "            width: auto;\n"
        "          }\n"
        "        </style>\n"
        "    -->\n"
        "    </head>\n"
        "    <body style=\"background-color:#FFFFFF\">\n"
        "        <img src=\"\">\n"
        "        <script>\n"
        "            const img = document.querySelector('img');\n"
        "            const WS_URL = (location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.hostname + '/ws';\n"
        "            const ws = new WebSocket(WS_URL);\n"
        "            ws.onerror = function() {\n"
        "               location.reload(true);\n"
        "            }\n"
        "            let urlObject;\n"
        "            ws.onmessage = message => {\n"
        "                const arrayBuffer = message.data;\n"
        "                if(urlObject){\n"
        "                    URL.revokeObjectURL(urlObject);\n"
        "                }\n"
        "                urlObject = URL.createObjectURL(new Blob([arrayBuffer]));\n"
        "                delete arrayBuffer;\n"
        "                delete message;\n"
        "                img.src = urlObject;\n"
        "            }\n"
        "        </script>\n"
        "    </body>\n"
        "</html>");
clientDisconnect(client);
    }
Aldrin-John-Olaer-Manalansan commented 4 years ago

Hello, It seems that you have not replied for a while. OK so I just discovered that "TCP Write" is being executed asynchronously:

client->tcp->write(somestring); // will be executed on a separate thread
clientDisconnect(client); // this will execute NOW!

Which leads a chance that TCP Write will finish late just after clientDisconnect() happened... But putting a "delay(2000)" before clientDisconnect fixes the thing:

client->tcp->write(somestring); // write the code asynchronously
delay(2000); // wait for 2 seconds
clientDisconnect(client); // if the tcp write happened before 2 seconds and this client disconnects, the delivery will be successful, else the TCP write will fail...

But that delay is a bad thing actually, because when "TCP Write" fails to write within 2 seconds(eg. slow connection between server and client) then it will immediately fail since "clientDisconnect(client);" will execute after 2 seconds...

What I have comeup for a workaround is by using an "onEvent()" of websockets

here is what I have gone so far:

#include <WebSocketsServer.h>

WebSocketsServer webSocket = WebSocketsServer(Port_WebSocket, "/ws");
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length)
{
  if (type == WStype_CONNECTED)
  {
    Serial.println("A Client Connects");
    websocket.disconnect(num); // <--- I am having a problem here because the compiler says that "this was not declared at this scope"
  }
}

void setup()
{
      webSocket.begin();
      webSocket.onEvent(webSocketEvent);
}

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

the problem is the compiler says inside the event: websocket.disconnect(num); // <--- I am having a problem here because the compiler says that "this was not declared at this scope".

Can you point out the problem?

Links2004 commented 4 years ago

websocket.disconnect(num); need to be webSocket.disconnect(num);

you can try

write(client, "HTTP/1.1 200 OK\r\n .........."); 

most likely your const char is longer then one TCP packet which the normal TCP write does not like. thats why there is a wrapper for it.

https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L652-L707

Aldrin-John-Olaer-Manalansan commented 4 years ago

websocket.disconnect(num); need to be webSocket.disconnect(num);

What is the Diference?

you can try

write(client, "HTTP/1.1 200 OK\r\n .........."); 

most likely your const char is longer then one TCP packet which the normal TCP write does not like. thats why there is a wrapper for it.

https://github.com/Links2004/arduinoWebSockets/blob/master/src/WebSockets.cpp#L652-L707

I tried it and It does the same thing, it writes on the tcp asynchronously, thus also requiring a "delay(2000);" in order to give time to write at the tcp before calling clientdisconnect().

Links2004 commented 4 years ago

ok strange, I never had this problem with the write function, are you running a ESP32 or ESP8266? you can try to use flush on the tcp socket before calling the clientdisconnect.

https://www.arduino.cc/en/Reference/ClientFlush

Aldrin-John-Olaer-Manalansan commented 4 years ago

ok strange, I never had this problem with the write function, are you running a ESP32 or ESP8266?

Yes I am using ESP32 CAM module to stream mjpeg, and I am trying to send a client.html to those who connects which will receive the stream from the websocket.

you can try to use flush on the tcp socket before calling the clientdisconnect.

https://www.arduino.cc/en/Reference/ClientFlush

Am I doing wrong something about this?

    virtual void handleNonWebsocketConnection(WSclient_t * client)
    {
        write(client,(uint8_t *)camwebpage,sizeof(camwebpage));

        //client.flush(); //this produces an error when compiling the code
        //client->flush(); //says there is no member on the struct, errors at compile time
        //flush(); //says there is no function, error at compile time

                client->tcp->flush(); //still does not wait for all buffers to get sent... clientdisconnect immediately executes
        clientDisconnect(client);
    }

client->tcp->flush(); does not do anything, clientDisconnect(client) is immediately called without any delay from the flush() that still leads the tcp write to fail sending the webpage.

How am I suppose to make it work?

Links2004 commented 4 years ago

yes,

client->tcp->flush();

where is the idea, if this is not working then there is a bug in the ESP32 TCP implementation. from reading the ESP32 vs ESP8266 code it looks like the ESP32 code is implementation is wrong.

https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiClient.cpp#L468 vs https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/src/WiFiClient.cpp#L311 and https://github.com/arduino-libraries/Ethernet/blob/master/src/EthernetClient.cpp#L123

since the official Arduino docu states

flush()
Waits until all outgoing characters in buffer have been sent.

flush() inherits from the Stream utility class.

https://www.arduino.cc/en/Reference/ClientFlush and the official EthernetClient class, which is basically the reference implementation is implemented that way.

at that point its up to a ticket in the ESP32 project. feel free to reference this issue.