me-no-dev / ESPAsyncWebServer

Async Web Server for ESP8266 and ESP32
3.8k stars 1.23k forks source link

Webserver only sends parts of html code #1087

Closed Linzm99 closed 2 years ago

Linzm99 commented 3 years ago

Hi, I am using an esp8266 with asyncwebserver as an iot device. Additionally I am using OTA flashing, a NTP client to get time information and an http client to get weather information. After booting everything works fine but after some time when loading the page from the webserver(for example with chrome on my pc) the esp just returns parts of the original html code that should be returned. To be exact only the first 30 to 40 lines and some additional strange ascii symbols. Sometimes it doesnt load at all. I also have issues pinging the esp but only for a short time. Then the ping works again, but not the webserver. The main loop code of the esp keeps running all the time.

This seems like a memory Problem, but I dont know where to start. Would storing the html code in SPIFFS possibly solve this problem?

Here is my code:

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
#include <NTPClient.h>
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <WiFiUdp.h>
#include <WiFiClient.h>

const char* host = "aquarium";
const char* ssid = "xxx";
const char* password = "xxx";

uint8_t led1_val = 100;
uint8_t led2_val = 200;
uint8_t led3_val = 10;
bool autopilot = true;

uint8_t lastminute = 0;
uint16_t Offset = 7200;
uint16_t Jahr = 0;
uint8_t Tag = 0;
uint8_t Monat = 0;
time_t rawtime;

uint8_t Minute = 0;
uint8_t Stunde = 0;

uint16_t secs_L1 = 0;
uint16_t secs_L1_start = 0;
uint8_t L1_offset = 0;
bool L1_rising = true;

uint16_t secs_L3 = 0;
uint16_t secs_L3_start = 0;
bool L3_rising = true;

uint16_t Timer = 0;

WiFiClient wificlient;
AsyncWebServer server(80);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", Offset);

String url = "http://api.openweathermap.org/data/2.5/weather?q=73650,DE&cnt=1&appid=55b2a8c3e21a3b4f50ee89c8c1770134";

const char* PARAM_INPUT = "value";

String header;

StaticJsonBuffer<200> jsonBuffer;

void getFullFormattedTime(void){
   yield();

   timeClient.setTimeOffset(Offset);

   rawtime = timeClient.getEpochTime();

   struct tm * ti;
   ti = localtime (&rawtime);

   Jahr = ti->tm_year + 1900;

   Monat = ti->tm_mon + 1;

   Tag = ti->tm_mday;

   Stunde = ti->tm_hour;

   Minute = ti->tm_min;

   return;
}

