ESP8266 Programming Basics

In this post we will have a look at the building blocks of an Arduino sketch. This will help you to build your own sketch quickly. The post covers the serial console, digitalRead and digitalWrite, interrupts, analogRead and finally WiFi, http and https.

This text is a chapter from my eBook “ESP8266 WeatherStation – Getting Started Guide”. You can get the book

Buy the WeatherStation Kit in the Squix Shop (international shipping) or in the Amazon Store (US only)

Preparation

In this post we will work with exercises which you can download from GitHub. They contain several Arduino projects for the
ESP8266. For an exercise open the related project in your Arduino IDE and try to solve the given task. If you get stuck or want
to see an alternative solution open the project which ends with “_Solution”:

  • Exercise_04_01: contains the first exercise in chapter 4
  • Exercise_04_01_Solution: contains a possible solution

Now download the zip file from GitHub and extract it in a place you will find it later. There is a green “Clone or download”
button which lets you download a zip file:

https://github.com/squix78/esp8266-getting-started

 

The Arduino Sketch

The Arduino platform was built with the beginner in mind. Compared to a normal C program the Arduino IDE hides a few things from you to simplify the setup. First of all you do not have to create a make file to build your code into an executable binary. The Arduino IDE also includes a default header file for you: #include “Arduino.h”. This contains all definitions needed for a regular Arduino program.

Another important change compared to a regular C/C++ program are the two default functions setup() and loop(). The first will be only called once during startup while the loop() method will be called repeatedly. On a normal Arduino hardware (Atmega chip) you can theoretically write code and never leave the loop() method again. The ESP8266 is a bit different here. If your operations run for too much time a so called watchdog will reset the ESP8266. You can prevent this by allowing the controller to do important operations while you are still in the main loop. Calling yield() or delay(ms) will do this.

Hello World: The serial console

Every self respecting programming tutorial starts with a “Hello World” program. And I don’t want to break with this tradition here. A Hello-World program usually does not more than printing these two words somewhere on the screen. But we are programming a micro controller which does not have a screen yet. So where can we display the text? We will use the Serial object to do that. While you are developing a program on the ESP8266 the micro controller is connected to the computer the Arduino IDE is running on. We use this connection to write a new binary onto the flash memory of the ESP8266. And while our program is running we can also use it to write messages from the ESP8266 back to our computer.

Using the Serial object is fairly easy. You have to initialize it first:

1 Serial.begin(115200);

This tells the Serial object that you want to communicate with a baud rate of 115200. Remember to set the same transfer rate later in the serial console on your computer. Both partners in the communication need to have the same speed settings or you will just see garbage. If you want to send a message from your program to your computer you just this:

1 Serial.print("Hello ");
2 Serial.println("World");

Please have a look at a little difference between the first and the second line. The first uses a method called print and the second println. The only difference is that the later is adding a line break to the output.

http://esp8266.github.io/Arduino/versions/2.3.0/doc/reference.html#serial

The exercise contains another important built-in function:

1 delay(1000);

This instructs the processor to wait 1000 milliseconds or 1 second to continue with the execution. As mentioned earlier with this command you also give the processor time to handle other tasks, such as receiving or sending network packages over WiFi. In this context a call to yield() does the same as delay(0).

Input/ Output: GPIO pins

Now that we can talk to our micro processor over the serial line it is time to interact with the real world. Our ESP8266 is equipped with several so called General Purpose Input Output or short GPIO pins. They can be used for many different applications sensing and generating digital signals of the 3.3 Volt range. This is important if you plan to use an external component with your ESP8266: hardware designed for older Arduino’s is often using the 5V (CMOS) range. Using such a device without a logic level shifter might destroy your ESP8266.

Using a GPIO pin is quite easy: first you tell the micro processor if you want to read or write from the pin. Then you do it. Here is the code for reading:

1   pinMode(PIN, INPUT);
2   int state = digitalRead(PIN);

Unless you want to change the mode of a pin you only need to call pinMode() once. Please note that depending on the pin you can also use INPUT_PULLUP or INPUT_PULLDOWN. Writing to a pin is not much different:

1   pinMode(PIN, OUTPUT);
2   digitalWrite(PIN, HIGH); // or
3   digitalWrite(PIN, LOW);

