WiFiWebServer latency

Hello.
Just started investigating what BW16 is about, managed to make it work instantly!

For now I have only one concern - latency.

Using WiFiWebServer example, I modified it just a bit for the response to be:

  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/plain");
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  client.println(text);

Using postman, I started to send requests to the mcu. Using 5.8G LAN.
Usual latency is around 40-50ms. Sometimes it becomes something around 150ms.

The questions are the following: is it the best we can get? What do these 150ms spikes mean?

Having some esp32 experience in the same field, I tried to disable all the powersafe modes, all the sleep modes and so on. No success. Noting changed no matter what.

The most promising function was

wifi_disable_powersave();

but still no changes detected.

For now I don’t care about power consumption much, so would appreciate any advice in terms of how to make it as stable and responsive as possible. Thanks.

Hi @nyancat

I am not familiar with postman, can you provide more details on this and how you are testing and measuring?

Postman is a software to debug requests. Used in web development often.

It gives such an output.

According to my notes about testing esp32 a while ago, measuring the same way, minimal total latency was 19ms.

Btw, using curl in linux console, it shows around 26ms

Lets compare these numbers relatively to each other 16ms and 40-50ms.

So with esp32 it was possible to receive the response it 2-3 times faster.

So I would like someone to share his experience about speeding it up.

With esp32 it was about disabling powersave in multiple ways and increasing clock frequency.

What options could be there with with BW16? The only thing I found is

wifi_disable_powersave();

which does not change anything.

Ok, so first thing to mention: I managed to decrease the latency to 20-something ms by disabling postman default headers that i don’t actually need.

But there is a much more interesting thing that I noticed:

After commenting out these printf lines I managed to decrease the latency up to 10ms!

And I don’t see any options to avoid unwanted serial output which takes a lot of time! Please let me know if there are any options to disable these debug messages without modifying the libs.

Ok. I returned those 2 printf()s and created a basic script with a loop that sends curl requests (100 requests with 50ms delay) :

Talking about this screenshot:

First request in a series takes much more time. After the first one it is pretty stable. If I restart the loop right after the previous execution gets finished - it keeps being stable. If I make a 5s pause - again the first request takes much more time.
Is MCU sleeping or something if not receiving requests for a while? Is it possible to make it stably responsive no matter when was the last time it received a request?

Again as described above, with esp32 I managed to avoid spikes and minimise the latency by disabling powersave, but not with BW16 :frowning:

At the same time I should mention that after running a 1000 requests - no packages lost, comparing to esp32 where I constantly have around 3/1000 lost.

Also BW16 does not heat at all, especially comparing with esp32 which becomes ultimately hot pretty fast no matter which esp32 module I use.

None are the current BW16 issues are critical for me, but still would be really nice to deal with these small problems never to think about them again.

And sorry if I’m using the wrong terms, I’m not a huge expert.

I would suggest looking through the Powersave and WiFi chapters of AN0400 Application Note.

What you are describing sounds like the WiFi powersave feature to me, where WiFi drops into a low power state when there is no data transmission going on. Thus the first transmission will take more time as it waits for WiFi to return to normal.


However, wifi_disable_powersave(); should turn off this feature.
Another possibility is FreeRTOS tickless mode, which puts the processor into a low power state when there are no tasks running. You might want to look into this aspect.

Regarding the printf lines, if you do not wish to change the code, increasing the Serial port baud rate to the max of 2M baud should help to reduce the time spent on waiting for UART transmission.

1 Like

@wyy , thanks for your reply.

I went through the documentation again, went through the source of the libraries…
Ended up trying the following functions, even all of them at once:

  wifi_disable_powersave();
  wext_set_trp_tis(RTW_TRP_TIS_DISABLE);
  rtw_ps_enable(0);
  wifi_set_lps_dtim(0);
  wifi_set_power_mode(0, 0);

Nothing really changes in terms of the thing that it takes a while for it to wake up after not being requested for some time. :frowning:

My current results look like this:

20 requests in a row, with a 50ms delay in between them. So it’s always something like that the first 1-15 requests take much longer. And all the rest is ultimately ok.

