A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE – Example Code Included

ESP8266

ESP8266 is a chip developed by Espressif.

Generally speaking, development boards equipped with the ESP8266 chip, like the one shown below, are called ESP8266 NodeMCU.

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

There may be slight variations, but as long as the chip is labeled ESP8266, it should work without issue. Anyone can create their own ESP8266 NodeMCU with an ESP8266 chip.

ESP8266 integrates WiFi internally, so it can serve as a WiFi module (I have used it to collect data with STM32, then communicate it to ESP8266 via serial, and finally upload the data to a server) or as a standalone microcontroller.

Next, we will begin programming the ESP8266 NodeMCU using Arduino IDE. (You should have some basic knowledge of microcontrollers, or some terms might be confusing.)

Arduino IDE Environment Configuration

To write programs for the ESP8266 NodeMCU using Arduino IDE, we need to have the resource package for the ESP8266 development board, which we can find on the official website of Dengdeng Technology.

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

Resources for ESP32 are also available.

After clicking to download, you will be redirected to the Arduino Chinese website, and you can follow the steps to download. You will get an exe file to execute directly.

Then, in Arduino IDE, select the options as shown below (I can’t take a screenshot because the options disappear when I use the shortcut, so I had to take a photo).

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

GPIO

To learn about a chip, we first need to become masters of electronics.

The GPIO ports available for the ESP8266 NodeMCU are actually quite limited.

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

While there appear to be many GPIO ports, the GPIO ports on the right side cannot be used (they are not used as ordinary GPIO ports, but for other special purposes), because they are used to control internal storage units. Just remember that the GPIO ports in the row with A0 cannot be used.

In the left row of GPIO, GPIO1 and GPIO3 are used for serial communication and generally are not used for other purposes, so the usable GPIO ports are quite limited.

Let’s start by turning on an LED.

Blinking an LED

Configuring GPIO Port Modes

pinMode(uint8_t pin, uint8_t mode);

The first parameter can be filled in directly with the label on the ESP8266 NodeMCU development board, for example, “D0”, or you can fill in a number; for instance, D0 is actually GPIO16, so filling in the number 16 is also acceptable.

The second parameter configures the mode; simply put, we will use three modes: OUTPUT, INPUT, INPUT_PULLUP, which correspond to output, input, and pull-up input. Actually, there are more configurable modes, but these three are the most commonly used.

Digital Output

digitalWrite(uint8_t pin, uint8_t val);

The first parameter specifies the GPIO port, just like above.

The second parameter can be filled in directly with a number; 1 is high, and 0 is low.

Lighting an LED