void getWeather() {
      HTTPClient http;
      http.begin(wificlient,url);
      int httpCode = http.GET();
      if(httpCode > 0) {
          if(httpCode == HTTP_CODE_OK) {
            String response = http.getString();
            for(int i = 0; i < 460; i++){
              if(response[i] == 't' && response[i+1] == 'i' && response[i+2] == 'm' && response[i+3] == 'e'){
                Offset = (response[i+10]-48)*1000 + (response[i+11]-48)*100;
                break;
              }
            }

          }
      }
      http.end();

} 

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
    <head>
        <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">
        <meta charset="utf-8">
        <title>Aquarium</title>
    </head>

    <style>
        @import url('https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap');
        *{
            margin: 0;
            padding: 0;
            font-family: 'Poppins', sans-serif;
        }

        html,body{
            height: 100%;
            width: 100%;
            text-align: center;
            place-items: center;
            background: #111;
        }
        .range{
            margin-left: auto;
            margin-right: auto;
            height: 80px;
            width: 230px;
            background: #333;
            border-radius: 10px;
            padding: 0 65px 0 45px;
            box-shadow: 2px 4px 8px rgba(0,0,0,0.1);
        }
        .sliderValue{
            position: relative;
            width: 100%;
        }
        .sliderValue span{
            position: absolute;
            height: 45px;
            width: 45px;
            transform: translateX(-70%) scale(0);
            font-weight: 500;
            top: -40px;
            line-height: 55px;
            z-index: 2;
            color: #333;
            transform-origin: bottom;
            transition: transform 0.3s ease-in-out;
        }
        .sliderValue span.show{
            transform: translateX(-70%) scale(1);
        }
        .sliderValue span:after{
            position: absolute;
            content: '';
            height: 100%;
            width: 100%;
            background: #ffe600;
            border: 3px solid #fff;
            z-index: -1;
            left: 50%;
            transform: translateX(-50%) rotate(45deg);
            border-bottom-left-radius: 50%;
            box-shadow: 0px 0px 8px rgba(0,0,0,0.1);
            border-top-left-radius: 50%;
            border-top-right-radius: 50%;
        }
        .field{
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100%;
            position: relative;
        }
        .field .value{
            position: absolute;
            font-size: 18px;
            color: #ffe600;
            font-weight: 600;
        }
        .field .value.left{
            left: -22px;
        }
        .field .value.right{
            right: -43px;
        }
        .range input{
            -webkit-appearance: none;
            width: 100%;
            height: 3px;
            background: #ddd;
            border-radius: 5px;
            outline: none;
            border: none;
            z-index: 2222;
        }
        .range input:disabled{
            background: #444;
        }
        .range input::-webkit-slider-thumb{
            -webkit-appearance: none;
            width: 20px;
            height: 20px;
            background: red;
            border-radius: 50%;
            background: #ffe600;
            border: 1px solid #ffe600;
            cursor: pointer;
        }
        .range input:disabled::-webkit-slider-thumb{
            background: #8b7e00;
            border: 1px solid #8b7e00;
        }
        .range input::-moz-range-thumb{
            -webkit-appearance: none;
            width: 20px;
            height: 20px;
            background: red;
            border-radius: 50%;
            background: #ffe600;
            border: 1px solid #ffe600;
            cursor: pointer;
        }
        .range input:disabled::-moz-range-thumb{
            background: #8b7e00;
            border: 1px solid #8b7e00;
        }
        .range input::-moz-range-progress{
            background: #ffe600; 
        }
        .range input:disabled::-moz-range-progress{
            background: #8b7e00;
            border: 1px solid #8b7e00;
        }
        .valuename{
            color: #ffe600;
            padding: 10px;
            padding-top: 20px;
            font-size: 28px;

        }
        .AutoLED{
            padding-top: 35px;
        }
        .AutoLED p{
            display: inline-block;
            color: #ffe600;
            font-size: 28px;
            transform: translateY(-10px);

        }
        .switch{
            position: relative;
            display: inline-block;
            width: 80px;
            height: 40px;
            margin: 0 10px;
        }
        .sw_slider{
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #333;
            transition: .4s;
            border-radius: 20px;
        }
        .switch input{display:none}
        .sw_slider:before{
            position: absolute;
            content: "";
            height: 34px;
            width: 34px;
            left: 3px;
            bottom: 3px;
            background-color: #ffe600;
            transition: .4s;
            border-radius: 36px;
        }
        input:checked + .sw_slider{
            background-color: #888;
        }
        input:checked + .sw_slider:before{
            transform: translateX(40px);
        }
        .aqua{
            padding-top: 20px;
            color: #ffffff;
            font-size: 44px;

        }
    </style>

    <body>
        <div class="aqua">
            <u>AQUARIUM</u>
        </div>
        <div class="valuename">
            Schwarzlicht
        </div>
        <div class="range">

            <div class="sliderValue">
              <span id="span1">100</span>
            </div>
            <div class="field">
                <div class="value left">0</div>
                <input id="input1" onchange="updateSliderPWM(this)" type="range" min="0" max="255" value="100" steps="1">
                <div class="value right">255</div>
            </div>
        </div>
        <div class="valuename">
            Photolicht:
        </div>
        <div class="range">

            <div class="sliderValue">
              <span id="span2">100</span>
            </div>
            <div class="field">
                <div class="value left">0</div>
                <input id="input2" onchange="updateSliderPWM(this)" type="range" min="0" max="255" value="100" steps="1">
                <div class="value right">255</div>
            </div>
        </div>
        <div class="valuename">
            Tageslicht:
        </div>
        <div class="range">   
            <div class="sliderValue">
              <span id="span3">100</span>
            </div>
            <div class="field">
                <div class="value left">0</div>
                <input id="input3" onchange="updateSliderPWM(this)" type="range" min="0" max="255" value="100" steps="1">
                <div class="value right">255</div>
            </div>
        </div>
        <div class="AutoLED">
            <p>Zeitgesteuert</p>
            <label class="switch">
                <input type="checkbox" id="inputSW" onchange="updateSwitch(this)">
                <span class="sw_slider"></span>
            </label>

        </div>
        <script>
            const slideValue1 = document.querySelector("span[id='span1'");
            const inputSlider1 = document.querySelector("input[id='input1']");
            const slideValue2 = document.querySelector("span[id='span2'");
            const inputSlider2 = document.querySelector("input[id='input2']");
            const slideValue3 = document.querySelector("span[id='span3'");
            const inputSlider3 = document.querySelector("input[id='input3']");
            const inputSwitch = document.querySelector("input[id='inputSW']");
            inputSlider1.oninput = (()=>{
                let value = inputSlider1.value;
                let x_pos = inputSlider1.offsetLeft;
                slideValue1.textContent = value;
                slideValue1.style.left = ((value/2.75)+7) + "%";
                slideValue1.classList.add("show");
            });
            inputSlider1.onblur = (()=>{
                slideValue1.classList.remove("show");
            });
            inputSlider2.oninput = (()=>{
                let value = inputSlider2.value;
                let x_pos = inputSlider2.offsetLeft;
                slideValue2.textContent = value;
                slideValue2.style.left = ((value/2.75)+7) + "%";
                slideValue2.classList.add("show");
            });
            inputSlider2.onblur = (()=>{
                slideValue2.classList.remove("show");
            });
            inputSlider3.oninput = (()=>{
                let value = inputSlider3.value;
                let x_pos = inputSlider3.offsetLeft;
                slideValue3.textContent = value;
                slideValue3.style.left = ((value/2.75)+7) + "%";
                slideValue3.classList.add("show");
            });
            inputSlider3.onblur = (()=>{
                slideValue3.classList.remove("show");
            });
            function updateSliderPWM(element) {
                var xhr = new XMLHttpRequest();
                xhr.open("GET", "/slider?value="+ inputSlider1.value + ";" + inputSlider2.value + ";" + inputSlider3.value , true);
                xhr.send();
            }
            function updateSwitch(element) {
                var xhr = new XMLHttpRequest();
                xhr.open("GET", "/auto?value="+ inputSwitch.checked, true);
                inputSlider1.disabled = inputSwitch.checked;
                inputSlider2.disabled = inputSwitch.checked;
                inputSlider3.disabled = inputSwitch.checked;
                xhr.send();
            }
            function led1( ) {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        inputSlider1.value = this.responseText;
                    }
                };
                xhttp.open("GET", "/led1", true);
                xhttp.send();
            }
            function led2( ) {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        inputSlider2.value = this.responseText;
                    }
                };
                xhttp.open("GET", "/led2", true);
                xhttp.send();
            };
            function led3( ) {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        inputSlider3.value = this.responseText;
                    }
                };
                xhttp.open("GET", "/led3", true);
                xhttp.send();
            }
            function updateSW( ) {
                var xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        if(this.responseText == "True"){
                            inputSwitch.checked = true;
                            inputSlider1.disabled = true;
                            inputSlider2.disabled = true;
                            inputSlider3.disabled = true;
                        }
                        else{
                            inputSwitch.checked = false;
                            inputSlider1.disabled = false;
                            inputSlider2.disabled = false;
                            inputSlider3.disabled = false;
                        }
                    }
                };
                xhttp.open("GET", "/switch", true);
                xhttp.send();
            }
            setInterval(led1(), 10000);
            setInterval(led2(), 10000);
            setInterval(led3(), 10000);
            led1();
            led2();
            led3();
            updateSW();
        </script>
    </body>
