So this one caught me off-guard.
I’m currently working on a project that uses an ESP32-S2, and for expedience I’m working with the Arduino framework. It’s just faster that way, honestly. It does what I need.
Or it did, right up until I discovered that all of my HTTP requests from the ESP32 were taking one exact second to complete, even though the server was responding in a couple of milliseconds.
What gives?
The Code
The code for the HTTP part of my little project uses the built in Arduino HttpClient, and looks something like this:
HttpClient c = HttpClient(wifi_client, server, port);
c.beginRequest();
c.get(uri);
c.sendHeader("Authorization", "bearer " + token);
c.endRequest();
auto status = c.responseStatusCode();
auto response = c.responseBody();
Not exact, but close enough.
The Problem
When I instrumented that simple code, I found that the delay always occurred in responseStatusCode(). This made some level of sense, because that’s where the actual bulk of the data retrieval is started. Digging into that revealed something… annoying.
The Problem
A bounce through the code in HttpClient revealed this little gem where it gathers the response data:
c = read();
if (c != -1)
{
// ...do a bunch of stuff...
}
else
{
// We haven't got any data, so let's pause to allow some to
// arrive
delay(kHttpWaitForDataDelay);
}
Can you guess what kHttpWaitForDataDelay is set to?
// Number of milliseconds that we wait each time there isn't any data
// available to be read (during status code and header processing)
static const int kHttpWaitForDataDelay = 1000;
Yup. And it’s not even configurable; no, it’s a constant!
The Solution
My solution? It looks like this:
HttpClient c = HttpClient(wifi_client, server, port);
c.beginRequest();
c.get(uri);
c.sendHeader("Authorization", "bearer " + token);
c.endRequest();
// My requests return in 2ms, which is (unsurprisingly) not quite fast
// enough to avoid the built-in 1s delay if no data is available
// immediately.
//
// TODO: Fix this in the library, because it's really dumb.
delay(10); // 10ms should do the trick in the general case...
auto status = c.responseStatusCode();
auto response = c.responseBody();
Yup. Add a delay, and my code runs faster.
Most of the time, anyway… Occasionally, something blips and it takes more than 10ms for the server response to be generated, though that is rare. Haven’t bothered tracking it down yet either, but it’s probably the vagaries of WiFi.
Final Thoughts
This is actually part of a product I’m working on in my attempt to start a business, so I don’t have as much time as I would like right this second; I’m in the middle of working on my prototype. If I did, I would go fix that in the Arduino code; I can see at least one trivial solution to the problem right off the top of my head, and without any event-driven witchcraft.
I should have some spare time in the near future, though, so I may just submit a patch as soon as I tear myself away…
[edit: typos. Somhow I managed to transpose characters in ‘Arduino’ and not catch it. Twice. Sigh.]