Raspberry Pi WiFi With The ESP8266
Written by Harry Fairhead   
Monday, 12 September 2016
Article Index
Raspberry Pi WiFi With The ESP8266
AT Commands
Some Utility Functions
Getting A Web Page
A Web Server
Listing

A Web Server

The most common use for an internet connection on a small device like the Pi is to allow another device to request data. It is fairly easy to create a web server running on the ESP8266, but don't expect Apache or anything advanced. All you can reasonably do is accept a connection and send a web page or two back to the client. 

The key differences between client and server mode is that in server mode the device constantly "listening" for clients to make TCP connections on the port.

When the device receives a connection it reads all of the data the client sends and passes it on via the serial port to the the Pi. This means that in server mode the Pi has to be constantly on the lookout for new data from the ESP8266. You can do this using an interrupt, but for simplicity this example uses a polling loop.

There is another difference between client mode and server mode - there can be multiple TCP connections from as many clients as try to connect. The solution to this problem is that the ESP8226 assigns each TCP socket connection an id number and this is what you need to use to make sure you send the data to the right place.

Let's see how it all works.

Assuming we are already connected to WiFi and have an IP address, we can set up a server quite easily. First we need to use the command CIPMUX =1 to set the device into multiple connection mode. You cannot start a server if CIPMUX=0, the default single connection mode. Once multiple connections are allowed you can create a server using CIPSERVER=1,port.

In this example we are using port 80. the standard HTTP port. but you can change this to anything you want:

int startServerWiFi() {
 char temp[blocksize];
 char id[10];
 dprintf(sfd, "AT+CIPMUX=1\r\n");
 if (getBlocks(10, "OK") < 0) return -1;
 dprintf(sfd, "AT+CIPSERVER=1,80\r\n");
 if (getBlocks(10, "OK") < 0) return -1;

If you run just this part of the program you will see the response:

AT+CIPMUX=1
OK
AT+CIPSERVER=1,80
no change
OK

Now we just have to wait for a client to make a connection and send some data.  This is done simply by reading the serial input and checking for "+IPD" in an infinite polling loop:

for (;;) {
 if (getBlocks(1, "+IPD") < 0)continue;

If we don't get a block containing "+IPD" we simply move on to the next iteration. There should be a call to nanosleep just before we do getBlocks to reduce the load on the processor - this is particularly important if you are running this on a single core Pi Zero. 

If we have got a block with "+IPD" the body of the loop processes it. As this is c string processing it is not elegant. The received data has the format:

+IPD,id, rest of data

We now need to extract the id so we can use it to communicate with the client. This is just some standard string handling, but it is still messy:

char *b = strstr(buf, "+IPD");
b += 5;
strncpy(temp, b, sizeof (temp));
char *e = strstr(temp, ",");
int d = e - temp;
memset(id, '\0', sizeof (id));
strncpy(id, temp, d);

The algorithm is:

  • Find "+IPD" and trim the string to remove it and the comma
  • Find the next comma and extract the characters from the start of the string to the next comma. 

Now we have the id we can communicate with the client, but first we need something to send. As with all HTTP transactions, we have to send some headers and then some data. There are a lot of possible headers you could send, but a reasonable minimum that works with most browsers is:

char data[] = "HTTP/1.0 200 OK\r\n
               Server: Pi\r\n
               Content-type: text/html\r\n\r\n
       <html><head><title>Temperature</title></head>
       <body><p>
         {\"humidity\":81%,\"airtemperature\":23.5C}  
       </p></body></html>\r\n";

Of course, in a real application, the HTML part of the data would be generated by the program or read from a file. You can include time stamps and lots of other useful information, but this is simple and it works. Notice the blank line at the end of the headers - this is vital. The browser will ignore everything sent to it if you don't have a blank line at the end of the headers.

Now we want to send the data to the client. This is just a matter of using the CIPSEND command again, only this time with the id specified as the first parameter:

CIPSEND=id,data length

and we wait for the response ">" before sending the data: Notice that we don't have to open a TCP socket as we did in the case of acting as a client. The TCP socket has already been opened by the client connecting to the server and when the transaction is complete we can close it. 

dprintf(sfd, "AT+CIPSEND=%s,%d\r\n", id, strlen(data));
if (getBlocks(10, ">") < 0) return -1;

Now, at last, we can send the data to the client:

dprintf(sfd, "%s", data);
if (getBlocks(10, "OK") < 0) return -1;

and wait for it to complete.

Finally we close the connection and complete the loop to wait for another connection:

  dprintf(sfd, "AT+CIPCLOSE=%s\r\n", id);
  if (getBlocks(10, "OK") < 0) return -1;
 }
}

If you now try it out with a main program something like:

int main() {
 initWiFi();
 modeWiFi(1);
 connectWiFi("ssid","pass");
 getIPWiFi();
 startServerWiFi();
}

you should now be able to connect to the IP address that is displayed and retrieve the web page that displays:

{"humidity":81%,"airtemperature":23.5C}

Where Next?

There are a lot of WiFi commands that haven't been covered here, but now that you have seen examples of most of the basic types and encountered the typical problems that occur you should be able to implement any that you need. 

There is still going to be the occasional unexplained crash and in this case the best solution is to use the soft reset command. It is also worth mentioning that the server program as presented does not actually handle multiple simultaneous connections. It has to finish dealing with one connection before it can deal with a second. If you want to do this you need to create a new thread every time that a connection is made or you need to implement an event queue so that you can deal with each request in turn. 

 



Last Updated ( Monday, 12 September 2016 )