ESP how-to code

In this How-To I will cover most of the building blocks you need for using the ESP with the Arduino IDE. I am using v1.6.7.

The Arduino IDE has been extended to support the ESP chips. This is a very simple C++ like environment that allows very quick code development and provides a very simple environment to work in.

These great little stand alone processors are capable of lots of processing, can control varied hardware and have program memory of up to 4MBytes so more than enough to store some quite elaborate code. Below are some examples of what can be done. 4 different types of communications with Linux (I used a RPI), A/D input, PWM output, GPIO, switch debouncing, I2C interface, interrupts, timers and so on!!!

Also included is a bit on how to read a file from the Flash file system and serve it as a picture in your website

Communications with Linux/RPI

This Code implements all 4 communications methods with a Linux machine (in this case an RPI):

  1. Push from ESP to Linux – here the ESP forces information onto the Linux machine when it needs to say something quickly. My example is for a remote doorbell.
  2. Push from Linux to ESP – again, when the Linux machine has something it needs to say to the ESP urgently.
  3. Pull from ESP to Linux – Here the ESP will have some data ready for the RPI but it will only send that data (as a web page) when the RPI asks for it. This is good for status messages where the RPI might want to periodically check up on the state of the ESP or get some non time critical information.
  4. Finally, Pull from Linux to ESP – where the RPI has some info that the ESP can read whenever it wants it. Useful if the ESP  is interested in the status of the Linux machine.

