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

AT Commands

The key idea in using the ESP8266 is that the Pi has to send an AT command, literally the characters AT, followed by other command strings. The command has to end with \r\n for the ESP8266 to take notice of it. 

You can find a fill list of commands at the Espressif web site, but the most important are: 

AT - Attention
AT+RST - Reset the board
AT+GMR - Firmware version
AT+CWMODE* - Operating Mode 1. Client 2. Access Point 3. Client and Access Point
AT+CWJAP=, - Join network
AT+CWLAP - View available networks
AT+CWQAP - Disconnect from network
AT+CIPSTATUS - Show current status as socket client or server
AT+CIPSTART=,, - Connect to socket server
AT+CIPCLOSE - Close socket connection
AT+CIFSR - Show assigned IP address when connected to network
AT+CIPMUX= - Set connection 0. Single Connection 1. Multi-Channel Connection
AT+CIPSERVER=Open the Socket Server
AT+CIPMODE= - Set transparent mode
+IPD,,format
AT+CIPSTO=

This is just a very general overview and omits the commands that allow the device to work as an access point. It is assumed that client mode is the more common application, but it isn't difficult to extend this example to access point operation.

Setup

We need a function to set up the hardware ready to communicate with the device:

int initWiFi() {
 system("sudo systemctl stop
          serial-getty@ttyAMA0.service");

 sfd = open("/dev/serial0", O_RDWR | O_NOCTTY);
 if (sfd == -1) {
  printf("Error no is : %d\n", errno);
  printf("Error description is : %s\n", strerror(errno));
  return -1;
 }
 struct termios options;
 tcgetattr(sfd, &options);
 cfsetspeed(&options, B115200);
 cfmakeraw(&options);
 options.c_cflag &= ~CSTOPB;
 options.c_cflag |= CLOCAL;
 options.c_cflag |= CREAD;
 options.c_cc[VTIME] = 1;
 options.c_cc[VMIN] = blocksize;
 tcsetattr(sfd, TCSANOW, &options);
};

 

The model of ESP8266 used worked at 115200 baud by default. Newer models and updated firmware are reported to work at 9600 baud. If this is the case you need to modify the data rate and/or change the ESP8266's baud rate. 

The read mode is set to a tenth of a second timeout between characters and a minimum read of 511 characters if there are no timeouts, the maximum. The plan for the rest of the functions is to use dprintf to send data and a blocking read that times out when the device has sent a burst of data. If you want to know more about the way the serial interface is set up then see: Serial C And The Raspberry Pi

Attention!

To start with the easiest, but possibly least useful, command let's implement some functions that test that we have a connection to the device - always a good thing to start with. 

In this section we develop the first of a number of functions that send a command to the device and get back a response. To do this we need a few other functions to help and these are reused in other AT functions. Once you have one AT function the rest are very similar, so it is worth spending some time following how this most simple AT function works.

If you send the string AT\r\n then the ESP8266 replies with a single "OK". Not much but it proves that the serial interface is working and there isn't much point in moving on until you have tested that AT works.

The most basic  AT function is simply:

int ATWiFi() {

  dprintf(sfd, "AT\r\n");
  return 0;
}

This is the most basic form of function that will do the job but it isn't really practical. We need to check that it works and we need to get some feedback. 

Now all we need to do is read the data sent back from the device and it makes sense to create a function that reads a block of data.  It uses a blocking read to get as much data from the device as possible. The timeout is 0.1 seconds, which is enough time for more than 500 characters at 115200 baud and 100 at 9600 baud.  Typical responses are a few hundred bytes, so using a buffer 512 bytes in size is reasonable.

The following function attempts to read a block of data from the device:

int getBlock() {
 int bytes;
 struct timespec pause;
 pause.tv_sec = 0;
 pause.tv_nsec = 100000000; 
 nanosleep(&pause, NULL);
 memset(buf, '\0', sizeof (buf));
 ioctl(sfd, FIONREAD, &bytes);
 if (bytes == 0)return 0;
 int count = read(sfd, buf, blocksize - 1);
 buf[count] = 0;
 if (DEBUG) {
  printf("%s", buf);
  fflush(stdout);
 }
 return count;
}

It first uses nanosleep to pause for 0.1 seconds and then it checks to see if there is any data in the serial buffer. If there isn't it returns without waiting - this means that there is an inter-character timeout of 0.1 second even if there are no characters to be read. Next the read function attempts to read a full buffer of data. Notice that there might not be a full buffer when the call is made, but as long as the data is coming in with gaps shorter than 0.1 second between characters it will continue reading until the buffer is full. 

Notice that a zero byte is added to the end of the buffer so that it can be treated like a valid C string. 

If the AT command has been successful it returns a string containing "OK". One of the problems of working with AT commands is that they don't have an exact syntax and there is no formal "end of message" signal. By observation, any successful command does end with "OK" and if you can't find "OK" in the response then you either haven't read it all or there has been an error. 

With all of this we can now finish the ATWiFi function:

int ATWiFi() {
 dprintf(sfd, "AT\r\n");
 return getBlock();
}

The return value is the number of bytes read which could be zero. If the constant DEBUG is a 1 then the string is printed to stdoout and you can examine it. 

Put all this together with a main program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <termios.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <time.h>

#define blocksize 255
#define DEBUG 1

int sfd;

char buf[blocksize];
int initWiFi();
int ATWiFi();
int getBlock();

int main(int argc, char** argv) {  initWiFi();
 ATWiFi();
 return (EXIT_SUCCESS);
}

 

int initWiFi() {
system("sudo systemctl stop
         serial-getty@ttyAMA0.service")
;

 sfd = open("/dev/serial0", O_RDWR | O_NOCTTY);

 if (sfd == -1) {
  printf("Error no is : %d\n", errno);
  printf("Error description is : %s\n", strerror(errno));
  return -1;
 }
 struct termios options;
 tcgetattr(sfd, &options);
cfsetspeed(&options, B115200);
 cfmakeraw(&options);
 options.c_cflag &= ~CSTOPB;
 options.c_cflag |= CLOCAL;
 options.c_cflag |= CREAD;
 options.c_cc[VTIME] = 1;
 options.c_cc[VMIN] = blocksize;
 tcsetattr(sfd, TCSANOW, &options);
};

 

int getBlock() {
 int bytes;
 struct timespec pause;
 pause.tv_sec = 0;
 pause.tv_nsec = 100000000;
 nanosleep(&pause, NULL);
 memset(buf, '\0', sizeof (buf));
 ioctl(sfd, FIONREAD, &bytes);
 if (bytes == 0)return 0;
 int count = read(sfd, buf, blocksize - 1);
 buf[count] = 0;
 if (DEBUG) {
  printf("%s", buf);
  fflush(stdout);
 }
 return count;
}

int ATWiFi() {dprintf(sfd, "AT\r\n");
 return getBlock();
}

When you run this program then you should see: 

AT

OK

printed on the serial console. If you don't there are five possible reasons:

  1. You have connected the wrong pins - check
  2. The power supply you are using is inadequate - check/replace
  3. The serial console isn't working - check you can see any message sent to it
  4. The baud rate is wrong - try 9600
  5. The ESP8266 is broken - try another

 



Last Updated ( Monday, 12 September 2016 )