The second statement will show a HIGH level on PIN which will be 3.3V. The third statement will set the pin to LOW which is 0V. What values for PINcan you use? If you are using a generic ESP8266 module your pins will be labeled GPIO0, GPIO1, etc. To use pin GPIO you would write digitalWrite(0, HIGH);

If you are using a NodeMCU things get a little bit more complicated. The original creators of the NodeMCU Lua firmware and the development module of the same name had the idea to give the pins different names. They are called D0, D1, etc. That by itself would not be confusing yet but they are not using the same digits, e.g. GPIO1 is not equal to D1. Here is a table to map the pins:

Raw Module Name NodeMCU & Wemos Name
GPIO0 D3
GPIO1 D10
GPIO2 D4
GPIO3 D9
GPIO4 D2
GPIO5 D1
GPIO6 N/A
GPIO7 N/A
GPIO8 N/A
GPIO9 D11
GPIO10 D12
GPIO11 N/A
GPIO12 D6
GPIO13 D7
GPIO14 D5
GPIO15 D8
GPIO16 D0

Interrupts

Depending on your age you might remember interrupts from your PC. They were always important to get your sound card play beautiful music. The ESP8266 also can be controlled by interrupts. In the previous exercises we were checking regularly for the state of a GPIO pin. This is fine if you are not doing anything else in the main loop. But you might miss a change in a state if it is very short and that is were the interrupts can help. With an interrupt handler you can tell the processor that you are interested in a specific type of change and a given pin. This is how it works:

1 void buttonPressed() {
2   ...
3 }
4 
5 void setup() {
6     pinMode(PIN, INPUT);
7     attachInterrupt(digitalPinToInterrupt(PIN), buttonPressed, CHANGE);
8 }

buttonPressed is a method without parameter that gets called when there is a change on PIN. Instead of CHANGE you can also use RISING which triggers the callback then the pin changes from LOW to HIGH and FALLING for a change in the opposite direction. Please do not execute long tasks in the callback method. The ESP’s watch dog will reset the processor if calling the interrupt takes too much time. You should not do much more than changing a flag.

Measuring analog signals

So far we can read and write the digital states HIGHand LOW but what if we want to deal with analog signals? The ESP has one Analog To Digital Converter (ADC) which can be used to measure voltage in the range 0 – 1V. To do that use the following command:

1 analogRead(A0);

You can also use the ADC to measure the input voltage without any additional wiring. You have to instruct the processor that you want to measure the supply voltage rather than the value on A0 with a special command outside the setup() and loop() method. Here is an example:

 1 ADC_MODE(ADC_VCC);
 2 
 3 void setup() {
 4    Serial.begin(115200);
 5 }
 6 
 7 void loop() {
 8    Serial.println(ESP.getVcc());
 9    delay(500);
10 }

WiFi

The last few chapters were all about built in functions of the Arduino/ESP8266 platform. Now we will start using libraries which are part of the platform and are already installed. So how can we use the WiFi module of the ESP8266? First of all you need to know that the ESP8266 can operate as a WiFi client and as a access point. You can set this mode with:

1 WiFi.mode(m);

where m must be one of the following modes: WIFI_AP (access point), WIFI_STA (client), WIFI_AP_STA (AP and client) or WIFI_OFF. Now let’s connect to your access point:

1 WiFi.begin(WIFI_SSID, WIFI_PWD);

This will connect you to a access point given the SSID and the password. Please note that this call is not blocking. This means that the code will immediately proceed to the next instruction whether the ESP is connected to the access point or not.

HTTP

By connecting to the internet you either want to exchange data between your ESP8266 and the network. Let’s look at how we can load content from a web server using the Hyper Text Transfer Protocol (HTTP). This protocol is the fundation of the internet.