Don’t get me wrong, these results are really nice, but I see that it is can constantly be as low as 7-10ms, so it would be nice to have the same consistency no matter if it is the first request of 20th.

For all the other people googling the same problem wanting it to go as low as 7ms:

What I did is I commented out all the printf() in the libs. Did it automatically with random ide’s auto-replace through the folder.
Also I my particular case I don’t need to get the whole request data, I just need the url string to parse it right after. And the following code reads just the first string of the request and saves a decent amount of milliseconds:

void readClient() {
  WiFiClient client = server.available();
  if (client) {
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n') {
          break;
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }

    int endwith = currentLine.indexOf(" HTTP");
    int startWith = currentLine.indexOf("/");
    if (endwith > 0 || startWith > 0) {
      emptyOkresponse(client);
      currentLine = currentLine.substring(startWith, endwith);
      Serial.println(currentLine);
    } else {
      emptyErrorResponse(client); // just in case
    }

    delay(1);
    client.stop();
  }
}

If anybody sees anything wrong with the code above - please let me know :slight_smile:

So the only question left is about how to disable this “power safe” or “sleep” feature. Would appreciate any advice, what else should I try except the functions that I listed in the very top of this comment.

Hi @nyancat

Can you provide the Arduino code and curl script you are using to get the results. I would like to try it out and see what I can do.

@wyy , thanks for your support. Here comes my code:

Arduino:

#include <WiFi.h>
#include "cmsis_os.h"
#include "wiring_os.h"
#include "wifi_conf.h"
#include "wifi_util.h"
#include "rtl8721d_system.h"

char ssid[] = "";    // your network SSID (name)
char pass[] = "";       // your network password

#define METHOD_GET "GET"
#define METHOD_POST "POST"

int status = WL_IDLE_STATUS;

WiFiServer server(80);

void setup() {
  Serial.begin(2000000);
  while (!Serial) {
    ;
  }

  wifi_disable_powersave();
  wext_set_trp_tis(RTW_TRP_TIS_DISABLE);
  rtw_ps_enable(0);
  wifi_set_lps_dtim(0);
  wifi_set_power_mode(0, 0);

  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    delay(3000);
  }

  server.begin();
}


void loop() {
  readClient();
}

void readClient() {
  // listen for incoming clients
  WiFiClient client = server.available();
  if (client) {
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n') {
          break;
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }

    processRequest(currentLine, client);

    delay(1);
    client.stop();
  }
}

void processRequest(String url, WiFiClient client) {
  int urlEndwith = url.indexOf(" HTTP");
  int urlStartWith = url.indexOf("/");
  int methodEndsWith = url.indexOf(" ");
  Serial.println(url);
  if (urlEndwith > 0 && urlStartWith > 0 && methodEndsWith > 0) {
    String method = url.substring(0, methodEndsWith);
    url = url.substring(urlStartWith + 1, urlEndwith);
    if (url == "ping") {
      response(client, 200, "alive");
      return;
    }
  }

  response(client, 500, "");
}

void response(WiFiClient client, int responseCode, String text) {
  client.println("HTTP/1.1 " + String(responseCode));
  if (text.length() > 0) {
    client.println("Content-Type: text/plain");
  }
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  if (text.length() > 0) {
    client.println(text);
  }
}

My script is written in PHP, I hope it is ok. In my case on linux it’s enough to have php installed, and run

php script.php

in console.

<?php
    const IP = "192.168.0.101";
    $total = 0;
    $max = 0;
    $min = 1000;
    $errors = 0;
    for($i = 0; $i<=100; $i++) {
        $start = microtime(true);
        $ch = curl_init("http://".IP."/ping");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_TCP_FASTOPEN, true);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_exec($ch);
        curl_close($ch);      
        $time_elapsed_secs = microtime(true) - $start;
        if (curl_errno($ch)) {
            $error_msg = curl_error($ch);
            echo $error_msg.PHP_EOL;
            $errors++;
            $continue;
        }
        $took = round(($time_elapsed_secs * 1000), 2);
        $total += $took;
        echo $i.") ". $took . "ms". PHP_EOL;
        $max = $took > $max ? $took : $max;
        $min = $took < $min ? $took : $min;
        usleep(100000);
    }
    echo PHP_EOL;
    echo "max = " . $max;
    echo PHP_EOL;
    echo "min = " . $min;
    echo PHP_EOL;
    echo "errors = " . $errors;