void setup() {  // put your setup code here, to run once:    pinMode(D0,OUTPUT);         // equivalent to pinMode(16,OUTPUT);     digitalWrite(D0, 1);        // equivalent to digitalWrite(16, 1)} void loop() {  // put your main code here, to run repeatedly: }

In case some friends are not familiar with this format of code (since both 51 and 32 write the main function), let me explain a bit. The code we write in the setup function is for configuring things and will only execute once, while the code in the loop function will continuously execute, which can be understood as the while(1) in our 51 or 32 code.

This way, we first configure the GPIO port, then output a high level, and finally connect an LED to achieve the operation of lighting up the LED.

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

Delay Function

delay(unsigned long ms);

Fill in a number to delay the corresponding number of milliseconds.

delayMicroseconds(unsigned int us);

This is for delaying in microseconds.

Blinking an LED

With the delay function, we can make the LED blink.

void setup() {    // put your setup code here, to run once:    pinMode(D0,OUTPUT);} void loop() {    // put your main code here, to run repeatedly:    digitalWrite(D0,1);    delay(1000);    digitalWrite(D0,0);    delay(1000);}

Just output a high level once and a low level once.

Of course, writing like this is too cumbersome; we have a more concise way to write.

Digital Reading

digitalRead(uint8_t pin);

With this function, we can read the input level of the corresponding GPIO port.

We only need to output a level different from the read-in level on the GPIO port to achieve blinking.

Blinking LED 2.0

void setup() {    // put your setup code here, to run once:    pinMode(D0,OUTPUT);} void loop() {    // put your main code here, to run repeatedly:    digitalWrite(D0, !digitalRead(D0));    delay(1000);}

Some may wonder, isn’t the GPIO port configured as output mode? How can it still read?

I have tested it, and it works; when the GPIO port is in output mode, the read value is actually its own output. If you want to truly read the input from another module, you still need to configure it in input (INPUT) mode; otherwise, the read data will be incorrect and may even affect the module to be connected.

Timer Function

First, you need to #include<Ticker.h>.

Then define a Ticker type object; the name cannot be time (a painful lesson).

Call the member function of this object to complete the timing operation.

Timed Execution

attach();

The first parameter of this member function fills in an integer that represents the timing in seconds.

The second parameter fills in the function name, and at most one int type parameter can be passed. This means that this function will be executed once every time the specified seconds are reached.

The third parameter can be left empty if the previous function has no parameters; otherwise, the third parameter is the parameter passed when calling the previous function.

This function can be executed multiple times, but only the last one will take effect.

For millisecond-level timing, use the following member function.

attach_ms();

Cancel Timing

detach();

This member function has no parameters; calling it will cancel the previous timing.

Counting Function

If you only need to call once and do not need to loop, you can use the counting function; this function has the same parameters as the timing function but will only be called once.

once(); once_ms();

Note that only one timing task can be assigned to each Ticker object at the same time.

Blinking LED 3.0

#include &lt;Ticker.h&gt; Ticker t0; Ticker t1; void test(int flag) { digitalWrite(D0, !digitalRead(D0)); } void setup() { pinMode(D0, OUTPUT); t0.attach(1, test, 1); t1.once(10, [&amp;]() { t0.detach(); }); } void loop() {}

The above function defines two Ticker objects; one times every second to flip the level of D0. The other times after ten seconds to stop the one-second timing task. Since each object can only have one task at a time, we used two tasks here.

PWM

After configuring the GPIO port to output mode, we can use the following function to output PWM.

analogWrite(uint8_t pin, int val);

The second parameter specifies the duty cycle. I have seen other tutorials say that the second parameter should be between 0-255, with larger values resulting in a higher PWM duty cycle; however, in my tests, the range of the second parameter is 0-1023. This still depends on the board you have, as it may vary.

I used the code below for testing; the LED can be off and also has three different brightness levels, confirming that parameters greater than 255 are also effective, thus confirming the range is 0-1023.

void setup() { pinMode(D0, OUTPUT); } void loop() { analogWrite(D0, 0); delay(1000); analogWrite(D0, 256); delay(1000); analogWrite(D0, 512); delay(1000); analogWrite(D0, 1024); delay(1000); }

If you want to create a breathing light, you just need to modify the duty cycle to increase and decrease continuously.

Additionally, some materials indicate that there are functions to modify the PWM output frequency, but after testing with a passive buzzer, I found it ineffective, and I couldn’t find the reason.

Here is the function; you can try it with your own board, who knows it might work.

analogWriteFreq(uint32_t freq);

External Interrupt

External Interrupt Configuration

attachInterrupt(uint8_t pin, void (*)(), int mode);

The first parameter specifies the GPIO port; this is different from before. The GPIO port filled in here needs to be wrapped with digitalPinToInterrupt() to indicate that the GPIO port is used for external interrupts, and D0 cannot be used.

The second parameter fills in a function without parameters and return value, which will be called when we trigger the external interrupt. When defining the function, you need to write ICACHE_RAM_ATTR at the beginning of the function.

The third parameter specifies the condition for triggering the external interrupt, with three options: RISING, FALLING, CHANGE, which correspond to rising edge, falling edge, and both edges.

You can refer to the following code for specifics.

Disabling Interrupts

detachInterrupt(uint8_t pin);

If we need the GPIO port to stop triggering external interrupts, we use this function. Here the parameter is filled in directly without wrapping the function.

Control LED Switch

int count=0; ICACHE_RAM_ATTR void test() { digitalWrite(D0, !digitalRead(D0)); if (++count >= 10) detachInterrupt(D1); } void setup() { pinMode(D0, OUTPUT); pinMode(D1, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(D1), test, RISING); } void loop() {}

This sets D0 to output mode and D1 to input mode, configuring D1 for external rising edge interrupts. Each time triggered, D0’s level will flip, and after ten times, the interrupt on D1 will be turned off.

Serial Communication

We can directly use the serial port to communicate with the computer by connecting the ESP8266 NodeMCU with a data cable.

Arduino IDE comes with a serial assistant, located at the top right corner of the page; clicking it will bring up the serial monitor at the bottom of the page. You can perform simple data transmission and reception.

A Comprehensive Guide to ESP8266 NodeMCU with Arduino IDE - Example Code Included

Serial Initialization

Serial.begin(unsigned long baud);

This is the only initialization function, but it has five overloaded versions. The most commonly used is the one above where we specify the baud rate, while the data bit is default to 8 bits, stop bit is 1, and no parity bit.

Output Data

There are many functions for outputting data; I will just introduce a few that I commonly use.

write

Serial.write();

This function also has twelve overloaded versions, but I won’t go into detail. Generally, we use this function to output raw data formats, i.e., in hexadecimal.

print, println, printf

Serial.print(); Serial.println(); Serial.printf();

These three output in text format and are commonly used to output debug information.

print just outputs normally.

println outputs the same as print but adds a newline character (“
”).

printf works like in C language, allowing for formatted output.

Reading Data

There are many functions for reading data; here I will also only mention a few that I commonly use.

read

Serial.read();

This function has two overloaded versions, and we will discuss both.

The first version has no parameters and simply reads one byte and returns it.

The second version is as follows.

Serial.read(char *buffer, size_t size);

This can read the number of data specified by the second parameter into the first parameter, but if the size of the second parameter exceeds the number of readable data, it will only read the available data and return the actual number of data read.

readString

Serial.readString();

This function reads data and returns it as a String type.

Checking for Data Availability

If we want to read data, we first need to have data, right? How do we know when data is available?

We use the following function.

Serial.available()

This will return an int value indicating whether there is data to read, but we can treat its return value as a bool type.

Serial Echo Experiment

After understanding the above functions, we can use the ESP8266 NodeMCU for serial communication.

The code below echoes back any received data, hence the name echo.

You can directly use the built-in serial assistant in Arduino IDE to experiment.

void setup() {    // put your setup code here, to run once:    Serial.begin(9600);} void loop() {    // put your main code here, to run repeatedly:    if (Serial.available()) {        Serial.print(

Leave a Comment

×