NodeMCU ESP8266

Programming the NodeMCU ESP8266 Board

Updated: 14 March 2024

Some notes on running code on the NodeMCU ESP8266

Basic Setup

To run the ESP8266 code you will need to use the Arduino CLI or Arduino IDE

You can compile the arduino code using the Arduino CLI

Next, you can work on this application using the Arduino IDE or the Arduino VSCode Extension. Using the VSCode extension you will need to run the Arduino: Initialize command to configure the IDE to be aware of the relevant libraries - For the board I am using I will select NodeMCU 1.0 (ESP-12E Module) thereafter select the Serial Port that your device is connected on via USB as well as selecting the sketch with the Arduino: Select Sketch command

To compile the code that will run on the device you can use the following commands:

Terminal window
1
# Install the board
2
arduino-cli core install esp8266:esp8266 --config-file ./.cli-config.yml
3
4
# Compile the app
5
arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 ./sketches/blink
6
7
# Upload the app to the board
8
9
arduino-cli compile --fqbn esp8266:esp8266:nodemcuv2 --port COM3 --upload ./sketches/blink
10
11
# Upload the app to the board (does not compile)
12
arduino-cli upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/blink
13
14
# Start Serial Monitor
15
arduino-cli monitor -p --fqbn esp8266:esp8266:nodemcuv2 --port COM3 -c baudrate=115200

The above commands provide a basic outline for running your board and the relevant tasks associated. You will only need to run the installation once, and the compiliation is not needed if using arduino-cli upload as it will upload the files as well

Note you can also do all of the above using the Arduino VSCode Extension

Install The Board

Before you can do anything else, you will need to install the board

First, define a config file for the Arduino CLI called .cli-config.yml with the following in it:

esp-8266/.cli-config.yml
1
board_manager:
2
additional_urls:
3
- http://arduino.esp8266.com/stable/package_esp8266com_index.json

Then, install the board using this config:

Terminal window
1
arduino-cli core install esp8266:esp8266 --config-file ./.cli-config.yml

Using the Serial Monitor

Note that you will have to set the Baud rate when connecting to a device using the serial monitor in order to view the output

You can connect the Serial Monitor to the device as follows:

Terminal window
1
arduino-cli monitor -p --fqbn esp8266:esp8266:nodemcuv2 --port COM3 -c baudrate=115200

Note that the baudrate needs to be the same as what is configured during the device setup function using Serial.begin

Connecting the serial monitor like this should also cause the device to restart so you should be able to get the from the start. Note that you will have to kill this process if you want to upload any code to the device again

To use the Arduino Extension’s Serial Monitor you can simply connect to the device. In the event you want to capture output from the moment the device turns on you can press the RST button on the device while the Serial Monitor is connected which wil allow you to connect as soon as the device starts up. This is necessary since when the device is being flashed the serial monitor must be disconnected

Examples

Note that when switching sketches you will need to use the VSCode Extension’s Arduino: Select Sketch and thereafter the Arduino: Initialize and Arduino: Rebuild Intellisense Configuration command to configure VSCode to register the installed libraries

For the sake of example, I will have all my sketches contained in a sketches directory with a directory for each sketch (as is required by Arduino)

This is effectively the standard getting started hardware programming task - blinking the light on the board. For this example create the sketches/beep/beep.ino file:

esp-8266/sketches/blink/blink.ino
1
// Based on example from: https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
2
3
void setup()
4
{
5
Serial.begin(115200);
6
7
Serial.println("STARTING BLINK");
8
pinMode(LED_BUILTIN, OUTPUT);
9
}
10
11
void loop()
12
{
13
digitalWrite(LED_BUILTIN, HIGH);
14
delay(1000);
15
digitalWrite(LED_BUILTIN, LOW);
16
delay(1000);
17
}

Provided you have the board connected via USB, you can then run it as follows:

Terminal window
1
arduino-cli compile --upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/blink

This is also a good opportunity to connect the Serial monitor to view some output from the device using the previous command

Beep

For this example we will play a short sound on the device when the setup hook runs using the builtin tone function. Add the following content to a file with the path esp-8266/sketches/beep/beep.ino:

esp-8266/sketches/beep/beep.ino
1
// Note frequencies from https://learn.adafruit.com/circuit-playground-o-phonor/musical-note-basics
2
//
3
// octave = 1 2 3 4 5 6 7 8
4
// NOTES = { "C" : (33, 65, 131, 262, 523, 1047, 2093, 4186),
5
// "D" : (37, 73, 147, 294, 587, 1175, 2349, 4699),
6
// "E" : (41, 82, 165, 330, 659, 1319, 2637, 5274),
7
// "F" : (44, 87, 175, 349, 698, 1397, 2794, 5588),
8
// "G" : (49, 98, 196, 392, 785, 1568, 3136, 6272),
9
// "A" : (55, 110, 220, 440, 880, 1760, 3520, 7040),
10
// "B" : (62, 123, 247, 494, 988, 1976, 3951, 7902)}
11
12
// Speaker connected to the D5 Pin and Ground
13
const int PIN_SPEAKER = D5;
14
15
void setup()
16
{
17
Serial.begin(115200);
18
19
Serial.println("STARTING BEEP");
20
21
pinMode(PIN_SPEAKER, OUTPUT);
22
23
playSound();
24
}
25
26
const int FREQ_HIGH = 880;
27
const int FREQ_LOW = 65;
28
29
void playSound()
30
{
31
int duration = 500;
32
33
tone(PIN_SPEAKER, FREQ_HIGH, duration);
34
delay(duration);
35
tone(PIN_SPEAKER, FREQ_LOW, duration);
36
delay(duration * 2);
37
}
38
39
void loop() {}