This first snippet is the ESP code:

 #include <ESP8266WiFi.h>  
 #include <ESP8266WebServer.h>  
   
 #define SSID   "PUT YOUR WIFI SSID HERE"  
 #define PASSWORD "PUT YOUR PASSWORD HERE"  
   
 int blind[6]={0,0,0,0,0,0};  
 int window[6]={0,0,0,0,0,0};  
 int doorbell=0;  
 int t=0;  
   
 ESP8266WebServer server(80);  
   
 /*------------------------------------------------------------------------*/  
 /* When the client asks for the /status web page this is what it will get */  
 /* use this when it is a hum reading the page on a web browser or similar */  
 /*------------------------------------------------------------------------*/  
 void handle_status()  
 {  
   char status_html[1000];  
   char page_data[]=   
     "<head></head>"  
     "<body>"  
     "<h1>Holy Mill Hall Controller</h1>"  
     "<p>Catslide Window 1 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "<p>Catslide Window 2 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "<p>Catslide Window 3 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "<p>Catslide Window 4 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "<p>Catslide Window 5 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "<p>Catslide Window 6 is <b>%s</b>, and the shutter is <b>%s</b></p>"  
     "</body>";    
   sprintf(status_html,page_data,   
       (window[0]==1) ? "Open" : "Closed", (blind[0]==0) ? "Up" : (blind[0]==1) ? "Half Way" : "Down",  
       (window[1]==1) ? "Open" : "Closed", (blind[1]==0) ? "Up" : (blind[1]==1) ? "Half Way" : "Down",  
       (window[2]==1) ? "Open" : "Closed", (blind[2]==0) ? "Up" : (blind[2]==1) ? "Half Way" : "Down",  
       (window[3]==1) ? "Open" : "Closed", (blind[3]==0) ? "Up" : (blind[3]==1) ? "Half Way" : "Down",  
       (window[4]==1) ? "Open" : "Closed", (blind[4]==0) ? "Up" : (blind[4]==1) ? "Half Way" : "Down",  
       (window[5]==1) ? "Open" : "Closed", (blind[5]==0) ? "Up" : (blind[5]==1) ? "Half Way" : "Down");  
   server.send(200, "text/html", status_html);  
 }  
 /* ----------------------------------------------------------------------*/  
 /* This is what the /status_internal request will give it, the same data */  
 /* but in a more machine readable format. Use this when the client is  */  
 /* asking for data it can use in a program                */  
 /* ----------------------------------------------------------------------*/  
 void handle_status_internal()  
 {  
   char status_internal_html[100];  
   char page_data[]=   
     "<head></head>"  
     "<body>"  
     "##1=%i,%i,##2=%i,%i,##3=%i,%i,##4=%i,%i,##5=%i,%i,##6=%i,%i"  
     "</body>";    
   sprintf(status_internal_html,page_data,   
        window[0],blind[0],  
        window[1],blind[1],  
        window[2],blind[2],  
        window[3],blind[3],  
        window[4],blind[4],  
        window[5],blind[5]);  
   server.send(200, "text/html", status_internal_html);  
 }  
   
 /*----------------------------------------------------------------------------------------*/  
 /* This code will accept data from a client that is pushing it to us (the server). It   */  
 /* comes in as arguments from the URL of the form  192.168.1.17/update?blind=1&setting=2 */  
 /* so the client is teeling us 2 bits of data: a blind number and a setting number    */  
 /* we can also send in window=3&setting=0 for example to change window settigns     */  
 /* Once you have the data you can do something with it such as open windows or set blinds */  
 /*----------------------------------------------------------------------------------------*/  
 void handle_update()  
 {  
   if (server.args() >0) {  
     if ((server.argName(0)=="blind") && (server.argName(1)=="setting")) {  
       Serial.println("Setting Blinds up now");  
       blind[(server.arg("blind").toInt())]=server.arg("setting").toInt();  
     } else if ((server.argName(0)=="window") && (server.argName(1)=="setting")) {  
       Serial.println("Setting Window up now");  
       window[(server.arg("window").toInt())]=server.arg("setting").toInt();  
     }  
     server.send(200, "text/html", "OK - it worked");  
   }  
 }  
 /*---------------------------------------------------------------------------------------------*/  
 /* This is the normal Arduino setup() routine, runs once at the start of the program execution */  
 /*---------------------------------------------------------------------------------------------*/  
 void setup(void)  
 {  
   /*----------------------------------------------*/  
   /* This bit sets up the link to my WiFi network */  
   /*----------------------------------------------*/  
   Serial.begin(115200);  
   Serial.println("starting up wifi");  
   WiFi.begin(SSID, PASSWORD);  
    
   /* wait for connection to SSID to be established */  
   while (WiFi.status() != WL_CONNECTED) {  
    delay(500);  
    Serial.print(".");  
   }  
   
   /*-----------------------------------------------------------------------------------------------------*/  
   /* This tells the web server which of the above routines to execute when specified pages are asked for */  
   /*-----------------------------------------------------------------------------------------------------*/  
   /* set up the dispatcher for endpoints */  
   server.on("/status", handle_status);  
   server.on("/update", handle_update);  
   server.on("/status_internal", handle_status_internal);  
   server.begin();  
       
   /* print to serial data about the connection */  
   Serial.print("SSID: ");  
   Serial.println(SSID);  
   Serial.println(WiFi.localIP());  
   Serial.println("WebServer ready:");  
 }  
   
 /*------------------------------------------------------------------*/  
 /* This is normal Arduino loopo() routine, runs over and over again */  
 /*------------------------------------------------------------------*/  
 void loop(void) {  
    
   /* tell the ESP that it should always be handling requests for pages as defined in the setup() above */  
   server.handleClient();  
   if ((t++)<1000) return;  
   t=0;  
   /*---------------------------------------------------------------------*/  
   /* This bit will read a bit of data from a client webserver      */  
   /* use this when you want to get data from another web serving machine */  
   /*---------------------------------------------------------------------*/  
   WiFiClient client;  
   const int httpPort = 80;  
   
   if (!client.connect(IPAddress(192,168,1,10), httpPort)) {  
     Serial.println("connection failed");  
     return;  
   }  
   
   String url = "GET /trial1.php HTTP/1.1";  
   client.println(url);  
   client.println("Host: 192.168.1.17");  
   client.println("Connection: close");  
   client.println();  
   delay(500);  
    
   const char *line;  
   char *posn;  
   float reading;  
     
   /* read back the page we have asked for */  
   while(client.available()){  
     line = client.readStringUntil('\r').c_str();  
     posn = strstr(line,"reading is:");  
     if (posn) { /* we have found the string we are looking for */  
       posn+=strlen("reading is:");  
       reading=atof(posn); /* convert text to a float from this poition */  
       Serial.print("Got a reading of: ");  
       Serial.println(reading);  
     }  
   }  
   
   client.stop(); /* all done now so let the client go */  
   
   /*---------------------------------------------------------------------*/  
   /* This bit will send some data to the client machine         */  
   /* use this when you want to push some data onto the client      */  
   /* eg tell it the doorbell has been pushed              */  
   /*---------------------------------------------------------------------*/  
   
   if (!client.connect(IPAddress(192,168,1,10), httpPort)) {  
     Serial.println("connection failed");  
     return;  
   }  
   String data = "doorbell=" + (String)doorbell;   
   url = "GET /trial.php?"+ data + "HTTP/1.1";  
   client.println(url);  
   client.println("Host: 192.168.1.17");  
   client.println("Connection: close");  
   client.println();  
   delay(500);  
    
   /* read back the page we have asked for */  
   while(client.available()){  
     line = client.readStringUntil('\r').c_str();  
  //    Serial.println(line);  
   }  
   client.stop(); /* all done now so let the client go */  
   
   /* this is just to make the above data push show something interesting at the other end */  
   doorbell++;  
 }  
   

