While the Arduino IDE is a convenient and familiar platform to develop ESP8266 Web Server based projects, it does have a significant drawback. That is, once the Web Server is started, it is necessary to poll the server each iteration of the loop() function to check for new connections before processing the request.
Putting the check in a loop with other activities, such as reading sensors could and often does result in significant response delays. And possible loss of communication due to connection timeouts.
A better approach is to use a callback that fires whenever an external client connects to the ESP8266 server. This frees up the sketch loop() from the task of servicing http request using inefficient polling code.
And callbacks is how a Web Server is implemented using the ESP8266 EspressIf SDK. But why is it missing from the Arduino IDE? I suspect it is because that is the way it is done with Arduino-based systems—making it both familiar and perhaps suitable for Arduino code reuse with the ESP8266.
But it is not the best solution for new designs.
So that got me thinking…
Why not customize the typical web server sketch to use the SDK callback schema? After all, it is possible to call ESP8266 SDK functions from the Arduino IDE. While searching, I could not find any examples of this being done, so I’ve created one myself.
And here it is…
The SDK Server Architecture
The SDK API requires several layers of callbacks in a web server design. Since these callbacks are usable in a sketch without modification, the actual sketch functions included in this example are shown.
First, the server must be setup in the sketch setup() routine. This initialization code was encapsulated in the following function, “SdkWebServer_Init()”, called in setup():
- void SdkWebServer_Init(int port) {
- LOCAL struct espconn esp_conn;
- LOCAL esp_tcp esptcp;
- //Fill the connection structure, including "listen" port
- esp_conn.type = ESPCONN_TCP;
- esp_conn.state = ESPCONN_NONE;
- esp_conn.proto.tcp = &esptcp;
- esp_conn.proto.tcp->local_port = port;
- esp_conn.recv_callback = NULL;
- esp_conn.sent_callback = NULL;
- esp_conn.reverse = NULL;
- //Register the connection timeout(0=no timeout)
- espconn_regist_time(&esp_conn,0,0);
- //Register connection callback
- espconn_regist_connectcb(&esp_conn, SdkWebServer_listen);
- //Start Listening for connections
- espconn_accept(&esp_conn);
- }
Upon an external TCP connection, the callback “SdkWebServer_listen” is executed:
- void SdkWebServer_listen(void *arg)
- {
- struct espconn *pesp_conn = ( espconn *)arg;
- espconn_regist_recvcb(pesp_conn, SdkWebServer_recv);
- espconn_regist_reconcb(pesp_conn, SdkWebServer_recon);
- espconn_regist_disconcb(pesp_conn, SdkWebServer_discon);
- }
This functions registers 3 connection-specific callbacks:
- SdkWebServer_recv – Called when connection data is received. This callback replaces the polling loop when using the Arduino library.
- SdkWebServer_recon – Called when TCP connection is broken.
- SdkWebServer_discon-Called when a TCP connection is closed.
The SdkWebServer_recv() function processes http GET and POST requests in a similar manner as the processing in the loop() after an Arduino client connection is detected.
Putting It All Together
In order to contrast the commonly used Arduino Web Server library with the SDK API, I’ve put together an example sketch that supports both web server approaches. It’s on Github:
ESP8266-12 Arduino IDE Web Server Example
This example provide the same functionality with either server interface. The selection is made prior to building the sketch, using precompilation directives. When possible, the same code is used with both selections. The difference is that the SDK uses callbacks while the Arduino polls for connections.
While no additional hardware is needed to run and test the Web Server, this example assumes the ESP8266 circuit from this post. But the code will work with any ESP8266 circuit, even with no externally connected sensors or controls. But without the ADC multiplexer from the referenced post, the ADC will simply make the same measurement for each multiplexer setting. Which is just fine for the purposes of this example.
Testing the Server From A Web Browser
Since both versions of the Web Server provide the same functionality, the same test can be performed for verification.
You might need to changed the static IP set in this example to match your own LAN subnet. The defaults are currently set to:
IP: 192.168.0.132
Listen Port: 9701
The test url is thus:
192.168.0.132:9701/?request=GetSensors
And the JSON returned from the Web Server should be in the following format:
{ "Ain0":"316.00", "Ain1":"326.00", "Ain2":"325.00", "Ain3":"314.00", "Ain4":"316.00", "Ain5":"163.00", "Ain6":"208.00", "Ain7":"333.00", "SYS_Heap":"25408", "SYS_Time":"26" }
And if connected, the server will also support changing the LED on/off state:
To turn it on, use the URL:
192.168.0.132:9701/?request=LedOn
And the off URL i:
192.168.0.132:9701/?request=LedOff
Adding server functionality should be obvious after reviewing the sketch.
Conclusions
Using a callback to process Web Server connection requests is far superior to polling. CPU bandwidth is only used in response to client connection events. And with the example framework provided in this post, you can still utilize many of the Arduino sensor libraries without modification.
With a Web Server application, you get the efficiency of the callback with the familiarity of the Arduino IDE.
I hope you find this information useful…