ESP8266 Dual AT & Web Server Mode

Here is an exciting framework for fully utilizing the huge capabilities of the ESP8266.

This is an original.

Something that I have not seen anywhere before. You see, using this structure opens up many more possibilities and applications for this amazing device…

Most folks in the ESP8266 world seem to fall into one of two camps. The first group, typically diehard Arduino developers, want to use the ESP8266 as a serial to WIFI shield. A couple bucks to add WIFI to the very popular Arduino platform is most cost effective method of bringing it on-line. Much cheaper than alternative WiFi shields.

Others see the ESP8266 as a complete solution. It is often referred to as a System On a Chip (SoC). They not only use the ESP8266 for WIFI internet access, but also use the platform to read sensors, control “things”, process data and provide web server functionality.

Among The Examples

The majority of those that have delved into the ESP8266 world started by loading the serial to WIFI firmware often referred to as the AT command set. This is available as a binary file downloaded to the ESP8266 via it’s serial interface. That’s where I began my ESP8266 journey.

It was not long after my start that I searched for the AT firmware source code. Fortunately, it is available within earlier versions of the EspressIf SDK. You see, prior to version 1.0, many example applications were included in the development package. And yes, the AT command set was among these examples.

The other most useful example application is the IoT_Demo. This software has survived every release of the SDK, including the current version. This example provides a framework for a web server to make your “things” Internet accessible.

After studying the software structure of these examples I got to thinking…

Wouldn’t it be great to merge these two applications? So you benefit from both features?

It indeed did look feasible.

The combined application would not only service AT commands through the serial port, but also service http GET or POST commands via the Internet.

And that’s not all…

This single ESP8266 firmware application could also be used to read sensors and control devices.

I am stoked to say I have successfully merged these examples. This broadens the capabilities and applications of a single ESP8266 based system.

It works!

Here’s how…

ESP8266 Dual Server Architecture

The ESP8266 is used to perform 3 primary functions. First, it provides basic serial to Wifi capability using the standard AT command set that many first-time users tinker with. Note that since the design presented here includes the AT firmware source code, the command set can be expanded to also provide access to the Esp8266 Daq and Control features. This is depicted in the following diagram as two-way arrows from the AT Server.


The ESP8266 also provides Web Server functionality. This feature operates completely independently from the serial AT Server. The web server responds to http GET commands received form the Internet via the built-in Wifi capability. Just like the AT Server, the Web Server has access to the Daq and Control functions.

And finally, the data acquisition (DAQ) and control function, also running independently from the other two features, controls all the Esp8266 outputs and receives all the sensor inputs. Because of the many possible sensors connected to the system, the DAQ function must be managed so that it does not monopolize the Esp8266 processor. This is accomplished by limiting it’s execution to the reading of one sensor each time the function is called.

Task Distribution

Separate callbacks are registered for each of the three ESP8266 primary functions. Both the AT and Web Server callbacks are event driven. They are only executed upon user request. The AT callback is executed anytime data is received on the serial port, while the Web Server callback is run upon receipt of an http GET request.

The callback for the Daq/Control features are different. This feature is set up to execute periodically (once every second) from a Timer callback. This keeps the sensor data fresh, available for consumption by the AT or Web Server, upon request.