This next snippet is to run on the RPI web server so these files should be put into the /var/www/ folder once you have installed the RPI apache server. This first php file implements the ESP push to the RPI:

 <html>  
 <head>  
 </head>  
 <body>  
 <?php  
   
  const SHMKEY=1234;  
   
  $doorbell=intval($_GET["doorbell"]);  
   
  $shm_id = shmop_open(SHMKEY, "w", 0600, 1024);  
  if (!$shm_id)  
  {  
    echo "Couldn't create shared memory segment\n";  
  } else  
  {  
    // Get shared memory block's size if we need it  
    $shm_size = shmop_size($shm_id);  
    shmop_write($shm_id, chr($doorbell%256), 0);  
  }  
  echo "Thanks for sending me ";  
  echo $doorbell;  
  echo "\n";  
 ?>  
 </body>  
 </html>  
   

And this also lives in /var/www. It implements the page for when the ESP just wants to pull some data from the RPI:

 <head>  
 </head>  
 <body>  
 <?php  
   
  const SHMKEY=1234;  
  const READING_OFFSET=1;  
   
  $shm_id = shmop_open(SHMKEY, "w", 0600, 1024);  
  if (!$shm_id)  
  {  
    echo "Couldn't create shared memory segment\n";  
  } else  
  {  
    // Get shared memory block's size if we need it  
    $shm_size = shmop_size($shm_id);  
  }  
  echo "the reading is:";  
  echo ord(shmop_read($shm_id, READING_OFFSET,1));  
  echo "\n";  
 ?>  
   
 </body>  
   