</html>)rawliteral";

String processor(const String& var){
  if (var == "SLIDERNUMBER1"){
    return String(led1_val);
  }
  else if (var == "SLIDERNUMBER2"){
    return String(led2_val);
  }
  else if (var == "SLIDERNUMBER3"){
    return String(led3_val);
  }
  return var;
}

void setup(void) {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  WiFi.mode(WIFI_STA);
  WiFi.setSleepMode(WIFI_NONE_SLEEP);
  WiFi.begin(ssid, password);
  digitalWrite(2, HIGH);
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  timeClient.begin();

  timeClient.update();
  getWeather();
  getFullFormattedTime();

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/html", index_html);
  });

  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    if (request->hasParam(PARAM_INPUT)) {
      inputMessage = request->getParam(PARAM_INPUT)->value();
    }
    else {
      inputMessage = "No message sent";
    }
    char inputBuffer[12] = {0};
    inputMessage.toCharArray(inputBuffer,12);
    String str1 = strtok(inputBuffer,";");
    String str2 = strtok(NULL,";");
    String str3 = strtok(NULL,";");
    led1_val = (str1).toInt();
    led2_val = (str2).toInt();
    led3_val = (str3).toInt();
    request->send(200, "text/plain", "OK");
    String outputString = String(255-led1_val) + ";" + String(255-led2_val) + ";" + String(255-led3_val) + ";0;0;" + String(led1_val);
    Serial.println(outputString);

  });
  server.on("/switch", HTTP_GET, [](AsyncWebServerRequest *request) {
    if(autopilot){
      request->send(200, "text/plain", "True");
    }
    else{
      request->send(200, "text/plain", "False");
    }

  });
  server.on("/auto", HTTP_GET, [](AsyncWebServerRequest *request) {
    String inputMessage;
    if (request->hasParam(PARAM_INPUT)) {
      inputMessage = request->getParam(PARAM_INPUT)->value();
    }
    else {
      inputMessage = "No message sent";
    }
    if(inputMessage == "true"){
      autopilot = true;
    }
    else{
      autopilot = false;
    }
    request->send(200, "text/plain", "OK");
  });
  server.on("/led1", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(led1_val));
  });
  server.on("/led2", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(led2_val));
  });
  server.on("/led3", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(led3_val));
  });

  AsyncElegantOTA.begin(&server);    // Start ElegantOTA
  server.begin();
}

