The objective of this ESP32 Arduino Tutorial is to explain how to get started using the WiFi functionalities of the ESP32, more precisely how to scan surrounding WiFi networks and how to connect to a specific WiFi network. All the tests performed here were made on a DFRobot’s ESP32 module, integrated in a ESP32 development board.
Introduction
The objective of this post is to explain how to get started using the WiFi functionalities of the ESP32, more precisely how to scan surrounding WiFi networks and how to connect to a specific WiFi network.
This post will also cover getting some parameters, such as the local IP of the ESP32 when connected to the WiFi network, and also its MAC address. We will also cover how to disconnect from the WiFi network.
Note that this tutorial sits on top of some previous tutorials that covered some aspects of connecting to a WiFi network with the ESP32. Nevertheless, this post has a much more vast set of functionalities used and it has the objective to serve as a getting started guide for WiFi on the ESP32, since it covers most of the functions that we typically perform when starting experimenting with the device.
If you are interested in a MicroPython version of how to connect to the a WiFi network with the ESP32, please consult this post. If you haven’t yet configured the Arduino IDE to work with the ESP32, please consult this post.
All the tests performed here were made on a DFRobot’s ESP32 module, integrated in a ESP32 development board. If you are using a ESP-WROOM-32 module alone, you can check here a guide on how to upload programs to the device using the Arduino IDE.
The setup function
The first thing we are going to do is including the WiFi.h library, which will allow us to connect to a WiFi network, amongst many other functionalities. You can check the header file definition here and the implementation file here.
For those who have a background using the ESP8266, note that this time the header file is generically called WiFi, as opposed to the ESP8266 library, which was named ESP8266WiFi.h.
#include <WiFi.h>
An important point to note is that there’s an extern variable defined in the header file called WiFi. This is a variable of WiFi class and as we will see, we will use it to access much of the WiFi functionality.
Next we will declare two global variables to hold our WiFi network credentials, more precisely the network name (SSID) and the password. This way, we can easily access and modify these variables. Note that you need to change the values by the ones that apply to your network.
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
Now we will move on to the Arduino setup function, where we will run all the remaining code. Since this is more of a network configuration tutorial, we will not need to use the main loop.
In the first line of our setup function, we will open a serial connection, so we can output the results of our program to the Arduino IDE serial monitor. To do so, we just need to call the begin function of the Serial object, passing as input the baud rate of the connection, in bits per second.
We will use a value of 115200 for the speed of the connection. It is important to retain this value in mind since we are going to need to respect it later, when establishing a serial connection to the ESP32 using the serial monitor of the Arduino IDE.
Serial.begin(115200);
After that, we will call two functions that we will define in the next sections. This way, we will encapsulate the code in different reusable functions, so it is much easier to read or use in other projects.
The first function, called scanNetworks, will scan the surrounding available WiFi networks and print some information about them. The second one, called connectToNetwork, will connect the device to a WiFi network.
scanNetworks();
connectToNetwork();
For now we will treat this functions as black boxes and assume they will work. After being connected to a network, our ESP32 should have a local IP assigned. To get it, we just need to call the localIP method of the WiFi extern variable.
Serial.println(WiFi.localIP());
It’s important to note that the local IP will depend on the network to which we are connected and may change on different connections.
Other interesting value that we can get is the MAC. So, to get the MAC address of the ESP32, we simply need to call the macAddress method, again on the WiFi extern variable.
Serial.println(WiFi.macAddress());
Note that this value is from the network interface of the ESP32, doesn’t change and can be retrieved even if we are not connected to a WiFi network.
To finalize the setup function, we will disconnect from the WiFi network. To do so, we just need to call the disconnect method of the WiFi variable. Note that this method receives as input a flag that allows to disable the station operation mode, by calling this method in its implementation.
Although this parameter value is set to false by default, we will pass a value of true to disable the WiFi. After that we will call the method to get the IP again, to confirm that we are disconnected from the network and no longer have an IP assigned.
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
The full code for the setup function can be seen bellow. Remember to change the global variables the the credentials of your WiFi network. Note that splitting the code into functions gives us a much clearer setup function, allowing to easily understand what we are doing without even looking to the implementation of the functions.
void setup() {
Serial.begin(115200);
scanNetworks();
connectToNetwork();
Serial.println(WiFi.macAddress());
Serial.println(WiFi.localIP());
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
}
Scanning for WiFi networks
Before connecting to an WiFi network, we will do a scan of the surrounding networks. Along with it, we will print some parameters for those networks, such as the network name (SSID), the signal strength, the MAC and the encryption type. As said before, this will be implemented in a function called scanNetworks.
To start performing a scan of networks, we just need to call the scanNetworks function of the previously mentioned WiFi extern variable. This call will initiate a scan and return the number of networks found upon a successful execution.
Note that this function receives two Boolean arguments which indicate if the scan should be performed in asynchronous mode and if hidden networks should be shown. Note however that in the header file of the class where the function is implemented both of the arguments have default values of false, so we can call the function in our code without passing any input parameter. For simplicity, that’s what we will do.
int numberOfNetworks = WiFi.scanNetworks();
Note that we stored the number of networks found in a variable. This will be needed for iterating the data structures where the information about those networks will be stored.
So, after the scanning is performed, we can access the parameters of each network with the functions shown here. Note that all of them receive as argument an integer with the number of the network, from 0 to the total number of networks detected minus 1.
In our code we will get and print the network name (SSID), the MAC address, the signal strength (RSSI) and the encryption type. Note however that there are also functions to retrieve the channel of the networks.
But before proceeding with that, we need to take in consideration that the encryption type is returned as an enum, which is defined here. So, we will define an auxiliary function that will receive the value of this enum and return a textual description indicating the encryption type. This way, we get a human readable result rather than an integer.
So, this function receives as input a variable of type wifi_auth_mode_t, which is the previously mentioned enum, and simply does a switch case that returns the textual description of each possible value. You can check bellow the implementation.
String translateEncryptionType(wifi_auth_mode_t encryptionType) {
switch (encryptionType) {
case (WIFI_AUTH_OPEN):
return "Open";
case (WIFI_AUTH_WEP):
return "WEP";
case (WIFI_AUTH_WPA_PSK):
return "WPA_PSK";
case (WIFI_AUTH_WPA2_PSK):
return "WPA2_PSK";
case (WIFI_AUTH_WPA_WPA2_PSK):
return "WPA_WPA2_PSK";
case (WIFI_AUTH_WPA2_ENTERPRISE):
return "WPA2_ENTERPRISE";
}
}
Now that we have a function that allows to translate the encryption types to strings, we can proceed on iterating the data. So, we do a loop using our number of networks variable as stopping condition and calling the previously mentioned functions to get the information of each scanned network.
This is shown bellow, with some additional prints to make the output more readable for the user. Note also the call to the translateEncryptionType function defined before.
for (int i = 0; i < numberOfNetworks; i++) {
Serial.print("Network name: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signal strength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MAC address: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryption type: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
Check the full function bellow. Note that there are some additional prints for making the output more complete.
void scanNetworks() {
int numberOfNetworks = WiFi.scanNetworks();
Serial.print("Number of networks found: ");
Serial.println(numberOfNetworks);
for (int i = 0; i < numberOfNetworks; i++) {
Serial.print("Network name: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signal strength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MAC address: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryption type: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
}
Connecting to a WiFi network
Now we are going to implement the connectToNetwork function, which will be responsible for handling the connection of the ESP32 to a specified network.
Note that there will be no link in the code with the networks obtained in the previous scan, although it is expected that the network that we want to connect to was listed in the previous call. Otherwise, it will most likely mean that it is not in the ESP32 range.
So start the connection, we simply call the Begin method on the previously mentioned WiFi class, passing as input both the network name and password defined in global variables.
Although for simplicity we are accessing the global variables directly, passing those values as input of the connectToNetwork would be more adequate in terms of reusability~of the function.
WiFi.begin(ssid, password);
Since the connection may take a while, we will next poll the WiFi variable to check when it is finished. To do so, we call the Status method, which will return a structure of type wl_status_t, defined here. The value we want to check for is WL_CONNECTED.
The code for this is shown bellow. Note that we are adding a delay between each iteration of the loop, to avoid constantly checking the variable. Also, this code will indefinitely poll for the connection until success, for simplicity. Naturally, for a more robust solution, you can create some sort of maximum poll attempts mechanism.
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Establishing connection to WiFi..");
}
The final code for the whole function can be seen bellow.
void connectToNetwork() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Establishing connection to WiFi..");
}
Serial.println("Connected to network");
}
The final code
You can check the full source code bellow, for an easy copy and paste. Don’t forget to change the values of the global variables by the credentials of your WiFi network. Also note that as said in a previous section, we are not going to use the main loop function, so we can leave it empty.
#include <WiFi.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";
String translateEncryptionType(wifi_auth_mode_t encryptionType) {
switch (encryptionType) {
case (WIFI_AUTH_OPEN):
return "Open";
case (WIFI_AUTH_WEP):
return "WEP";
case (WIFI_AUTH_WPA_PSK):
return "WPA_PSK";
case (WIFI_AUTH_WPA2_PSK):
return "WPA2_PSK";
case (WIFI_AUTH_WPA_WPA2_PSK):
return "WPA_WPA2_PSK";
case (WIFI_AUTH_WPA2_ENTERPRISE):
return "WPA2_ENTERPRISE";
}
}
void scanNetworks() {
int numberOfNetworks = WiFi.scanNetworks();
Serial.print("Number of networks found: ");
Serial.println(numberOfNetworks);
for (int i = 0; i < numberOfNetworks; i++) {
Serial.print("Network name: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signal strength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MAC address: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryption type: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
}
void connectToNetwork() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Establishing connection to WiFi..");
}
Serial.println("Connected to network");
}
void setup() {
Serial.begin(115200);
scanNetworks();
connectToNetwork();
Serial.println(WiFi.macAddress());
Serial.println(WiFi.localIP());
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
}
void loop() {}
Testing the code
To test the code, we just need to compile and upload it using the Arduino IDE, and then open the serial monitor to check the results. Don’t forget to select the correct board from the Tools -> Board menu. In my case, I need to select FireBeetle ESP32.
Also, on the serial monitor, use the baud rate of 115200 defined in the setup function, so the serial connection works correctly.
The result obtained in the serial console is shown bellow in figure 1 and yours should be similar. My ESP32 detected 3 WiFi networks and the parameters for each one of them (MAC address, network name, signal strength and encryption type) are shown as expected.
After that, the connection is successful and our device gets a local IP assigned by the router. Then, after disconnecting the ESP32 from the WiFi network, the local IP previously assigned is lost, as expected.
Also, the MAC address of our ESP32 is printed as defined in the code. For curiosity, the MAC address contains the information about the vendor. So, if you put the MAC you obtain in this lookup website, it should return Espressif, the makers of ESP32.
Figure 1 – Output of the ESP32 program.
A word on local IPs
As stated before, when we connect the ESP32 to a WiFi network, we will be given a local IP. Note that this IP is only valid inside that network and you cannot use it to reach the ESP32 from outside the network.
To do so, you need to port forward your router and interact with its public IP. The router will then forward the packets to the ESP32. Note however that the port forwarding procedure may not be trivial and it depends on the router used.
More than that, port forwarding the router may expose your network to security problems, so advance with caution if you intend to try it.
The same way entities outside the network will need to use the public IP of the router to reach the ESP32, all the data sent from the ESP32 to the outside of the network will have the public IP of the network rather than the private IP we obtained in the previous code.
Naturally this is just a simplification of how networks work and explaining it in more detail is outside the scope of this post. Nevertheless, I recommend you to do a little bit of research on that topic so you can understand better how things work, which will be useful when developing more complex applications that involve changing data between the ESP32 and other entities.In this post we just covered connecting to the network and not exchanging data, so the architecture of the program is very simple.