?>  

Output in my case is the following:
image

@nyancat

I have managed to replicate your latency results, and after some testing, it seems like the increased latency for the first few requests might be coming from the WiFi router.

Can you test with the following Arduino code? It starts a local WiFi AP on the BW16, to which you can connect and test using the php script.

#include <WiFi.h>

char ssid[] = "";    // your network SSID (name)
char APssid[]="AmebaD_testAP";    // your network SSID (name)
char pass[] = "Password";       // your network password

#define METHOD_GET "GET"
#define METHOD_POST "POST"

int status = WL_IDLE_STATUS;
WiFiServer server(80);

void setup() {
  Serial.begin(2000000);

  status = WiFi.status();
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
//    status = WiFi.begin(ssid, pass);
    status = WiFi.apbegin(APssid, pass, "153");
    delay(3000);
  }

  server.begin();
}

void loop() {
  readClient();
}

void readClient() {
  // listen for incoming clients
  WiFiClient client = server.available();
  if (client) {
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n') {
          break;
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }

    processRequest(currentLine, client);

    delay(1);
    client.stop();
  }
}

void processRequest(String url, WiFiClient client) {
  int urlEndwith = url.indexOf(" HTTP");
  int urlStartWith = url.indexOf("/");
  int methodEndsWith = url.indexOf(" ");
//  Serial.println(url);
  if (urlEndwith > 0 && urlStartWith > 0 && methodEndsWith > 0) {
    String method = url.substring(0, methodEndsWith);
    Serial.println(method);
    url = url.substring(urlStartWith + 1, urlEndwith);
    if (url == "ping") {
      response(client, 200, "alive");
      return;
    }
  }

  response(client, 500, "");
}

void response(WiFiClient client, int responseCode, String text) {
  client.println("HTTP/1.1 " + String(responseCode));
  if (text.length() > 0) {
    client.println("Content-Type: text/plain");
  }
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  if (text.length() > 0) {
    client.println(text);
  }
}

Yeap, this way, the first request latency is around the same as for all the following. Nice.

Are you sure the router is the most likely to blame for such a difference?

Can it be anyhow possible that along with entering the AP mode microcontroller also switches to the power-safe mode?

@nyancat

I should have first considered the obvious solution of disabling powersave only after starting WiFi

#include <WiFi.h>
#include "wifi_conf.h"

char ssid[] = "";    // your network SSID (name)
char pass[] = "";       // your network password

#define METHOD_GET "GET"
#define METHOD_POST "POST"

int status = WL_IDLE_STATUS;
WiFiServer server(80);

void setup() {
  Serial.begin(2000000);

  status = WiFi.status();
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    delay(3000);
  }

  wifi_disable_powersave();
  server.begin();
}

void loop() {
  readClient();
}

void readClient() {
  // listen for incoming clients
  WiFiClient client = server.available();
  if (client) {
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n') {
          break;
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }

    processRequest(currentLine, client);

    delay(1);
    client.stop();
  }
}

void processRequest(String url, WiFiClient client) {
  int urlEndwith = url.indexOf(" HTTP");
  int urlStartWith = url.indexOf("/");
  int methodEndsWith = url.indexOf(" ");
//  Serial.println(url);
  if (urlEndwith > 0 && urlStartWith > 0 && methodEndsWith > 0) {
    String method = url.substring(0, methodEndsWith);
    Serial.println(method);
    url = url.substring(urlStartWith + 1, urlEndwith);
    if (url == "ping") {
      response(client, 200, "alive");
      return;
    }
  }

  response(client, 500, "");
}

void response(WiFiClient client, int responseCode, String text) {
  client.println("HTTP/1.1 " + String(responseCode));
  if (text.length() > 0) {
    client.println("Content-Type: text/plain");
  }
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  if (text.length() > 0) {
    client.println(text);
  }
}

@wyy,
omg, thank you! It works perfectly this way. I thought that I had tried to put wifi_disable_powersave() everywhere with no success, but probably missed this exact way.

1 Like