AT Callback – The AT serial port server callback is installed in the uset_init() function when at_init() is executed. The AT server responds to any of the requests defined in at_cmd.h.

  1. at_funcationType at_fun[at_cmdNum]={
  2. {NULL, 0, NULL, NULL, NULL, at_exeCmdNull},
  3. .
  4. .
  5. {"+CIPSERVER", 10, NULL, NULL,at_setupCmdCipserver, NULL},
  6.   {"+CIPMODE", 8, NULL, at_queryCmdCipmode, at_setupCmdCipmode, NULL},
  7.   {"+CIPSTO", 7, NULL, at_queryCmdCipsto, at_setupCmdCipsto, NULL},
  8.   {"+CIUPDATE", 9, NULL, NULL, NULL, at_exeCmdCiupdate},
  9.   {"+CIPING", 7, NULL, NULL, NULL, at_exeCmdCiping},
  10.   {"+CIPAPPUP", 9, NULL, NULL, NULL, at_exeCmdCipappup},
  11.   {"+GETSENSOR", 10, NULL, NULL, at_exeGetSensorVal, NULL},

The new request, “+GETSENSOR”, has been added to provide a method for the serial port server to get the sensor readings. Upon receiving this request, the function at_exeGetSensorVal() is called. This function returns the current sensor reading over the ESP8266 serial port.

  2. at_exeGetSensorVal(uint8_t id, char *pPara)
  3. {
  4.       int isensno = atoi(++pPara);
  5.       switch(isensno) {
  6.             case 1:
  7.                   uart0_sendStr(tInside);
  8.                   break;
  9.             case 2:
  10.                   uart0_sendStr(tOutside);
  11.                   break;
  12.             case 3:
  13.                   uart0_sendStr(tAttic);
  14.                   break;
  15.             case 4:
  16.                   uart0_sendStr(tDht11);
  17.                   break;
  18.             case 5:
  19.                   uart0_sendStr(hDht11);
  20.                   break;
  21.             case 6:
  22.                   uart0_sendStr(pBmp085);
  23.                   break;
  24.             case 7:
  25.                   uart0_sendStr(tBmp085);
  26.                   break;
  27.             case 8:
  28.                   uart0_sendStr(aBmp085);
  29.                   break;
  30.             default:
  31.                   uart0_sendStr("out of range");
  32.                   break;
  33.       }
  34.       uart0_sendStr("\r\n");
  35. }

The requests are made by sending the following string over the serial port:


“n” is the sensor number and corresponds to the case number in the code above.

As you can see, it is not difficult to add your own custom commands to the standard ESP8266 AT command set.

Web Server Callback – This application uses the Web Server code provided in the IoT_Demo example. The user_init() function sets up and connects to the local Wifi before launching the Web Server. The callback webserver_recv() is registered during the initialization sequence. That function is executed any time an http GET request is received.

For this demo application, the Web Server only responds to “request-GetSensors”. More requests can be added as noted in the code that follows. When received, the server replies by sending a JSON string containing all the sensor readings.

  2. webserver_recv(void *arg, char *pusrdata, unsigned short length)
  3. {
  4.     URL_Param *pURL_Param = NULL;
  5.     char *pParseBuffer = NULL;
  6.     bool parse_flag = false;
  7.     struct espconn *ptrespconn = arg;
  8.     int i;
  9.     espconn_set_opt(ptrespconn, ESPCONN_REUSEADDR);
  10.     if(upgrade_lock == 0){
  11.           parse_flag = save_data(pusrdata, length);
  12.         if (parse_flag == false) {
  13.               response_send(ptrespconn, false);
  14.         }
  15.         pURL_Param = (URL_Param *)os_zalloc(sizeof(URL_Param));
  16.         parse_url_params(precvbuffer, pURL_Param);
  17.         switch (pURL_Param->Type) {
  18.             case GET:
  19.                 if(os_strcmp(pURL_Param->pParam[0], "request")==0) {
  20.                     <strong>// GetSensors is the only request the server currently supports</strong>
  21. <strong>                    if(os_strcmp(pURL_Param-&gt;pParVal[0], "GetSensors")==0) {</strong>
  22. <strong>                          json_send(ptrespconn, GET_SENSORS);</strong>
  23. <strong>                    }</strong>
  24.                     // Add additional requests here
  25.                 }
  26.                 json_send(ptrespconn, CONNECT_STATUS);
  27.                 break;
  28.             case POST:
  29.                   ets_uart_printf("We have a POST request.\n");
  30.                  break;
  31.         }
  32.         if (precvbuffer != NULL){
  33.               os_free(precvbuffer);
  34.               precvbuffer = NULL;
  35.         }
  36.         os_free(pURL_Param);
  37.         pURL_Param = NULL;
  38.     }
  39.     else if(upgrade_lock == 1){
  40.           local_upgrade_download(ptrespconn,pusrdata, length);
  41.             if (precvbuffer != NULL){
  42.                   os_free(precvbuffer);
  43.                   precvbuffer = NULL;
  44.             }
  45.             os_free(pURL_Param);
  46.             pURL_Param = NULL;
  47.     }
  48. }


Acquisition/Control Timer Callback – This callback serves as the application’s periodic loop() function. It is called once every second. The functions is mechanized as a state machine. In this example, 5 states are implemented, one for each sensor in the system. Exactly one state is executed each time the function is called. Upon completion of the sensor read, the code is set to execute the next state the next time the function is called. The code can be modified as needed to add or delete states and sensor hardware.

  1. LOCAL void ICACHE_FLASH_ATTR loop_cb(void *arg)
  2. {
  3.     char szT[32];
  4.     DHT_Sensor DHsensor;
  5.     DHT_Sensor_Data data;
  6. = 5;  //GPIO14
  7.     DHsensor.type = DHT11;
  8.     int32_t temperature;
  9.     int32_t pressure;
  10.     //---------------------------------------------------
  11.     //This state machine reads 1 sensor each iteration
  12.     //---------------------------------------------------
  13.       switch(nTcnt%5) {
  14.             case 0: //Read first DS18B20 Temperature Sensor
  15.                 get_temp_ds18b20(1,1,tInside);
  16.                 break;
  17.             case 1: //Read second DS18B20 Temperature Sensor
  18.                 get_temp_ds18b20(2,1,tOutside);
  19.                 break;
  20.             case 2: //Read third DS18B20 Temperature Sensor
  21.                 get_temp_ds18b20(3,1,tAttic);
  22.                 break;
  23.             case 3: //Read DHT11 temperature and humidity Sensor
  24.                 DHTRead(&amp;DHsensor, &amp;data);
  25.                 DHTFloat2String(tDht11, ((9/5) * data.temperature)+32);
  26.                 DHTFloat2String(hDht11, data.humidity);
  27.                 break;
  28.             case 4: //Read BMP085 Temperature and pressure Sensor
  29.                 temperature = BMP180_GetTemperature();
  30.                 pressure = BMP180_GetPressure(OSS_0);
  31.                 os_sprintf(pBmp085,"%ld.%01d", pressure/3386,(pressure%3386)/1000);
  32.                 os_sprintf(tBmp085,"%ld.%01d", ((temperature*18)/100) + 32,(temperature*18)%100);
  33.                 os_sprintf(aBmp085,"%03d", 328 * (BMP180_CalcAltitude(pressure)/100000));
  34.                 break;
  35.             default:
  36.                 break;
  37.       }
  38. }

Want the code for this framework? Please feel free to use and modify it for your own custom requirements. It is available on Github here.

Testing the Code

Let’s test the dual server by sending a command over the serial port and via http GET. The command will retrieve sensors values read by the ESP8266. For this test case, the 1 second loop() function has been modified as follows to populate the sensor variables without actually reading any sensors.

  1. LOCAL void ICACHE_FLASH_ATTR loop_cb(void *arg)
  2. {
  3.     //-----------------------------------------------------
  4.     //This test code populates the variables associated
  5. //with the sensor values in lieu of actually reading
  6. //sensors. This code is used to test the functionality
  7. //of the dual server application. All variables are
  8. //set with fixed values except iTinside, which is
  9. // incremented by 1.5 degrees each time this function
  10. //is called.
  11.     //-----------------------------------------------------
  12. if(tI&lt;800) {
  13. tI += 15;
  14. }
  15. else {
  16. tI = 600;
  17. }
  18. os_sprintf(tInside,"%d.%d",tI/10,tI%10); //Sensor 1
  19. os_sprintf(tOutside,"%s","79.2"); //Sensor 2
  20. os_sprintf(tAttic,"%s","88.5"); //Sensor 3
  21. os_sprintf(tDht11,"%s","69.1"); //Sensor 4
  22. os_sprintf(hDht11,"%s","34.7"); //Sensor 5
  23. os_sprintf(pBmp085,"%s","29.7"); //Sensor 6
  24. os_sprintf(tBmp085,"%s","71.1"); //Sensor 7
  25. os_sprintf(aBmp085,"%s","555.0"); //Sensor 8
  26. }

AT Serial Server Test

The added function in this demo application to the AT command set is “AT+GETSENSOR=n”. “n” is filled in with an integer value representing the sensor number. Enter the following to test this new command:

Sensor Serial Port Command Expected Reply
Inside Temperature AT+GETSENSOR=1 increasing value in range 60-80 F
Outside Temperature AT+GETSENSOR=2 79.2
Attic Temperature AT+GETSENSOR=3 88.5
DHT11 Temperature AT+GETSENSOR=4 69.1
DHT11 Humidity AT+GETSENSOR=5 34.7
BMP085 Pressure AT+GETSENSOR=6 29.7
BMP085 Temperature AT+GETSENSOR=7 71.1
BMP085 Altitude AT+GETSENSOR=8 550.0

Web Server Test

The web server can easily be tested by entering a single URL in any web browser. In this demo application, the IP is hard-coded to “” and the server responds to port 9703 requests. Thus, the test URL is:

And the expected reply will be the following JSON string:

  1. {
  2. "B_Pressure":"29.7",
  3. "B_Temperature":"71.1",
  4. "B_Altitude":"555.0",
  5. "DS_TempInside":"79.5", &lt;--NOTE: This value will range from 60-80 F in 1.5 degree increments
  6. "DS_TempOutside":"79.2",
  7. "DS_TempAttic":"88.5",
  8. "DH_Humidity":"34.7",
  9. "DH_Temperature":"69.1"
  10. }

Your demo application is working properly if the expected responses are observed.

In Closing

This opens up new possibilities. With this structure, you use the ESP8266 as an Arduino serial to WIFI shield. Yet at the same time use this same ESP8266 as a web server. And a sensor acquisition engine. And to control “things”. All you need to do is add meat to the bones provided here. What will you come up with?

I hope you find this information useful…


Share This:
Social tagging: > >

13 Responses to ESP8266 Dual AT & Web Server Mode

  1. Ray Ellam says:

    Refreshing 🙂 iv been looking around for a couple of weeks now for something similar to what you have done. it still falls short of my requirement which are.

    1. Web configuration of the username/password so you can connect to your router, this will also show the IP address once it has connected, similar to the ESP-Link interface.
    2. A reset button on an ESP i/o port that will get you back to a default IP, similar to how a router works, press for 5 seconds and it resets to and in STA+AP mode.
    3. pre compiled binaries so i can use it.

    Im willing to make a small donation if this is possible?

    Keep up the good work

    • facebook-profile-picture InternetOfHomeThings says:

      Thanks for your comment Ray,

      Have you seen this:

      I think it is close to what you are seeking. In this application, if a WiFi connection is not successful within 10 seconds of the ESP8266 power-up, it switches to AP mode with an IP of (or whatever AP IP you have last stored in EEPROM). This allows a device (computer,tablet,phone) to connect to the ESP and configure the UN/PW/IP as needed via a web browser. This application also supports a serial interface that is assumed to be an Arduino, but it could be any serial connected device.

      And as for your exact requirements, it is certainly possible. Here is what I would do.

      1. Merge the AT command interface with the Web Configuration code.
      2. Add a display of the current IP setting to the configuration page.
      3. Add a digital input callback to detect a button press with a 5 sec timeout. Then revert the IP & mode & reset the ESP when button is released.

      Since you indicated you were looking for a binary, it sounds like you did not want to tackle this yourself.

      I am around 10 days away from fitting this effort into my queue.


      • Ray Ellam says:

        Hi Dave,
        Thanks for the reply. The triple server front end looks interesting but i do like the very simple functionality of the AT command set. My main requirements are the ability to have a transparent serial port between the attached MCU and some remote location and the ability to get/put data to a remote server and the AT command set has worked fine for me in these requirements.

        “Since you indicated you were looking for a binary, it sounds like you did not want to tackle this yourself”

        I am quite busy doing other things at the moment and the thought of climbing a very steep hill (the ESP SDK) does not really appeal to me at the moment 🙂

        Hows about i paypal you a little beer money and you compile me a binary of the dual server without the web front end so i can play with it at my end. I believe the UN+PW+IP are hard coded? so just make those user, password,

        Waddya say Dave?


      • Ray Ellam says:

        Hi Dave,
        I know this goes back a while. How much will it cost if you could build me a dual server with the requirements detailed above?


  2. Ray Ellam says:

    Hi Dave,
    I found the demo binary flashed an ESP12E, set up a router the the hard coded credentials and now i can play 🙂 I will get back to you.

  3. mi_ka says:

    This project sounds interesting. I wonder if you could add support for over-the-air ESP firmware updates to the web server, via a special HTTP POST such as “http://$hostname/flash/upload”, as in the esp-link firmware (cgiflash.c at

    • facebook-profile-picture InternetOfHomeThings says:

      That’s a great idea and I do have OTA updates planned. I’m just way behind on my plans!

    • Don says:

      For all its perceived limitations, that is one advantage of the NodeMCU firmware; just add a few lines of telnet server, set up a dummy serial port redirected to tcp so that Esplorer doesn’t know any better, and wahla! as the French say. OTA reprogramming! – here’s the blog post that details it – and by substituting the Windows port redirection with the socat utility, it works under linux, too.

    • Star says:

      Drupal is very powerful. I think, if some one do;8#&217nt mind to make their hands dirty by trniyg to modify or write new code, Drupal is the best to create web pages.

  4. T johnson says:

    do you have some better instructions on compiling this because in my tool chain it is riddled with errors.. and the instructions that come with it are pretty light on information, for example it is missing something in a folder called third party, some lwipopts… then if you find a version of that but i have no way of telling what version would be a right version you get :

    at_wifiCmd.c:104: undefined reference to `os_str2macaddr'
    build/app_app.a(at_wifiCmd.o): In function `at_setupCmdCwmode'

    • facebook-profile-picture InternetOfHomeThings says:

      These are the instructions I used when setting up the EspressIF compiler. It uses the Eclipse IDE on a Window 10 PC.

      It has been a while since I fired the environment up as most folks prefer to develop ESP8266 applications using the Arduino IDE. And I have to cater to my audience. But I just opened it up and recompiled the project noted in this post. It completed without any errors. The EspressIF SDK using the Eclipse IDE is a great development environment and gives you great control over the low level functions.

      Since my initial installation, I had updated the SDK to v1.1, but I suspect a much newer version exists today.

    • facebook-profile-picture InternetOfHomeThings says:

      I just upgraded to ESP8266_NONOS_SDK_V2.0.0_16_07_19 and now have been able to duplicate your compile errors.

      I will let you know what I find.

      This has been a constant problem. Updated SDK versions are not backward compatible!

    • facebook-profile-picture InternetOfHomeThings says:

      I was able to fix the compile error and it now builds correctly.

      1. Used ESP8266_NONOS_SDK_V2.0.0_16_07_19
      2. The /include/osapi.h file was missing the following line. Add it and the project should compile:

      #define os_str2macaddr ets_str2macaddr

Leave a Reply

Press Ctrl+C to copy the following code.