1 #include <ESP8266WiFi.h>
 2 
 3 const char* ssid     = "SSID";
 4 const char* password = "PASSW0RD";
 5 
 6 const char* host = "www.squix.org";
 7 
 8 void setup() {
 9   Serial.begin(115200);
10 
11   Serial.print("Connecting to ");
12   Serial.println(ssid);
13 
14   WiFi.begin(ssid, password);
15 
16   // Wait until WiFi is connected
17   while (WiFi.status() != WL_CONNECTED) {
18     delay(500);
19     Serial.print(".");
20   }
21 
22   Serial.println("");
23   Serial.println("WiFi connected");
24   Serial.println("IP address: ");
25   Serial.println(WiFi.localIP());
26 }
27 
28 void loop() {
29   delay(5000);
30 
31   Serial.print("connecting to ");
32   Serial.println(host);
33 
34   // Use WiFiClient class to create TCP connections
35   WiFiClient client;
36   const int httpPort = 80;
37   if (!client.connect(host, httpPort)) {
38     Serial.println("connection failed");
39     return;
40   }
41 
42   // We now create a URI for the request
43   String url = "/guide/";
44 
45   Serial.print("Requesting URL: ");
46   Serial.println(url);
47 
48   // This will send the request to the server
49   client.print(String("GET ") + url + " HTTP/1.1\r\n" +
50                "Host: " + host + "\r\n" +
51                "Connection: close\r\n\r\n");
52 
53   unsigned long timeout = millis();
54   while (client.available() == 0) {
55     if (millis() - timeout > 5000) {
56       Serial.println(">>> Client Timeout !");
57       client.stop();
58       return;
59     }
60   }
61 
62   // Read all the lines of the reply from server and print them to Serial
63   while(client.available()){
64     String line = client.readStringUntil('\r');
65     Serial.print(line);
66   }
67 
68 }

How does this work? First we define the SSID and password of the WiFi access point we want to connect to. Please note that there are better ways to do that. The WiFiManager (https://github.com/tzapu/WiFiManager) for instance starts the ESP8266 as access point if it cannot connect to any SSID. You then use your smart phone to configure the WiFi credentials and there is no need to hard code these into your firmware. But for the sake of simplicity let’s ignore this here.

On line 14 we start connecting to the defined access point and wait until the connection is established. After all there is no point to send requests to a server if the network connection is not confirmed yet.

Line 49 sends the request to the server. The command GET /guide/ HTTP/1.1\r\nmight look strange to you. This is how your browsers talks to the web server. GET is the command for the webserver, /guide/ is the resource on the server we want to get and HTTP/1.1 is the protocol that we are using. If you are interested how this works in detail have a look at this Wikipedia article: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol. It

On line 63 we print out the response line by line as long as there is text coming in.

Sadly this is quite complicated. Especially if we want to add encryption in the form of SSL to the connection. This protects your data and makes sure that you are talking to the right server. With the following command we can verify that the host matches the given SHA1 fingerprint.

1 if (client.verify(fingerprint, host)) {
2   Serial.println("certificate matches");
3 } else {
4   Serial.println("certificate doesn't match");
5   return;
6 }

How can you find this finger print? Your browser can help you with this. I will show it with Chrome. First open the page you need the finger print for, in my case www.google.ch. Then click on the little lock symbol and then on Details:

blank
Click to zoom

A click on View Certificate will bring up the detail window about Google’s certificate:

blank
Click to zoom

Scroll down to the bottom of the window copy the value behind SHA1. This is the finger print to verify that you are actually talking to www.google.ch.

Exercise 04.06: Better save than sorry!

In this exercise we will start with the same program as I included earlier in this chapter. But now you are going to change the code to receive the search site from google on a secure channel. Complete the following tasks:

  1. Change the host from www.squix.org to www.google.ch
  2. Get the SHA1 finger print for www.google.ch
  3. Add a check that this fingerprint matches www.google.ch

 

blank
Posted by Daniel Eichhorn

Daniel Eichhorn is a software engineer and an enthusiastic maker. He loves working on projects related to the Internet of Things, electronics, and embedded software. He owns two 3D printers: a Creality Ender 3 V2 and an Elegoo Mars 3. In 2018, he co-founded ThingPulse along with Marcel Stör. Together, they develop IoT hardware and distribute it to various locations around the world.

4 comments

  1. You mention Yield() in passing, and I think it’s much more important than just a passing mention…
    >>> If your operations run for too much time a so called watchdog
    >>>will reset the ESP8266. You can prevent this by allowing the
    >>>controller to do important operations while you are still in
    >>>the main loop. Calling yield() or delay(ms) will do this.

    Your Fan Base would love some more wisdom on this subject… Such as: WHEN TO I CALL YIELD()???

    Thanks,
    Jeff in Texas

Leave a Reply