This can then be uploaded and run on the device using:

Terminal window
1
arduino-cli compile --upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/beep

WebServer

For this example we will create a small application that responds to HTTP requests and can be accessed from other devices on the same network:

First, define the WiFi connection details by adding a config.h in the project directory with the following:

1
#define STASSID "WIFI SSID"
2
#define STAPSK "WIFI_PASSWORD"

Then, the code for the Web Server will look like so:

esp-8266/sketches/web-server/web-server.ino
1
// Based on the ESP8266WebServer example
2
#include <ESP8266WiFi.h>
3
#include <WiFiClient.h>
4
#include <ESP8266WebServer.h>
5
#include <ESP8266mDNS.h>
6
#include "config.h"
7
8
const char *ssid = STASSID;
9
const char *password = STAPSK;
10
11
ESP8266WebServer server(80);
12
13
void handleRoot()
14
{
15
digitalWrite(LED_BUILTIN, HIGH);
16
server.send(200, "text/plain", "hello from esp8266!\r\n");
17
digitalWrite(LED_BUILTIN, LOW);
18
}
19
20
void handleNotFound()
21
{
22
digitalWrite(LED_BUILTIN, HIGH);
23
String message = "File Not Found\n\n";
24
message += "URI: ";
25
message += server.uri();
26
message += "\nMethod: ";
27
message += (server.method() == HTTP_GET) ? "GET" : "POST";
28
message += "\nArguments: ";
29
message += server.args();
30
message += "\n";
31
for (uint8_t i = 0; i < server.args(); i++)
32
{
33
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
34
}
35
server.send(404, "text/plain", message);
36
digitalWrite(LED_BUILTIN, LOW);
37
}
38
39
void setup(void)
40
{
41
Serial.begin(115200);
42
43
pinMode(LED_BUILTIN, OUTPUT);
44
45
digitalWrite(LED_BUILTIN, HIGH);
46
delay(1000);
47
digitalWrite(LED_BUILTIN, LOW);
48
49
WiFi.mode(WIFI_STA);
50
WiFi.begin(ssid, password);
51
Serial.println("");
52
53
Serial.print("Connecting to WIFI");
54
while (WiFi.status() != WL_CONNECTED)
55
{
56
// Wait for connection
57
delay(500);
58
Serial.print(".");
59
60
if (WiFi.status() == WL_WRONG_PASSWORD)
61
{
62
Serial.print("INCORRECT WIFI PASSWORD");
63
return;
64
}
65
}
66
Serial.println("");
67
68
Serial.print("Connected to ");
69
Serial.println(ssid);
70
Serial.print("IP address: ");
71
Serial.println(WiFi.localIP());
72
73
if (MDNS.begin("esp8266"))
74
{
75
Serial.println("MDNS responder started");
76
}
77
78
server.on("/", handleRoot);
79
80
server.onNotFound(handleNotFound);
81
82
server.begin();
83
Serial.println("HTTP server started");
84
}
85
86
void loop()
87
{
88
server.handleClient();
89
MDNS.update();
90
}

You can then upload and run it on the device using:

And thereafter you can connect the serial monitor as normal. This is necessary since we want to view the IP adress of the

WebSocket

For this example we will create a small application that connects to a WebSocket server and prints out the received messages:

Before running the following, you will need to install the ArduinoWebSockets library using the Arduino CLI

Terminal window
1
arduino-cli lib install ArduinoWebsockets

Next, define the config by adding a config.h in the project directory with the following:

1
#define STASSID "WIFI SSID"
2
#define STAPSK "WIFI_PASSWORD"
3
4
// This server will send an inital message and will thereafter echo any message sent to it
5
#define WSSERVER "wss://echo.websocket.org"

And the code for the application can be seen below:

