marcoschwartz / aREST

A RESTful environment for Arduino
http://aREST.io/
Other
1.2k stars 279 forks source link

ESP32 hangs after aREST call #257

Closed edalongeville closed 5 years ago

edalongeville commented 5 years ago

Hello,

I'm currently facing an issue where aREST will hang the ESP32 after answering one single http call. Everything else works fine, but as soon as I open my browser on the device homepage, the code hangs (after returning the expected result).

I set aREST in debug mode and ran the program again, here is the debug message I get:

`Memory loss before available:4294740928 Memory loss before handling:0 GET / HTTP/ Memory loss:208 Sending command Command: r State: x State of buffer at the start: Memory loss:0 Added to buffer as progmem: HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS Content-Type: application/json Connection: close

Memory loss:0 Added to buffer as progmem: {"variables": { Memory loss:0 Added to buffer as progmem: : Memory loss:0 Added to buffer as char: false Memory loss:0 Added to buffer as progmem: , Memory loss:0 Added to buffer as progmem: : Memory loss:0 Added to buffer as char: false Memory loss:0 Added to buffer as progmem: , Memory loss:0 Added to buffer as progmem: : Memory loss:0 Added to buffer as char: false Memory loss:0 Added to buffer as progmem: , Memory loss:0 Added to buffer as char: mac Memory loss:0 Added to buffer as progmem: : Memory loss:48 Added to buffer as char: 64:f2:18:71:71:3c Memory loss:4294967248 Added to buffer as progmem: }, Memory loss:0 Added to buffer as progmem: "id": Memory loss:0 Added to buffer as char: 1 Memory loss:0 Added to buffer as progmem: , "name": Memory loss:0 Added to buffer as char: v1 Memory loss:0 Added to buffer as progmem: , "hardware": Memory loss:0 Added to buffer as char: esp32 Memory loss:0 Added to buffer as progmem: , "connected": true} Memory loss:0 Added to buffer as progmem:

Memory loss:0 State of buffer at the end: HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, PUT, OPTIONS Content-Type: application/json Connection: close

{"variables": {"mac": "64:f2:18:71:71:3c"}, "id": "1", "name": "v1", "hardware": "esp32", "connected": true}

Memory loss after handling:0 Memory loss before sending:0 Buffer size: 336 Memory loss after sending:2120 Buffer size: 336 Memory loss after buffer reset:4294966760 Buffer size: 336 Memory loss before reset:4294965556 Memory loss after reset:0 Memory free:227912`

Surprisingly, this happens only when using my desktop web browser. API calls within an android app don't hang the ESP32.

Is there any workaround for this issue please?

edalongeville commented 5 years ago

I know this updates comes shortly after posting the issue, but after adding even more debug lines to the aREST library, I'm no longer sure aREST is the issue.

Here is a sample of the loop():

// Handle REST calls WiFiClient client = server.available(); if (!client) { return; } while(!client.available()){ delay(1); } write_to_logs("Handling aREST call."); rest.handle(client);

The ESP32 actually gets stuck in the while(!client.available()) loop, so this is not caused by aREST. I'll close this issue and hope somebody facing the same problem will one day stumble upon this.

I'll work tomorrow on a workaround, probably by restarting the server after x executions of the faulty loop.

edalongeville commented 5 years ago

So I finally found a solution.

This is not directly caused by aREST, but by the way the WiFi.h library is used in the provided examples. Here is what we do in the examples:

void loop() {

  // Handle REST calls
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
  while(!client.available()){
    delay(1);
  }
  rest.handle(client);
}

The problem is simple: If the client hangs for any reason (multiple connection at once for instance), we remain in the While loop forever. What I did is add an exit condition after a certain number of occurrences of this loop. This is probably not the sexiest way to do this, but it does the trick.

First, define 2 global int variables:

int serverhanging = 0; // Used to monitor how long the client is hanging

int serverhangingrestartdelay = 1000; // delay after which we discard a hanging client

Next, update the above code to look like this:

WiFiClient client = server.available();

  if (!client) {

    serverhanging = 0; // client not hanging, reset timer

    return;

  }

  while(!client.available()){

    write_to_logs("Client unavailable: " + String(serverhanging) + "/" + String(serverhangingrestartdelay));

    serverhanging += 1;

    if(serverhanging >= serverhangingrestartdelay){ // if hanging for too long, discard the client

      write_to_logs("Client hanging for too long. Discarding it.");

      client.stop();

      client.flush();

      delay(500);

      serverhanging = 0;

      return;

    }

    delay(1);

  }

What happens now: If the client hangs for 1 second, we discard it (stop and flush), and return to our main loop. So far I've observed a recovery rate of 100%. The 1sec delay needs to be adjusted to your specific application. The delay(500) after discarding may also be unnecessary.

I really hope this helps someone one day, because it was a pain. I do not know if the issue is specific to the ESP32, or if all Arduino compatible boards are subject to this behavior.