void loop() {
  if(autopilot == true){
     if(secs_L1 != 0){
        secs_L1++;
        if(L1_rising){
          led1_val = (uint8_t)(255.0 * sin(PI * (secs_L1-secs_L1_start)/3600.0) + L1_offset);
        } 
        else{
          led1_val = (uint8_t)(255.0 * cos(PI * (secs_L1-secs_L1_start)/3600.0) + L1_offset);
        }
     }
     else{
        secs_L1_start = rawtime;
     }
     if(secs_L3 != 0){
        secs_L3++;
        if(L3_rising){
          led3_val = (uint8_t)(255.0 * sin(PI * (secs_L3-secs_L3_start)/10800.0));
          led2_val = led3_val;
        } 
        else{
          led3_val = (uint8_t)(255.0 * cos(PI * (secs_L3-secs_L3_start)/3600.0));
          led2_val = led3_val;
        }
     }
     else{
        secs_L3_start = rawtime;
     }
  }
  if(Timer >= 5){
    if(autopilot == true){
      String outputString = String(255-led1_val) + ";" + String(255-led2_val) + ";" + String(255-led3_val) + ";" + String((uint8_t)(led1_val/12)) + ";0;" + String(led1_val);
      Serial.println(outputString);
    }

    timeClient.update();
    getFullFormattedTime();

    Timer = 0;
    if(lastminute != Minute){
      getWeather();

      lastminute = Minute;
      if(autopilot == true){
        if((Stunde*60+Minute) > 480 && (Stunde*60+Minute) < 510){
          secs_L1 = rawtime;
          L1_offset = 0;
          L1_rising = true;
        }
        else if((Stunde*60+Minute) >= 510 && (Stunde*60+Minute) <= 540){
          secs_L1 = 0;
          led1_val = 255;
        }
        else if((Stunde*60+Minute) > 540 && (Stunde*60+Minute) <= 555){
          secs_L1 = rawtime;
          L1_rising = false;
          L1_offset = 0;
        }
        else if((Stunde*60+Minute) >= 555 && (Stunde*60+Minute) <= 1095){
          secs_L1 = 0;
          led1_val = 127;
        }
        else if((Stunde*60+Minute) > 1095 && (Stunde*60+Minute) < 1110){
          secs_L1 = rawtime;
          L1_rising = true;
          L1_offset = 127;
        }
        else if((Stunde*60+Minute) >= 1110 && (Stunde*60+Minute) <= 1290){
          secs_L1 = 0;
          led1_val = 255;
        }
        else if((Stunde*60+Minute) > 1290 && (Stunde*60+Minute) < 1320){
          secs_L1 = rawtime;
          L1_rising = false;
          L1_offset = 0;
        }
        else{
          secs_L1 = 0;
          led1_val = 0;
        }

        if((Stunde*60+Minute) > 540 && (Stunde*60+Minute) < 630){
          secs_L3 = rawtime;
          L3_rising = true;
        }
        else if((Stunde*60+Minute) >= 630 && (Stunde*60+Minute) <= 1110){
          secs_L3 = 0;
          led3_val = 255;
          led2_val = 255;
        }
        else if((Stunde*60+Minute) > 1110 && (Stunde*60+Minute) < 1140){
          secs_L3 = rawtime;
          L3_rising = false;
        }
        else{
          secs_L3 = 0;
          led3_val = 0;
          led2_val = 0;
        }
        //Reset
        if((Stunde*60+Minute) == 100 || (Stunde*60+Minute) == 645 || (Stunde*60+Minute) == 850 || (Stunde*60+Minute) == 1050){
          server.end();
          delay(10000);
          server.begin();
        }

      }

    }   
  }

  yield();
  delay(1000);
  Timer++;
}
alixti commented 2 years ago

Once this happened to me because the ESP didn't have enough Wi-Fi signal. When I moved it closer to the AP it worked fine.

stale[bot] commented 2 years ago

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

stale[bot] commented 2 years ago

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

JohnvanderPol2 commented 2 years ago

I had the same problem, But the problem seems to be in OTA (AsyncElegantOTA)

I have code that is like void TConfigurationWebHandler::RequestHandler(AsyncWebServerRequest* request) { DBG_OUTPUT_PORT.println(F("TConfigurationWebHandler::RequestHandler")); if(request->method()==HTTP_POST) { } else { TConfigurationWebHandler handler; String json=handler.getConfigurationData();//So the content is not released to early DBG_OUTPUT_PORT.println(F("before send")); DBG_OUTPUT_PORT.println(json); request->send(200, "text/json", json); DBG_OUTPUT_PORT.println(F("after send")); DBG_OUTPUT_PORT.println(json); } }

When OTA was enabled, after the send, the content of the json would be cleared. This while the string is passed as const String &

I am still investigating why OTA causes this behavior.