ESP8266 Multicast Domain Name System
DNS
mDNS
#include <ESP8266WiFi.h> // Include the Wi-Fi library
#include <ESP8266WiFiMulti.h> // Include the Wi-Fi-Multi library
#include <ESP8266mDNS.h> // Include the mDNS library
ESP8266WiFiMulti wifiMulti; // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
void setup() {
Serial.begin(115200); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // add Wi-Fi networks you want to connect to
wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
Serial.println("Connecting ...");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
delay(1000);
Serial.print(++i); Serial.print(' ');
}
Serial.println('\n');
Serial.print("Connected to ");
Serial.println(WiFi.SSID()); // Tell us what network we're connected to
Serial.print("IP address:\t");
Serial.println(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer
if (!MDNS.begin("esp8266")) { // Start the mDNS responder for esp8266.local
Serial.println("Error setting up MDNS responder!");
}
Serial.println("mDNS responder started");
}
void loop() { }
user@computername:~$ ping esp8266.local
PING esp8266.local (10.92.237.128) 56(84) bytes of data.
64 bytes from 10.92.237.128: icmp_seq=1 ttl=128 time=5.68 ms
64 bytes from 10.92.237.128: icmp_seq=2 ttl=128 time=3.41 ms
64 bytes from 10.92.237.128: icmp_seq=3 ttl=128 time=2.55 ms
64 bytes from 10.92.237.128: icmp_seq=4 ttl=128 time=2.19 ms
64 bytes from 10.92.237.128: icmp_seq=5 ttl=128 time=2.29 ms
64 bytes from 10.92.237.128: icmp_seq=6 ttl=128 time=2.74 ms
^C
--- esp8266.local ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5007ms
rtt min/avg/max/mdev = 2.190/3.148/5.687/1.202 ms
MDNS.begin
.ESP8266 Establishing a Wi-Fi connection
Station mode
Connecting to one specific network
#include <ESP8266WiFi.h> // Include the Wi-Fi library
const char* ssid = "SSID"; // The SSID (name) of the Wi-Fi network you want to connect to
const char* password = "PASSWORD"; // The password of the Wi-Fi network
void setup() {
Serial.begin(115200); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println('\n');
WiFi.begin(ssid, password); // Connect to the network
Serial.print("Connecting to ");
Serial.print(ssid); Serial.println(" ...");
int i = 0;
while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
delay(1000);
Serial.print(++i); Serial.print(' ');
}
Serial.println('\n');
Serial.println("Connection established!");
Serial.print("IP address:\t");
Serial.println(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer
}
void loop() { }
WiFi.beginfunction. Then wait for the connection to complete, et voilà, your ESP8266 is now connected to your Local Area Network.
Connecting to SSID ...
1 2 3 4 5 6 ...
Connection established!
IP address: 192.168.1.3
CTRL+ALT+T.
ping, and then the IP address you received in the Serial monitor. If you're on Mac or Linux, use
CTRL+Cto stop it after a couple of lines. The output should look something like this:
user@computername:~$ ping 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=128 time=6.38 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=128 time=45.2 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=128 time=69.1 ms
64 bytes from 192.168.1.3: icmp_seq=4 ttl=128 time=94.0 ms
64 bytes from 192.168.1.3: icmp_seq=5 ttl=128 time=20.5 ms
64 bytes from 192.168.1.3: icmp_seq=6 ttl=128 time=7.37 ms
^C
--- 192.168.1.3 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5003ms
rtt min/avg/max/mdev = 6.384/40.463/94.047/32.588 ms
- Access point: Other Wi-Fi devices can connect to it, to be part of the local network.
- Router: It routes IP packets to the right sub-nets so that they will arrive at their destination. E.g. if the computer sends a message that is meant for the ESP over the Ethernet sub-net, the router will send the packet to the Wi-Fi sub-net, because it knows that's where the ESP is.
- Modem: if the router can't find the addressee on the local network, the packet will be passed on to the integrated modem, and it will be sent to the Internet Service Provider over a DSL line, heading for the Internet, where lots of other routers will try to get the packet to the right destination.
Automatically connect to the strongest network
#include <ESP8266WiFi.h> // Include the Wi-Fi library
#include <ESP8266WiFiMulti.h> // Include the Wi-Fi-Multi library
ESP8266WiFiMulti wifiMulti; // Create an instance of the ESP8266WiFiMulti class, called 'wifiMulti'
void setup() {
Serial.begin(115200); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); // add Wi-Fi networks you want to connect to
wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
Serial.println("Connecting ...");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above
delay(1000);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connected to ");
Serial.println(WiFi.SSID()); // Tell us what network we're connected to
Serial.print("IP address:\t");
Serial.println(WiFi.localIP()); // Send the IP address of the ESP8266 to the computer
}
void loop() { }
Access Point mode
#include <ESP8266WiFi.h> // Include the Wi-Fi library
const char *ssid = "ESP8266 Access Point"; // The name of the Wi-Fi network that will be created
const char *password = "thereisnospoon"; // The password required to connect to it, leave blank for an open network
void setup() {
Serial.begin(115200);
delay(10);
Serial.println('\n');
WiFi.softAP(ssid, password); // Start the access point
Serial.print("Access Point \"");
Serial.print(ssid);
Serial.println("\" started");
Serial.print("IP address:\t");
Serial.println(WiFi.softAPIP()); // Send the IP address of the ESP8266 to the computer
}
void loop() { }
Uploading sketches to the ESP8266
Auto-reset
How to use Auto-reset
To use this auto-reset circuit, connect it to the DTR line of your USB-to-Serial converter, and to the reset line of the ESP, as shown in the diagram. Then click compile (just because the first compilation can take quite some time). Go to Tools > Reset and select 'ck'. When it's done compiling, hold down the program button we added in the hardware step, and click upload. Wait for it to say "Uploading..." and then release the program button.Auto-reset and Auto-program
Manual reset and manual program
- press and hold the reset button
- press and hold the program button
- release the reset button, the ESP will boot in program mode
- release the program button
- upload the sketch
Board options
Flash Mode
Flash Size
Debug port
Debug level
Reset Method
Flash Frequency
CPU Frequency
Upload Speed
ESP8266 Wi-Fi
The TCP/IP stack
Layer | Protocols |
---|---|
Application | HTTP, FTP, mDNS, WebSocket, OSC ... |
Transport | TCP, UDP |
Internet | IP |
Link | Ethernet, Wi-Fi ... |
The Link layer
- The ESP8266 connects to a wireless access point (WAP or simply AP). The AP can be built-in to your modem or router, for example.
In this configuration, the ESP acts like a wireless station. - The ESP8266 acts as an access point and wireless stations can connect to it. These stations could be your laptop, a smartphone, or even another ESP in station mode.
TL;DR
The Internet or Network layer
Sub-net mask (optional)
MAC addresses and ARP (optional)
What about the Internet?
TL;DR
The Transport layer
We need a third layer on top of the Internet layer: the Transport layer.
- TCP makes sure that all packets are received, that the packets are in order, and that corrupted packets are re-sent. This means that it can be used for communication between multiple applications, without having to worry about data integrity or packet loss. This is why it's used for things like downloading webpages, sending email, uploading files etc.
- UDP on the other hand, doesn't guarantee that every packet reaches its destination, it does check for errors however, but when it finds one, it just destroys the packet, without re-sending it. This means that it's not as reliable as TCP, but it's faster, and has a much lower latency, because it doesn't require an open connection to send messages, like TCP does. That's why it's used in voice and video chats, and for example in online games.
TL;DR
The Application layer
HyperText Transfer Protocol
WebSocket
Open Sound Control
Domain Name System
Sources
The ESP8266 as a microcontroller - Hardware
While the ESP8266 is often used as a ‘dumb’ Serial-to-WiFi bridge, it’s a very powerful microcontroller on its own. In this chapter, we’ll look at the non-Wi-Fi specific functions of the ESP8266.
Digital I/O
Just like a normal Arduino, the ESP8266 has digital input/output pins (I/O or GPIO, General Purpose Input/Output pins). As the name implies, they can be used as digital inputs to read a digital voltage, or as digital outputs to output either 0V (sink current) or 3.3V (source current).
Voltage and current restrictions
The ESP8266 is a 3.3V microcontroller, so its I/O operates at 3.3V as well. The pins are not 5V tolerant, applying more than 3.6V on any pin will kill the chip.
The maximum current that can be drawn from a single GPIO pin is 12mA.
Usable pins
The ESP8266 has 17 GPIO pins (0-16), however, you can only use 11 of them, because 6 pins (GPIO 6 - 11) are used to connect the flash memory chip. This is the small 8-legged chip right next to the ESP8266. If you try to use one of these pins, you might crash your program.
GPIO 1 and 3 are used as TX and RX of the hardware Serial port (UART), so in most cases, you can’t use them as normal I/O while sending/receiving serial data.
Boot modes
As mentioned in the previous chapter, some I/O pins have a special function during boot: They select 1 of 3 boot modes:
GPIO15 | GPIO0 | GPIO2 | Mode |
---|---|---|---|
0V | 0V | 3.3V | Uart Bootloader |
0V | 3.3V | 3.3V | Boot sketch (SPI flash) |
3.3V | x | x | SDIO mode (not used for Arduino) |
Note: you don’t have to add an external pull-up resistor to GPIO2, the internal one is enabled at boot.
We made sure that these conditions are met by adding external resistors in the previous chapter, or the board manufacturer of your board added them for you. This has some implications, however:
- GPIO15 is always pulled low, so you can’t use the internal pull-up resistor. You have to keep this in mind when using GPIO15 as an input to read a switch or connect it to a device with an open-collector (or open-drain) output, like I²C.
- GPIO0 is pulled high during normal operation, so you can’t use it as a Hi-Z input.
- GPIO2 can’t be low at boot, so you can’t connect a switch to it.
Internal pull-up/-down resistors
GPIO 0-15 all have a built-in pull-up resistor, just like in an Arduino. GPIO16 has a built-in pull-down resistor.
PWM
Unlike most Atmel chips (Arduino), the ESP8266 doesn’t support hardware PWM, however, software PWM is supported on all digital pins. The default PWM range is 10-bits @ 1kHz, but this can be changed (up to >14-bit@1kHz).
Analog input
The ESP8266 has a single analog input, with an input range of 0 - 1.0V. If you supply 3.3V, for example, you will damage the chip. Some boards like the NodeMCU have an on-board resistive voltage divider, to get an easier 0 - 3.3V range. You could also just use a trimpot as a voltage divider.
The ADC (analog to digital converter) has a resolution of 10 bits.
Communication
Serial
The ESP8266 has two hardware UARTS (Serial ports):
UART0 on pins 1 and 3 (TX0 and RX0 resp.), and UART1 on pins 2 and 8 (TX1 and RX1 resp.), however, GPIO8 is used to connect the flash chip. This means that UART1 can only transmit data.
UART0 also has hardware flow control on pins 15 and 13 (RTS0 and CTS0 resp.). These two pins can also be used as alternative TX0 and RX0 pins.
I²C
The ESP doesn’t have a hardware TWI (Two Wire Interface), but it is implemented in software. This means that you can use pretty much any two digital pins. By default, the I²C library uses pin 4 as SDA and pin 5 as SCL. (The data sheet specifies GPIO2 as SDA and GPIO14 as SCL.) The maximum speed is approximately 450kHz.
SPI
The ESP8266 has one SPI connection available to the user, referred to as HSPI. It uses GPIO14 as CLK, 12 as MISO, 13 as MOSI and 15 as Slave Select (SS). It can be used in both Slave and Master mode (in software).
GPIO overview
GPIO | Function | State | Restrictions |
---|---|---|---|
0 | Boot mode select | 3.3V | No Hi-Z |
1 | TX0 | - | Not usable during Serial transmission |
2 | Boot mode select TX1 |
3.3V (boot only) | Don’t connect to ground at boot time Sends debug data at boot time |
3 | RX0 | - | Not usable during Serial transmission |
4 | SDA (I²C) | - | - |
5 | SCL (I²C) | - | - |
6 - 11 | Flash connection | x | Not usable, and not broken out |
12 | MISO (SPI) | - | - |
13 | MOSI (SPI) | - | - |
14 | SCK (SPI) | - | - |
15 | SS (SPI) | 0V | Pull-up resistor not usable |
16 | Wake up from sleep | - | No pull-up resistor, but pull-down instead Should be connected to RST to wake up |
The ESP8266 as a microcontroller - Software
Most of the microcontroller functionality of the ESP uses exactly the same syntax as a normal Arduino, making it really easy to get started.
Digital I/O
Just like with a regular Arduino, you can set the function of a pin using pinMode(pin, mode);
where pin
is the GPIO number*, and mode
can be either INPUT
, which is the default, OUTPUT
, or INPUT_PULLUP
to enable the built-in pull-up resistors for GPIO 0-15. To enable the pull-down resistor for GPIO16, you have to use INPUT_PULLDOWN_16
.
(*) NodeMCU uses a different pin mapping, read more here. To address a NodeMCU pin, e.g. pin 5, use D5: for instance: pinMode(D5, OUTPUT);
To set an output pin high (3.3V) or low (0V), use digitalWrite(pin, value);
where pin
is the digital pin, and value
either 1 or 0 (or HIGH
and LOW
).
To read an input, use digitalRead(pin);
To enable PWM on a certain pin, use analogWrite(pin, value);
where pin
is the digital pin, and value
a number between 0 and 1023.
You can change the range (bit depth) of the PWM output by using analogWriteRange(new_range);
The frequency can be changed by using analogWriteFreq(new_frequency);
. new_frequency
should be between 100 and 1000Hz.
Analog input
Just like on an Arduino, you can use analogRead(A0)
to get the analog voltage on the analog input. (0 = 0V, 1023 = 1.0V).
The ESP can also use the ADC to measure the supply voltage (VCC). To do this, include ADC_MODE(ADC_VCC);
at the top of your sketch, and use ESP.getVcc();
to actually get the voltage.
If you use it to read the supply voltage, you can’t connect anything else to the analog pin.
Communication
Serial communication
To use UART0 (TX = GPIO1, RX = GPIO3), you can use the Serial
object, just like on an Arduino: Serial.begin(baud)
.
To use the alternative pins (TX = GPIO15, RX = GPIO13), use Serial.swap()
after Serial.begin
.
To use UART1 (TX = GPIO2), use the Serial1
object.
All Arduino Stream functions, like read, write, print, println, ...
are supported as well.
I²C and SPI
You can just use the default Arduino library syntax, like you normally would.
Sharing CPU time with the RF part
One thing to keep in mind while writing programs for the ESP8266 is that your sketch has to share resources (CPU time and memory) with the Wi-Fi- and TCP-stacks (the software that runs in the background and handles all Wi-Fi and IP connections).
If your code takes too long to execute, and don’t let the TCP stacks do their thing, it might crash, or you could lose data. It’s best to keep the execution time of you loop under a couple of hundreds of milliseconds.
Every time the main loop is repeated, your sketch yields to the Wi-Fi and TCP to handle all Wi-Fi and TCP requests.
If your loop takes longer than this, you will have to explicitly give CPU time to the Wi-Fi/TCP stacks, by using including delay(0);
or yield();
. If you don’t, network communication won’t work as expected, and if it’s longer than 3 seconds, the soft WDT (Watch Dog Timer) will reset the ESP. If the soft WDT is disabled, after a little over 8 seconds, the hardware WDT will reset the chip.
From a microcontroller’s perspective however, 3 seconds is a very long time (240 million clockcycles), so unless you do some extremely heavy number crunching, or sending extremely long strings over Serial, you won’t be affected by this. Just keep in mind that you add the yield();
inside your for
or while
loops that could take longer than, say 100ms.
Sources
This is where I got most of my information to writ this article, there’s some more details on the GitHub pages, if you’re into some more advanced stuff, like EEPROM or deep sleep etc.