logo elektroda
logo elektroda
X
logo elektroda

[Solved] ESP8266 (Arduino): Optimisation of server polling frequency in lighting control

krzbor 1401 20
Best answers

Can I use an ESP8266 web server as a wake-up trigger by calling server.available() and immediately client.stop() without reading the request, and will that cause memory leaks or require separate WiFiClient objects?

Yes — if you keep a single WiFiClient object outside loop(), using server.available() as a trigger and then calling client.stop() immediately is acceptable, and repeatedly creating and deleting WiFiClient inside loop() is what can fragment the heap [#18943842][#18944171] You should still check the result of client.stop() or the connection state (for example with server.hasClient(), client.connected(), or similar) so you know whether the connection was actually closed [#18944171] There is no benefit to using two WiFiClient objects for the same server/client role here; it only increases RAM usage [#18944073] Also, connect() already closes any previous connection for that WiFiClient, and in the final working setup the author used a global WiFiClient plus different timeouts: a short timeout for reads and a longer one before client.connect(host, 80) to avoid occasional delays [#18944712][#19069011]
Generated by the language model.
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
  • #1 18943804
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    Let me start by describing the problem. I have a lot of ESP at home. The whole control model is based on connectivity to a server, specifically PHP scripts. The WEB application connects to the server and sends or receives information (e.g. lights a lamp, reads the temperature, etc.). On the other hand, I have ESPs that respond accordingly. If they just send data (e.g. temperature) then there is no problem – they do it at a certain interval. Worse when they are actors – then they ask the server if there is something to do e.g. whether to switch on a relay. In the case of lighting control, however, I don't want to wait long for a response, hence the polling needs to be frequent e.g. every 0.5s. This is not a problem for the server, but nevertheless generates unnecessary traffic and even so these 0.5s are noticeable. Of course, the solution would be to set the ESP in the position of the server, but here other problems arise, e.g. time control – for example, if someone turns on an outdoor light and forgets to turn it off, then with regular polling, there is no problem for the PHP server to conclude that the sun has risen and there is no need to shine.
    So I came up with a solution that combines the advantages of both solutions (client and server) for actors. It involves cyclically querying at a longer interval (e.g. 20s), while at the same time using the web server function in ESP exclusively to signal – „stop waiting and query, because I have something for you”. In theory, this approach doesn't change the philosophy and doesn't require a lot of software redesign, yet it should be fast and effective.
    My idea is very simple (below is the skeleton of the idea):
    Code: C / C++
    Log in, to see the code
    .
    And now the questions – is such an immediate server shutdown (without downloading) correct? Will everything allow itself and will there be no memory leak? Can I use the „client” variable immediately for uploading?
  • ADVERTISEMENT
  • Helpful post
    #2 18943842
    Anonymous
    Level 1  
  • ADVERTISEMENT
  • #3 18943873
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    I mean:
    Code: C / C++
    Log in, to see the code
    .
    That is, I check if something is there and don't even read it, but immediately client.stop()

    As for the location of the WiFiClient (inside the loop) you are absolutely right. I think I used a not very good example once.
  • ADVERTISEMENT
  • #4 18943945
    mpier
    Level 29  
    Posts: 817
    Help: 153
    Rate: 141
    Hello,
    in the past if I remember correctly, http and www were used to serve pages to the browser. If you chose php because you have a good understanding of the issues, then you could use websocket for two-way communication. If not necessarily, then the easiest thing would be to write a separate server to communicate with clients (I mean service, because whether the "server" is a server or a client is irrelevant), here it's all the same whether in php, c or other. You solve both problems in one go.
  • #5 18943948
    Anonymous
    Level 1  
  • #6 18944041
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    I am not explicitly allocating anything. The point is that when server.available() is called, some memory will already be allocated because some data has already been received (unless I'm thinking wrong). I don't read that data, I just call stop(). Second point - from your description I conclude that it's better to use two objects of the WiFiClient class - one for receiving and one for sending then I won't have to worry about the time for closing.
  • #7 18944073
    Anonymous
    Level 1  
  • #8 18944103
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    Maybe I have described the idea wrong. In a loop which is called quite often (10ms) at first I check if there was a call to the ESP server (server.available(); ) and then instead of reading from it and being interested I want to immediately ignore everything that came in and close the call. I don't want to read anything from it. It's just supposed to be this kind of 'trigger'. Then there is my actual connection (which I have been using for years) - this time ESP is the client, not the server. I started this thread to consider this problem - if the ESP server "reports" server.available(), and I brute-force it immediately with client.stop(); would this be correct.
  • #9 18944171
    Anonymous
    Level 1  
  • #10 18944247
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    OK, but it can't return the wrong client to me because one has any port from Apache and port 80 on ESP server, and the other has any port on ESP client and 80 port on Apache.
  • #11 18944283
    Anonymous
    Level 1  
  • #12 18944699
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    khoam wrote:
    So only one external client connects to the local server in the ESP? I don't quite understand this statement. Maybe some simple diagram?
    .
    Exactly as you wrote.
    Currently it is as follows:
    1. I turn on the light in the browser (app).
    2. this information flies to the main server (to PHP).
    3. PHP writes the information "you need to turn on the light for 7 minutes" into a file.
    4. completely asynchronously ESP connects to the same server and asks "do you need to turn on the light?".
    5. the server replies "yes light on" or "no light on" (depending on the time)
    6. the ESP performs the specified action.
    In order for the whole thing to be responsive, the ESP needs to ask itself frequently, e.g. every 0.5s, and this is what I wanted to change.

    After the change it should be like this:
    1. in the browser (app) I turn on the light.
    2. this information goes to the main server (to PHP).
    3. PHP writes the information "you need to turn on the light for 7 minutes" into a file and additionally sends a signal to the actual ESP "stay awake and get on with it"
    4. completely asynchronously the ESP connects to the same server and asks "do you need to turn on the light?".
    5. the server replies "yes light up" or "no light up" (depending on the time)
    6. the ESP performs the specified action.
    Now the ESP can connect every 20s (e.g. to find out that it's already daytime and it's pointless to light up), because an additional signal (trigger) has forced the immediate triggering of point 4. As you can see, this additional trigger can, in my conception, only be fired from the main server. It also does not carry any data, it is simply to force ESP to immediately execute 4.

    I know the whole concept can be remodelled, but it works for me. I just want to optimise it. I am currently adding an OTA to my solutions. When ESP starts, there is a version check and a possible firmware swap (the version is also checked by PHP). On this occasion, I will be collecting the IP addresses of the modules so that PHP at point 3 knows which ESP to urge.
  • ADVERTISEMENT
  • #13 18944712
    Anonymous
    Level 1  
  • #14 18945332
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    @khoam, thank you for your guidance. In the end, I decided on 2 WiFiClient objects (declared outside the loop of course) - one for the server and one for the client, and left the client.stop without parameters. The server client will have a lot of time because when there was a request to the ESP server, there is an ESP client action (and that action is running on another object), which is after all ongoing. Then the start of the loop has a 10ms delay. After the initial perplexities of not working (I just forgot about server.begin(); ) the whole thing works wonderfully - I built a test circuit with an LED - virtually instant responsiveness.
  • #15 18950174
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    It was time to move the test solution to the existing solution. Overall it works well - instant responsiveness as it was during testing. Unfortunately there is a noticeable delay every so often (once every few dozen actions). I estimate it to be about 1s. I have analysed the PHP code and it is not the cause. The ESP remains. It is definitely not related to my "trigger" getting lost, because the delays would be random and much longer (up to 20s). The only place is:
    Code: C / C++
    Log in, to see the code
    .
    There is a 1 second delay here. I changed the program and instead of delay(1000) I gave delay(100). Since then I have not seen a delay once. Of course, the problem itself probably still occurs, but the 100ms is not noticeable, and the next call already happens. I'm writing about this because I was treating the line "if (!client.connect(host, httpPort))" as a really fallback solution. And here it appears that a few percent of calls may be affected. Is this normal?
  • #16 18950298
    Anonymous
    Level 1  
  • #17 18950472
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    I think I have found the reason. Well, when the WiFiClient is inside the function, as I read, it is not necessary to call client.stop() (it is called when releasing the object). When the WiFiClient object is brought outside the loop function client.stop() is mandatory, and I didn't have it.
    For now, I went back to delay(1000) to see if the delay would show up again. However, it works flawlessly.
  • #18 18950488
    Anonymous
    Level 1  
  • #19 18950526
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    I was based on this thread: Link Several people commented that without the "stop" something didn't work for them. I can only confirm that "stop" has also improved my situation.
  • #20 18950538
    Anonymous
    Level 1  
  • #21 19069011
    krzbor
    Level 29  
    Posts: 1731
    Help: 40
    Rate: 1044
    The communication problem has finally been solved. After moving WiFiClient outside of the function (global object) the setting client.setTimeout(10); applies not only to subsequent calls (client.readString();) but also to other operations, in particular client.connect(host, 80); Here these 10ms are often not enough and therefore did not always work. Now before client.connect(host, 80) I added client.setTimeout(300); and it is OK. In general, I have to say that the concept of periodically checking the state every 20-30 seconds with an additional wakeup by calling the server on the ESP works well - instant responsiveness, and if anything there is a status query every 20 seconds anyway.
    Thank you @khoam Topic closed

Topic summary

✨ The discussion revolves around optimizing the polling frequency of ESP8266 devices in a lighting control system that communicates with a PHP server. The user initially faced issues with high-frequency polling (every 0.5 seconds) causing unnecessary traffic and noticeable delays. Solutions proposed include using two instances of the WiFiClient class—one for sending and one for receiving data—to manage connections more efficiently. The importance of properly managing client connections with methods like client.stop() and client.connect() was emphasized to avoid memory issues and ensure responsiveness. The final implementation involved setting a timeout for client connections and periodic state checks, resulting in improved performance and instant responsiveness in the lighting control system.
Generated by the language model.

FAQ

TL;DR: Poll every 20–30 s, add an HTTP “trigger” for “instant responsiveness,” and use ~300 ms timeouts to stabilize connects. [Elektroda, krzbor, post #19069011]

Why it matters: You’ll get near‑instant light control without chatty 0.5 s polling or major code rewrites.

Quick Facts

How do I reduce ESP8266 server polling for lighting control without missing commands?

Use hybrid polling with a wake trigger.
  1. In loop(), check server.available(); on hit, stop() and set a flag.
  2. If flagged or interval elapsed, client.connect() to PHP and fetch action.
  3. Execute action; next periodic poll happens in 20–30 s. This keeps responsiveness high and traffic low. [Elektroda, krzbor, post #18944699]

Is it safe to call client.stop() right after server.available()?

Yes. “Nothing bad will happen.” server.available() returns a client reference; you may close it immediately. Check stop()’s return to confirm a clean close, and server.hasClient() if you need to know about existing connections. This approach fits a trigger-only endpoint. [Elektroda, khoam, post #18944171]

Should I create WiFiClient inside loop(), or reuse a global?

Avoid creating and destroying WiFiClient on each loop pass. It increases heap fragmentation and is impractical. Prefer a persistent client object or reuse patterns that limit allocations. This keeps memory stable during frequent polling and quick triggers. [Elektroda, khoam, post #18943842]

Do I need client.stop() before client.connect() on ESP8266?

Not strictly. client.connect() implicitly calls stop() and frees the previous client context. The WiFiClient assignment operator also closes any existing connection. Explicit stop() is still fine if you want to release sooner. [Elektroda, khoam, post #18944712]

Why do I sometimes see a 1 s delay during connect?

Transient connect failures can occur due to heap availability or timeouts. client.connect() also defaults to a specific TCP Nagle setting (setNoDelay), which you can change. Handle failures gracefully and keep your retry wait minimal to avoid visible lag. [Elektroda, khoam, post #18950298]

How can I reduce visible lag when connect occasionally fails?

Shorten your fallback delay after a failed client.connect(). Dropping the delay from 1000 ms to 100 ms eliminated noticeable lag in practice, even when a few percent of attempts failed. This keeps the UI feeling instant. [Elektroda, krzbor, post #18950174]

Does client.setTimeout affect connect(), and what value should I use?

Yes, with a global WiFiClient, setTimeout also applies to connect(). A 10 ms timeout proved too short and caused intermittent failures. Increasing to ~300 ms restored reliable connections while keeping the system responsive. [Elektroda, krzbor, post #19069011]

Can I use two WiFiClient objects (one for the ESP server, one for the PHP client)?

Yes. Using two globally-declared WiFiClient objects worked and delivered instant responsiveness in testing. Keep in mind memory trade-offs if many modules run. Validate stability under your workload. [Elektroda, krzbor, post #18945332]

Will two WiFiClient objects cost extra RAM versus one?

Yes. If both talk to the same server, the second client brings little benefit and increases RAM usage due to extra contexts. Reusing a single client is leaner unless separation simplifies your flow. [Elektroda, khoam, post #18944073]

What’s a safe way to close a pending server client quickly?

Use a short, conditional stop. Example: client.stop(client.connected() ? 5 : 0). This gives a brief timeout to tear down cleanly and clear buffers before you proceed with the outbound client.connect(). “Give a short timeout.” [Elektroda, khoam, post #18944712]

How can I check if the ESP server already has a client connection?

Use server.hasClient() to detect pending connections. You can also inspect client.connected(), client.status(), or client.remoteIP() after server.available(). This helps you decide whether to accept, ignore, or immediately stop that client. [Elektroda, khoam, post #18944171]

Should I switch to WebSocket instead of HTTP polling for real-time control?

For true bidirectional, low-latency control, WebSocket is a solid choice. Alternatively, implement a dedicated service for client communication if PHP isn’t ideal for real-time signaling. Both approaches reduce polling load and latency. [Elektroda, mpier, post #18943945]

Is 0.5 s polling fast enough for lights, and is it worth the traffic?

0.5 s polling can feel noticeable and creates unnecessary server traffic. A trigger-plus-longer-interval model preserves responsiveness while cutting requests. Keep the periodic poll for safety checks like sunrise/sunset logic. [Elektroda, krzbor, post #18943804]

Why didn’t client.connect() release the previous connection on older cores?

Early Arduino Core for ESP8266 2.4.0‑rc builds had a bug where connect() failed to release prior contexts. Explicit stop() was a workaround. Update to newer cores to avoid this edge case. [Elektroda, khoam, post #18950538]
Generated by the language model.
ADVERTISEMENT