1. 14
  1. 2

    Is that the right logic, though? If handleClient handles only one request, we would be adding a delay systematically after each request, even in cases where many requests have come at the same time (or maybe one request took a lot of time, so other requests came in the meantime).

    I would rather expect the delay to be placed in the branch where we see that, since the last time we checked, no new request has arrived.

    1. 1

      The article answers your question:

      In the common case where a page is only requested from the server every once in a while, the system will spend almost all of its time in this [busy-wait] state.

      Unfortunately, the simple Arduino environment does not want us to mess with the RTOS. The ESP8266WebServer library certainly isn’t written with such use in mind. This approach would require a lot of refactoring of ESP8266WebServer and the code that uses it.

      1. 2

        No, I don’t think this answers my question. My question/remark is that the code should be something like this:

        if (server.clientWaiting()) {
          server.handleClient();
        } else {
          delay(1);
        }
        

        which guarantees that, if several requests arrive together, there is no unnecessary delay between them (as there will be with the current code).

        1. 1

          ESP8266WebServer does not have any such method. There is an obvious place in the handleClient method to return some kind of “no work” status code which would accomplish what you want, but as-is it doesn’t do that.

          Either way, the always delay approach works fine for the common case the author describes.

          1. 1

            If there are other resources on the page (stylesheet, images, …) that take their own request to get, we are inserting a delay inbetween loading those page elements. That’s why we may want to skip delaying while we are busy. It depends how much it hurts, of course.

            If that becomes important (maybe later in the product cycle), and we can’t fix the API, we would have to resort to some side channel like timing it or putting a canary value on the stack. Such fragile workarounds, of course, really point at a too limiting API and that we should use something else.

            Making it interrupt driven should of course be ideal, latency wise, but I don’t know about the power – how a mere delay(1) could have such a power impact. That was an interesting observation.

          2. 1

            Maybe something like:

            
            static bool workDoneThisIteration = false;
            
            void setup(void) {
              server.addHook([](const String& method, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction contentType) {
                workDoneThisIteration = true;
                return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
              });
              server.on("/", handleRoot)
              server.on("/favicon.ico", handleFavicon)
            }
            
            void loop(void) {
              workDoneThisIteration = false;
              server.handleClient();
              if (!workDoneThisIteration) {
                delay(1);
              }
            }
            
      2. 1

        Can the ESP8266 do interrupts? This would be the ol’ time-based to event-based improvement.

        1. 4

          The article answers your question:

          Ideally we could use the underlying RTOS to only schedule a handleClient() task in response to network events. The RTOS is smart enough to put the CPU to sleep when no task needs to run. Unfortunately, the simple Arduino environment does not want us to mess with the RTOS.

          1. 1

            So we essentially just spin endlessly, in the RTOS ?