esp-8266/sketches/websocket/websocket.ino
1
// Example from the ArduinoWebsockets docs: https://github.com/gilmaimon/ArduinoWebsockets
2
#include <ArduinoWebsockets.h>
3
#include <ESP8266WiFi.h>
4
#include "config.h"
5
6
using namespace websockets;
7
8
const char *ssid = STASSID;
9
const char *password = STAPSK;
10
const char *websockets_server = WSSERVER;
11
12
void onMessageCallback(WebsocketsMessage message)
13
{
14
Serial.println("Received Websocket Message: ");
15
Serial.println(message.data());
16
}
17
18
void onEventsCallback(WebsocketsEvent event, String data)
19
{
20
21
Serial.println("Received Event");
22
Serial.println(data);
23
24
switch (event)
25
{
26
case WebsocketsEvent::ConnectionOpened:
27
Serial.println("Connnection Opened");
28
break;
29
30
case WebsocketsEvent::ConnectionClosed:
31
Serial.println("Connnection Closed");
32
break;
33
}
34
}
35
36
WebsocketsClient client;
37
void setup()
38
{
39
Serial.begin(115200);
40
WiFi.begin(ssid, password);
41
42
Serial.print("Connecting to WIFI");
43
while (WiFi.status() != WL_CONNECTED)
44
{
45
// Wait for connection
46
delay(500);
47
Serial.print(".");
48
49
if (WiFi.status() == WL_WRONG_PASSWORD)
50
{
51
Serial.print("INCORRECT WIFI PASSWORD");
52
return;
53
}
54
}
55
Serial.println("");
56
57
Serial.print("Connected to ");
58
Serial.println(ssid);
59
Serial.print("IP address: ");
60
Serial.println(WiFi.localIP());
61
62
// Setup Callbacks
63
client.onMessage(onMessageCallback);
64
client.onEvent(onEventsCallback);
65
66
client.connect(websockets_server);
67
}
68
69
void loop()
70
{
71
client.poll();
72
}

Then, you can run the following command to upload and run the code on the device

Terminal window
1
arduino-cli compile --upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/websocket

And thereafter you can connect the serial monitor as normal to view any logs from the websocket response

Screen

This one was a bit more complicated since it required a little more info, the following youtube videos were really handy in explaining the hardware setup process in more detail:

Connecting the Screen

For the connection Information I used the Tutorial on I2C OLED Display with Arduino/NodeMCU video. The connection information for the screen I have is as follows:

Screen PinNodeMCU Pin
VCC3V
GNDGND
SCLD1
SDAD2

Libraries

We also need a few libraries which we can install with:

Terminal window
1
arduino-cli lib install "i2cdetect"
2
arduino-cli lib install "Adafruit GFX Library"
3
arduino-cli lib install "Adafruit BusIO"
4
arduino-cli lib install "Adafruit SSD1306"

For running the display there are two things we need to do

Get the Screen Address

Firstly, we need to figure out the screen address, this can be done by using the i2cdetect library using their example as-is. This is just needed the first time we use the screen:

esp-8266/sketches/i2c-detect/i2c-detect.ino
1
// Example from the i2detect library
2
#include <Wire.h>
3
#include <i2cdetect.h>
4
5
void setup()
6
{
7
Wire.begin();
8
Serial.begin(115200);
9
Serial.println("i2cdetect example\n");
10
Serial.print("Scanning address range 0x03-0x77\n\n");
11
}
12
13
void loop()
14
{
15
i2cdetect(); // default range from 0x03 to 0x77
16
delay(2000);
17
}

You can run this with:

Terminal window
1
arduino-cli compile --upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/i2c-detect

Then, we need to connect the Serial Monitor and view the output. In the output we should see a value that is active, in my case C3:

1
0 1 2 3 4 5 6 7 8 9 a b c d e f
2
00: -- -- -- -- -- -- -- -- -- -- -- -- --
3
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
4
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
5
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
6
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
7
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
8
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
9
70: -- -- -- -- -- -- -- --

This is the value we will use for the Screen Address in the next setup

Write Some Stuff

To write stuff to the screen we can use the Adafruit sample from the library as a base, I’ve simplified it to just print a message on the screen to demonstrate the basic usage:

esp-8266/sketches/screen/screen.ino
1
// Based on Example for AdaFruit Monochrome OLEDs based on SSD1306 drivers
2
3
#include <SPI.h>
4
#include <Wire.h>
5
#include <Adafruit_GFX.h>
6
#include <Adafruit_SSD1306.h>
7
8
#define SCREEN_WIDTH 128 // OLED display width, in pixels
9
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
10
11
#define OLED_RESET -1
12
#define SCREEN_ADDRESS 0x3C // This value depends on the screen, running the i2x-detect script will give you the correct one for your screen
13
14
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
15
16
void displayText(void)
17
{
18
display.clearDisplay();
19
20
display.setTextSize(3);
21
display.setTextColor(SSD1306_WHITE);
22
display.setCursor(10, 0);
23
display.println(F("Hello"));
24
display.println(F("WORLD!!"));
25
display.display();
26
}
27
28
void setup()
29
{
30
Serial.begin(9600);
31
32
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
33
{
34
Serial.println(F("SSD1306 allocation failed"));
35
for (;;)
36
; // Don't proceed, loop forever
37
}
38
}
39
40
void loop()
41
{
42
displayText();
43
}

And this can be run using:

Terminal window
1
arduino-cli compile --upload --fqbn esp8266:esp8266:nodemcuv2 --port COM3 ./sketches/screen