And this is the C code that communicates with the PHP above and also direct with the ESP

 #include <netinet/in.h>  
 #include <sys/types.h>  
 #include <sys/socket.h>  
 #define _BSD_SOURCE  
 #include <sys/ipc.h>  
 #include <stddef.h>  
 #include <sys/shm.h>  
   
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <unistd.h>  
 #include <string.h>  
 #include <stdarg.h>  
 #include <stdint.h>  
 #include <signal.h>  
 #include <time.h>  
 #include <sys/stat.h>  
 #include <fcntl.h>  
 #include <sys/mman.h>  
   
 #define DOORBELL_OFFSET 0  
 #define READING_OFFSET  1  
 #define SHMKEY 1234  
   
 static char *shmpointer;  
 static int shmid;  
   
 int shared_mem_setup()  
 {  
   struct shmid_ds shmbuffer;  
   int segment_size;  
     int i;  
   
   shmid = shmget(SHMKEY, 1024, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);  
   printf ("shmid = %i\n",shmid);  
   
   shmpointer = (char *)shmat(shmid, NULL, 0);  
   
   shmctl(shmid, IPC_STAT, &shmbuffer);  
   segment_size = shmbuffer.shm_segsz;  
   printf ("segment size: %d\n", segment_size);  
   
   for (i=0; i<10; i++) shmpointer[i]=7;  
   
   return 0;  
 }  
   
 void setWebpage(char *buffer, int bufsize, char *ipaddress, int code, int setting)  
 {  
   int sockfd;  
   struct sockaddr_in destAddr;  
   
   if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1){  
     printf("Error opening client socket\n");  
     close(sockfd);  
     return;  
   }  
   
   destAddr.sin_family = PF_INET;  
   destAddr.sin_port = htons(80); // HTTP port is 80  
   destAddr.sin_addr.s_addr = inet_addr(ipaddress); // Get int representation of IP  
   memset(&(destAddr.sin_zero), 0, 8);  
   
   if(connect(sockfd, (struct sockaddr *)&destAddr, sizeof(struct sockaddr)) == -1){  
     printf("Error with client connecting to server\n");  
     close(sockfd);  
     return;  
   }  
   /* Send http request - this needs careful formatting */  
   
 //  char *httprequest = "GET /status_internal HTTP/1.1\r\nHost:10.10.100.254\r\nAuthorization:Basic YWRtaW46YWRtaW4=\r\nUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0\r\n\r\n";   
   char *http_data = "GET /update?window=%i&setting=%i HTTP/1.1\r\nHost:10.10.100.254\r\nAuthorization:Basic YWRtaW46YWRtaW4=\r\nUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0\r\n\r\n";   
 char httprequest[1000];  
   sprintf(httprequest,http_data,code,setting);  
   if(send(sockfd, httprequest, strlen(httprequest), 0)==-1) {  
     printf("Error with send\n");  
     close(sockfd);  
     return;  
   }  
   /* seems this flag WAITALL is needed for some slow things like my NAS */  
   recv(sockfd, buffer, bufsize, MSG_WAITALL);  
   close(sockfd);  
 }  
   
 void getWebpage(char *buffer, int bufsize, char *ipaddress)  
 {  
   int sockfd;  
   struct sockaddr_in destAddr;  
   
   if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1){  
     printf("Error opening client socket\n");  
     close(sockfd);  
     return;  
   }  
   
   destAddr.sin_family = PF_INET;  
   destAddr.sin_port = htons(80); // HTTP port is 80  
   destAddr.sin_addr.s_addr = inet_addr(ipaddress); // Get int representation of IP  
   memset(&(destAddr.sin_zero), 0, 8);  
   
   if(connect(sockfd, (struct sockaddr *)&destAddr, sizeof(struct sockaddr)) == -1){  
     printf("Error with client connecting to server\n");  
     close(sockfd);  
     return;  
   }  
   /* Send http request - this needs careful formatting */  
   
   char *httprequest = "GET /status_internal HTTP/1.1\r\nHost:10.10.100.254\r\nAuthorization:Basic YWRtaW46YWRtaW4=\r\nUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.5.0\r\n\r\n";   
   
   if(send(sockfd, httprequest, strlen(httprequest), 0)==-1) {  
     printf("Error with send\n");  
     close(sockfd);  
     return;  
   }  
   /* seems this flag WAITALL is needed for some slow things like my NAS */  
   recv(sockfd, buffer, bufsize, MSG_WAITALL);  
   close(sockfd);  
 }  
   
 int main(int argc, char **argv)  
 {  
   shared_mem_setup();  
   int old_val=1;  
   char out_reading=0;  
   int timer=0;  
   int timer2=0;  
   int i;  
   char buffer[1000];  
   char *posn;  
   int in_reading;  
   
   while (1)  
   {  
     if (shmpointer[DOORBELL_OFFSET]!=old_val)  
     {  
       old_val=shmpointer[DOORBELL_OFFSET];  
       printf("Value has changed to %i\n",old_val);  
     }  
     if (timer++==30000)   
     {  
       timer=0;  
       getWebpage(buffer, 1000, "192.168.1.17");  
       posn=strstr(buffer,"##1=");  
       posn+=strlen("##2=");  
       in_reading=atoi(posn);  
       printf("I just read from ESP Window 1: %i\n",in_reading);  
     }  
     if (timer2++==70000)   
     {  
       timer2=0;  
       printf("sending update request for %i\n",timer%23);  
       setWebpage(buffer, 1000, "192.168.1.17",0,timer%23);  
     }  
     shmpointer[READING_OFFSET]=out_reading++;  
   }  
 }  

