In this post, we delve into the programming of the ESP8266. We will start with a programming guide for the ESP8266 in the Arduino environment, looking at the main functions and comparing them with their equivalents in a conventional Arduino.
In the previous entry of the series about the ESP8266, we saw how to set up the Arduino environment to program the ESP8266. We already mentioned that, despite being very similar, there are differences between programming an Arduino and an ESP8266.
On the other hand, we have also seen the hardware details of the ESP8266, comparing them with those of Arduino. In this entry, we will assume that we are clear about the hardware differences. If in doubt, refer to the previous entry.
Therefore, we begin with the programming guide for the ESP8266, the different functions of the ESP8266 and the similarities and differences with Arduino programming.
Time Functions
Millis and Delay
The time functions millis()
and micros()
work the same as in Arduino. The functions delay()
and delayMicroseconds()
also work, with the previous considerations that we will see below about Yielding.
Yielding
This is one of the most important differences compared to an Arduino. The ESP8266 executes many functions related to maintaining and managing the WiFi connection and the TCP/IP stack. If it does not execute these actions, we will have communication issues, freezes, and restarts.
For this reason, it is recommended to avoid blocking for longer than 100-200ms in our code. In fact, if 3 seconds pass without calling these functions, the WatchDog will restart the ESP8266. Even with the WatchDog disabled, with a wait longer than 8 seconds, the ESP8266 will restart itself.
The functions of the ESP8266 are executed at the end of each loop, when calling the delay() function, or with the function developed for the ESP8266 in Arduino yield(), which we could consider equivalent to delay(0)
.
In summary, if you have a process that requires more than 100-200ms, you should modify it to include a delay()
or a yield()
.
On the other hand, the function delaymicroseconds()
does not make a call to yield()
, so we should avoid using it for waits longer than about 20ms.
Connections and Hardware
Pin Designation
The pin designation is (theoretically correctly) associated in the implementation of the ESP8266 in the Arduino environment, according to the following table:
Alias - GPIO | Alias - GPIO | Alias - GPIO |
---|---|---|
D0 - GPIO3 | D6 - GPIO12 | D12 - GPIO12 |
D1 - GPIO1 | D7 - GPIO13 | D13 - GPIO14 |
D2 - GPIO16 | D8 - GPIO0 | D14 - GPIO4 |
D3 - GPIO5 | D9 - GPIO2 | D15 - GPIO5 |
D4 - GPIO4 | D10 - GPIO15 | |
D5 - GPIO14 | D11 - GPIO13 |
To reference a pin, we can use both designations interchangeably. So, in theory, the following two statements are equivalent,
digitalWrite(D5, LOW); // D5, which is equal to GPIO14
digitalWrite(14, LOW); // GPIO14 directly
However, in practice, we know that not all manufacturers follow the same criteria when identifying the pins on the boards (especially the Chinese ones).
Therefore, when programming the ESP8266, we should pay special attention to the pinout of our board and be very aware of possible mistakes in the designation to avoid headaches.
Digital Inputs and Outputs (GPIO)
Digital inputs and outputs (GPIO) are programmed practically the same as in any Arduino. Thus, we can change the mode of a GPIO between input or output with the function:
pinMode(pin, mode)
Where the mode can be OUTPUT
, INPUT
, or INPUT_PULLUP
. Pin 16 has an additional mode INPUT_PULLDOWN_16
, but it is not common that we will use it. As in Arduino, by default, all pins are initialized as INPUT.
On the other hand, when the GPIO is in OUTPUT mode, we can use it as an output by assigning a value (LOW or HIGH) just like in Arduino with:
digitalWrite(pin, output)
Finally, when the GPIO is in input mode, we can read its value just like in Arduino with:
digitalRead(pin)
Analog Outputs (PWM)
The programming of PWM outputs (analog outputs) is very similar to conventional Arduino. However, the ESP8266 does not have hardware PWM, so it performs it in software instead.
This entails additional computational costs that Arduino does not have. However, it also allows us to use PWM on any GPIO.
For this, just like in Arduino, to use a PWM output, we use the function:
analogWrite(pin, value)
By default, the range is 10 bits, so value takes values from 0-1023 (most Arduino models take values from 0-255). However, we can change the range up to 14 bits output with:
analogWriteRange(range)
The default frequency is 1kHz, but it can be changed with the function (the minimum is 100Hz and the maximum is 40kHz)
analogWriteFreq(frequency)
Another difference from an Arduino is that if in the same program we want to use a PWM output as digital later, we must explicitly disable the PWM. To do this, we simply do:
analogWrite(pin, 0)
Analog Inputs (ADC)
The ESP8266 has a single analog input (ADC), which in the Arduino environment is designated as A0. To read it, we use the same function as Arduino, that is, we simply use the function:
analogRead(A0)
The response is a 10-bit value in the range of 0-1023 (just like in most Arduinos), whose voltage is proportional to the voltage at the input. Remember that the ESP8266 has a maximum analog input of 1V, although many boards have a converter to expand it up to 5V.
Verify the maximum allowable voltage of the ADC on your board. Applying a voltage higher than allowed will damage the ADC pin.
On the other hand, the ESP8266 has an additional mode that allows measuring its supply voltage. This is useful, for example, when operating with batteries. To activate it, we add this line in our Sketch.
ADC_MODE(ADC_VCC)
While we are using this mode, the actual ADC pin must be disconnected, and logically, we cannot use it as an analog input.
Interrupts
The ESP8266 has interrupts on all GPIO pins, except on GPIO16 (D2). The usage is similar to hardware interrupts in Arduino.
attachInterrupt(digitalPinToInterrupt(interruptPin), handler, FALLING);
The options are RISING
, FALLING
, CHANGE
, ONLOW
, ONHIGH
, ONLOW_WE
, ONHIGH_WE
.
PROGMEM
The macro PROGMEM
, which stores variables in flash memory instead of dynamic memory, also works in the ESP8266 with a small but important difference.
Instead of Arduino, which performs a pre-analysis to avoid having the same variable multiple times, the equivalent in ESP8266 does not perform this preprocessing.
Thus, for example:
string text1 = F("hello");
string text2 = F("hello");
Will store the literal “hello” twice in memory. We can avoid this with the FPSTR function as follows
const char hello[] PROGMEM = "hello";
string texto1 = FSPTR(hello);
string texto2 = FSPTR(hello);
Communication
Serial Port
The use of the serial port in the ESP8266 is very similar to its use in Arduino and uses all the functions we are accustomed to for sending and receiving data (read, write, print, println…)
The only peculiarities are related to additional functions available on the ESP8266, as the ESP8266 has 2 serial ports.
The first port (UART0) is connected to 2 pairs of pins. By default, it uses TX0 GPIO1 and RX0 GPIO3, but can be changed to TX0 GPIO15 and RX0 GPIO13. If we want to switch between both possibilities, we use the function.
Serial.swap()
The second port (UART1) is associated with the pins TX1 GPIO2 and RX2 GPIO8. However, GPIO8 is used for the connection with external memory. Therefore, UART1 can only be used to send data. The functions are the same as usual but using Serial1 instead of Serial. For example, UART1 would be initialized as follows:
Serial1.begin(baud)
I2C Bus
The use of I2C in the ESP8266 is similar to Arduino and uses the same functions. The difference is that the ESP8266 does not have hardware for I2C communication, so it simulates it in software.
This entails an additional processing load for the ESP8266 that Arduino does not have. But, as an advantage, we can use any two pins for communication.
By default, the pins used are SDA GPIO4 (D2) and SCL GPIO5 (D14). But we can change it with the function:
Wire.begin(SDA, SCL)
The maximum speed is about 450kHZ. Otherwise, the programming is identical to Arduino.
SPI Bus
The ESP8266 has a hardware SPI accessible to the user (sometimes designated HSPI). Again, its use is similar and uses the same functions as in a conventional Arduino. The default pins are MISO GPIO12 (D12), MOSI GPIO13 (D7), CLK GPIO14 (D13), and SS GPIO15 (D10).
Additionally, in the ESP8266, we can easily change the SPI frequency up to 1MHz with the function:
SPI.setFrequency(frequency)
WiFi Communication
To access the WiFi functionalities of the ESP8266, we will use the ESP8266WiFi library, which operates similarly to the Arduino WiFi library.
ESP8266WiFi Class
To include it in our project, we use:
#include <ESP8266WiFi.h>
WiFi functionality is one of the main points of interest of the ESP8266. We will see its usage extensively in the upcoming tutorials of the ESP8266 series, so we will not delve into it for now.
Arduino Libraries
One of the most frequently asked questions when talking about programming the ESP8266 in Arduino is whether Arduino libraries will work on the ESP8266. And the answer is, I’m sorry to say, that in general they will not be compatible.
But it is not always the case. If the library only uses C++ code (for example, a calculation library), or uses functions from the environment to handle hardware, then this library will work.
However, if it accesses internal elements of the architecture like timers, registers, or if it uses assembly codes to speed up certain tasks, for example, then the library will not work unless it has been specifically adapted for the ESP8266.
Fortunately, the popularity of the ESP8266 has led the community to develop or adapt most of the libraries available for Arduino. However, among the many available, we will have to specifically look for those compatible with the ESP8266.
As we can see, despite some differences, in general, it is very similar to programming an Arduino. This is due to the great work of the community in developing compatibility between devices, making it very easy to use the ESP8266 with the Arduino environment.
In the upcoming entries, we will see how to do something similar with the ESP32, and we will start to see examples and projects with the ESP8266.