Language: EN


ESP8266 Programming Guide in Arduino Environment

In this post, we will delve into the programming of the ESP8266. And we will start with a programming guide for the ESP8266 in the Arduino environment looking at the main functions and comparing them to their equivalents in a conventional Arduino.

In the previous post in 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 details of the ESP8266 hardware, comparing them with those of Arduino. In this post, we will assume that we are clear about the differences in terms of hardware. In case of doubt, refer to the previous post.

Therefore, we start with the ESP8266 programming guide, the different functions of the ESP8266, and the similarities and differences with the programming of an Arduino.

Time Functions

Millis and Delay

The time functions millis() and micros() work the same as in Arduino. delay() and delayMicroseconds() also work, with the previous considerations that we will see below about 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 TC/IP stack. If it does not execute these actions, we will have communication problems, lockups, and restarts.

For this reason, it is recommended to avoid blockages of more than 100-200ms in our code. In fact, if 3 seconds pass without calling these functions, the WatchDog will restart the ESP8266. And even with the WatchDog deactivated, with a wait of more than 8 seconds, the ESP8266 will restart by 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 (in theory correctly) associated in the implementation of the ESP8266 in the Arduino environment, according to the following table:

Alias - GPIOAlias - GPIOAlias - GPIO
D0 - GPIO3D6 - GPIO12D12 - GPIO12
D1 - GPIO1D7 - GPIO13D13 - GPIO14
D2 - GPIO16D8 - GPIO0D14 - GPIO4
D3 - GPIO5D9 - GPIO2D15 - GPIO5
D4 - GPIO4D10 - GPIO15
D5 - GPIO14D11 - GPIO13

Therefore, to refer to a pin we can use both designations interchangeably. So, in theory, the following two statements are equivalent,

digitalWrite(D5, LOW);  // D5, which is the same as 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 Outputs and Inputs (GPIO)

Digital inputs and outputs (GPIO) are programmed almost the same as in any Arduino. So, 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 to 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:


Analog Outputs (PWM)

The programming of PWM outputs (“analog” outputs) is very similar to a conventional Arduino. However, the ES8266 does not have hardware PWM, so, instead, it is done by software.

This represents an additional computational cost that Arduino does not have. However, it also allows us to use PWM in any GPIO.

To use a PWM output, just like in Arduino, 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 of output with:


The default frequency is 1kHz, but it can be changed with the function (minimum is 100Hz and maximum is 40kHz)


Another difference with Arduino is that if in the same program we want to use a PWM output as digital later, we must explicitly deactivate the PWM. For 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:


The response is a 10-bit value in the range of 0-1023 (the same as in most Arduinos), whose voltage is proportional to the input voltage. Remember that the ESP8266 has a maximum analog input of 1V, although many boards have a converter to expand it up to 5V.

Check the maximum permissible 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 working with batteries. To activate it, we add this line in our Sketch.


While we are using this mode, the real ADC pin must be disconnected and, logically, we cannot use it as an analog input.


The ESP8266 has interrupts on all GPIO pins, except on pin GPIO16 (D2). The use is similar to hardware interrupts in Arduino.

attachInterrupt(digitalPinToInterrupt(interruptPin), handler, FALLING);



The PROGMEM macro, which stores variables in flash memory instead of dynamic memory, also works on the ESP8266 with a small but important difference.

Unlike Arduino, which performs a previous analysis to avoid having the same variable multiple times, the equivalent in ESP8266 does not perform this preprocessing.

So, for example:

string text1 = F("hello");
string text2 = F("hello");

Will save the literal “hello” twice in memory. We can avoid this with the FPSTR function like this

const char hello[] PROGMEM = "hello";
string text1 = FSPTR(hello);
string text2 = FSPTR(hello);


Serial Port

The use of the serial port on the ESP8266 is very similar to its use on Arduino and uses all the functions we are used 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 it can be changed to be TX0 GPIO15 and RX0 GPIO13. If we want to switch between both possibilities, we use the function.


The second port (UART1) is associated with the pins TX1 GPIO2 and RX2 GPIO8. But the 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 like this:


I2C Bus

The use of I2C on 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 by software.

This represents an additional computational 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.


The ESP8266 has a hardware SPI accessible to the user (sometimes designated as 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, on the ESP8266, we can easily change the frequency of the SPI up to 1Mhz with the function:


WiFi Communication

To access the WiFi functionalities of the ESP8266 we will use the ESP8266WiFI library, whose operation is similar to the Arduino WiFi library.

ESP8266WiFi Class

To include it in our project, we use:

#include <ESP8266WiFi.h>

The WiFi functionality is one of the main points of interest of the ESP8266. We will see its use intensively in the next tutorials in 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, will Arduino libraries work on the ESP8266? And the answer is, I’m sorry to say, that in general they will not be compatible.

But not always. If the library only uses C++ code (for example, a math library), or uses environment functions to handle hardware, this library will work.

However, if it accesses internal architecture elements such as timers, registers, or if it uses assembly code to accelerate certain tasks, for example, 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 in Arduino. Although, among the many available, we will have to specifically look for those compatible with ESP8266.

As we can see, despite some differences, it is generally very similar to programming an Arduino. This is due to the great work of the community in developing compatibility between devices, and makes it very easy to use the ESP8266 with the Arduino environment.

In the next posts, we will see how to do something similar with the ESP32, and we will start to see examples and projects with the ESP8266.