Hardware Control

This code shows how to control the hardware of the ESP chip. It includes use of timers, interrupts, GPIO read and write. reading the ADC and switch debounce.

 #include "user_interface.h"  
   
 #define LED_PIN 2  
 #define LED_OFF 0  
 #define LED_ON 1  
 #define SWITCH_PIN 0  
   
 os_timer_t timer_1_second;  
   
 void timer_1_second_callback(void *pArg) {  
   
   /* flash the LED */  
   /* note GPIO can source 12mA and sink 20mA */  
   /* this can be for all 16 pins = 16 *12mA total */  
   static int led=LED_OFF;  
   if (led==LED_ON)  
     led=LED_OFF;  
   else  
     led=LED_ON;  
   digitalWrite(LED_PIN, led);  
     
   /* Now read the ADC */  
   int adc_input;  
   adc_input = analogRead(A0);  
   Serial.print("ADC reading was: ");  
   Serial.println(adc_input);  
   
   
   /* also read the digital input pin */  
   int switch_state;  
   switch_state=digitalRead(SWITCH_PIN);  
   Serial.print("Switch state is: ");  
   Serial.println(switch_state);  
 }   
   
 volatile int switch_pressed; /* used outside of the ISR so must be volatile */  
 volatile int switch_pressed_time=0;  
   
 void switch_pressed_isr() {  
   /* debounce the switch by saying it can't be pressed more than 5 times a second */  
   if ((millis()-switch_pressed_time)<200) return;  
   switch_pressed_time=millis();  
   switch_pressed=1;  
 }  
   
 void setup() {  
   Serial.begin(115200);  
   os_timer_setfn(&timer_1_second, timer_1_second_callback, NULL);  
   os_timer_arm(&timer_1_second, 1000, true);  
   
   pinMode(LED_PIN, OUTPUT);  
   pinMode(SWITCH_PIN, INPUT);  
   
   attachInterrupt(digitalPinToInterrupt(SWITCH_PIN), switch_pressed_isr, FALLING);  
 }  
   
 int switch_processed=0;  
   
 void loop() {  
   if (switch_pressed) {  
     if (!switch_processed) {  
        Serial.println("Switch Pressed ISR went off");  
        switch_processed=1;  
     }  
     if ((millis()-switch_pressed_time) > 200) {  
       if (digitalRead(SWITCH_PIN)==1) {  
         switch_pressed_time=millis();  
         switch_pressed=0;  
         switch_processed=0;  
       }  
     }  
   }  
 }  
   

 

PWM control

Here is code that will output a PWM signal on one of the GPIOs.

 #define LED_PIN 2  
   
 void setup() {  
   Serial.begin(115200);  
   pinMode(LED_PIN, OUTPUT);  
   /* analogWriteFreq(new_frequency)   
     analogWriteRange(new_range)  */  
 }  
   
 int x=0;  
   
 void loop() {  
   analogWrite(LED_PIN, x++);  
   delay(10);  
 }  
   

 

I2C demo code

This code demonstrated how to setup the code for communicating with an I2C device. It does a simple write to a register and a read from another register to show the general idea of how to use the wire interface. Note this is a bit bashing I2C bus – it is not native hardware and does not support data rates above 450KHz.

 #include <Wire.h>  
   
 /* Here are some definitions for using the MPU6000 chip */  
 #define MPU6000_I2C_ADDR0  0x68  
 #define MPU6000_WHO_AM_I   0x75  
 #define MPU6000_WHO_AM_I_VAL 0x68  
 #define MPU6000_PWR_MGMT_1  0x6B  
   
 /* Here we tell the program what pins we connected the I2C bus up to */  
 #define SDA_PIN 5  
 #define SCL_PIN 4  
   
 void setup() {  
   
   Serial.begin(115200);  
     
   Wire.begin(SDA_PIN, SCL_PIN);       /* setup SDA, SCL with teh I2C driver */  
   
   /* first we write to the Power management register to tell it to use it's internal clock */  
   /* this starts the chip doing stuff */  
   Wire.beginTransmission(MPU6000_I2C_ADDR0); /* we are going to work with chip at i2c_addr */  
   Wire.write(MPU6000_PWR_MGMT_1);      /* Write out the register we want to write to */   
   Wire.write(0);               /* and write the value we want to set it tock */  
   Wire.endTransmission();          /* finished writing out part of the read */  
    
   /* Next we read the ID register and print it out */  
   Wire.beginTransmission(MPU6000_I2C_ADDR0); /* we are going to work with chip at i2c_addr */  
   Wire.write(MPU6000_WHO_AM_I);       /* Write out the register we want to read from */   
   Wire.endTransmission();          /* finished the writing out part of the read */  
   Wire.requestFrom(MPU6000_I2C_ADDR0, 1);  /* request read one byte from this register */  
   if (1 <= Wire.available()) {        /* if our byte was received */  
     Serial.print("Chip ID I read was: ");  
     Serial.print(Wire.read());       /* print it out */   
     Serial.print(", it should be ");  
     Serial.println(MPU6000_WHO_AM_I_VAL);  
   } else {  
     Serial.println("Failed to receive the number of bytes we asked for");  
   }  
 }  
   
 void loop() {  
  /* nothing to do in loop */  
 }  

 

Pictures in your web pages

Here is an example of how to add a picture to your web pages by reading from the SPIFFS (SPI Flash File System). In order to get your picture into the file system you can install the ESP Sketch Data Upload plug in. A great tutorial to doing this is in:

https://github.com/esp8266/Arduino/blob/master/doc/filesystem.md

 

 #include <ESP8266WiFi.h>  
 #include <ESP8266WebServer.h>  
 #include "FS.h"  
   
 #define SSID "YOUR SSID HERE"  
 #define PASSWORD "YOUR PASSWORD HERE"  
   
 ESP8266WebServer server(80);  
 String read_amps_html =   
 "<head></head>"  
 "<body style=\"text-align:center;background-color:black\">"  
 "<h1 style=\"color:white\">Holy Mill Current Monitor</h1>"  
 "<img src=\"dragon.gif\">"  
 "<h1 style=\"color:white\">Right now we are using 123.4 Amps</h1>"  
 "</body>";  
   
 void handle_read_amps()  
 {  
   server.send(200, "text/html", read_amps_html);  
 }  
   
 void handle_dragon_gif()  
 {  
   File f=SPIFFS.open("/dragon.gif", "r");  
   if (!f) {  
     Serial.println("file open failed");  
   }  
   Serial.println(f.size());  
   String s="";  
   int a;  
   char b;  
   for (a=0;a<f.size();a++) {  
     f.read((uint8_t *)&b,1);  
     s+=(char)b;   
   }  
     
   server.send(200, "image/gif", s);  
 }  
   
 void setup(void)  
 {  
   boolean ok;  
   Serial.begin(115200);  
   Serial.println("starting up wifi");  
   WiFi.begin(SSID, PASSWORD);                 // Connect to WiFi network  
    
   ok=SPIFFS.begin();  
   if(!ok) Serial.println("Failed to begin SPIFFS");  
   
     
   /* wait for connection to SSID to be established */  
   while (WiFi.status() != WL_CONNECTED) {  
    delay(500);  
    Serial.print(".");  
   }  
   
   /* set up the dispatcher for endpoints */  
   server.on("/", handle_read_amps);   
   server.on("/dragon.gif", handle_dragon_gif);  
   server.begin();  
       
   /* print to serial data about the connection */  
   Serial.print("SSID: ");  
   Serial.println(SSID);  
   Serial.println(WiFi.localIP());  
   Serial.println("WebServer ready:");  
 }  
   
 void loop(void) {  
   server.handleClient();  
 }  
   



%d bloggers like this: