ESP32 Wifi C++ ESP-IDF (Station)

ESP32 WiFi station mode using the ESP-IDF in VS Code with C++

The WiFi module is what makes the ESP32 special. It provides a low-cost WiFi solution for embedded designs. This is what made the ESP devices ad popular as they are.

In this tutorial, we are going to set up a class to set the ESP32 up in station (STA) mode and connect to an access point or router. In a future tutorial, we are going to expand this class to add access point (AP) mode support and possibly a combination of the two.

ESP32 Wifi station mode C++ library

Create a new project

The easiest way to create a new project in VS Code for the ESP-IDF is to open the command palette by pressing ctrl+shift+P and typing ESP-IDF: Show Examples Projects and then selecting Sample Project from the list.

This will create all of the configuration files for your setup.

When the project has been created we need to close the project by clicking on File and then Close Folder.

Now navigate to where the project has been created and rename the folder named sample_project to CPPWIFI. Be careful not to add spaces in the folder name.

We are now going to restructure the folder a bit and create some new files. For more information on creating a project for C++ please see this tutorial: ESP-IDF C++ with CMake for ESP32.

Change the folder structure and add files and folders so that the project looks like the following:

Project Structure

I’m only showing the folder that we created. There will be several more folders that the Sample Project template has created.

This will be another library to be reusable in future projects that will make use of the Wifi so the project will only have the include and src folders.

The CmakeLists.txt files are also going to change when we integrate the libraries into other projects, however for now we are going to keep it like this to develop and test it as stand-alone projects.

Wifi project folder structure

Configure CMake

Change the content of CMakeLists.txt in the project root to the following:

# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.8)
set(CMAKE_CXX_STANDARD 17)
set(EXTRA_COMPONENT_DIRS src include)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(CPPWIFI)

Now change the content of CmakeLists.txt in the src folder to the following:

set(SOURCES main.cpp
            CPPWIFI/wifi.cpp)
            
idf_component_register(SRCS ${SOURCES}
                    INCLUDE_DIRS .  ../include/CPPWIFI
                    REQUIRES esp_wifi nvs_flash esp_event esp_netif lwip)

You will notice that we are adding several items after required which we have not done previously. The Wifi is dependent on several other services which we need to include for the Wifi to function properly.

This concludes the Cmake configuration for this project. The project is not configured to use C++ 17 and all the header files will be found and source files will resolve.

Define the Wifi class in the header

Open up wifi.h in the include\CPPWIFI folder and add the following code:

#pragma once

#include <cstring>

#include <mutex>

#include "esp_wifi.h"
#include "esp_event.h"


namespace WIFI
{
    class Wifi
    {
    public:
        enum class state_e
        {
            NOT_INITIALIZED,
            INITIALIZED,
            READY_TO_CONNECT,
            CONNECTING,
            WAITING_FOR_IP,
            CONNECTED,
            DISCONNECTED,
            ERROR
        };

    private:
        static esp_err_t _init();
        static wifi_init_config_t _wifi_init_cfg;
        static wifi_config_t _wifi_cfg;

        static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                                       int32_t event_id, void *event_data);
        static void ip_event_handler(void *arg, esp_event_base_t event_base,
                                     int32_t event_id, void *event_data);

        static state_e _state;
        static esp_err_t _get_mac(void);
        static char _mac_addr_cstr[13];
        static std::mutex _mutx;


    public:
        Wifi(void);

        void SetCredentials(const char *ssid, const char *password);
        esp_err_t Init();
        esp_err_t Begin(void);

        constexpr static const state_e &GetState(void) { return _state; }
        constexpr static const char *GetMac(void) { return _mac_addr_cstr; }
    }; // Wifi class

} // namaspace WIFI

Let’s take a closer look at the parts:

#pragma once

#include <cstring>

#include <mutex>

#include "esp_wifi.h"
#include "esp_event.h"

This is the guard to ensure that a header is not included twice and the includes for the Wifi driver.

    public:
        enum class state_e
        {
            NOT_INITIALIZED,
            INITIALIZED,
            READY_TO_CONNECT,
            CONNECTING,
            WAITING_FOR_IP,
            CONNECTED,
            DISCONNECTED,
            ERROR
        };

We are going to treat the wifi like a state machine where the wifi can be in any one state at any given time. We can then decide what needs to happen based on what state the wifi is in. This enables us to build a robust wifi implementation. In the simple example at the end of this tutorial, we are going to check if the wifi has been disconnected every second and reconnect if it was.

This enum must be public in order to use it outside of this class.

    private:
        static esp_err_t _init();
        static wifi_init_config_t _wifi_init_cfg;
        static wifi_config_t _wifi_cfg;

        static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                                       int32_t event_id, void *event_data);
        static void ip_event_handler(void *arg, esp_event_base_t event_base,
                                     int32_t event_id, void *event_data);

        static state_e _state;
        static esp_err_t _get_mac(void);
        static char _mac_addr_cstr[13];
        static std::mutex _mutx;

These are all the private methods and variables. All of the variables and methods here have the static modifier. This is useful because we will only ever have one instance of this class. If for some reason we have more than one, then all of the variables will hold the same value and the same private methods will be called.

Let’s look at the purpose of each:

  • _init() This method will initialize the Wifi
  • _wifi_init_cfg This variable to hold the wifi initialization configuration data
  • _wifi_cfg This variable will hold the Wifi configuration data
  • wifi_event_handler() An event handler that will trigger whenever the state of the wifi changes.
  • ip_event_handler() An even handler that will trigger whener the state of the IP changes.
  • _state A enum type variable to keep track of which state the wifi is in
  • _get_mac A method that returns the MAC address of the ESP32 module
  • _mutx A mutex to make certain methods thread safe.
    public:
        Wifi(void);

        void SetCredentials(const char *ssid, const char *password);
        esp_err_t Init();
        esp_err_t Begin(void);

        constexpr static const state_e &GetState(void) { return _state; }
        constexpr static const char *GetMac(void) { return _mac_addr_cstr; }
    }; // Wifi class

There are not many public methods. Here we have the constructor, a method to specify the SSID and provide a password.

Init() will initialize the wifi and Begin() attempts to connect to the AP.

GetState() return the current state of the wifi and GetMac() return the MAC address of the ESP32 module.

Implement the class methods

If you have not yet created wifi.cpp in the src folder then do so now and add the following code into the file:

#include "Wifi.h"

namespace WIFI
{
    // Statics
    char Wifi::_mac_addr_cstr[]{};
    std::mutex Wifi::_mutx{};
    Wifi::state_e Wifi::_state{state_e::NOT_INITIALIZED};
    wifi_init_config_t Wifi::_wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    wifi_config_t Wifi::_wifi_cfg{};

    // Wifi Contrustor
    Wifi::Wifi(void)
    {
        if (!_mac_addr_cstr[0])
        {
            if (ESP_OK != _get_mac())
            {
                esp_restart();
            }
        }
    }

    void Wifi::wifi_event_handler(void *arg, esp_event_base_t event_base,
                                  int32_t event_id, void *event_data)
    {
        if (WIFI_EVENT == event_base)
        {
            const wifi_event_t event_type{static_cast<wifi_event_t>(event_id)};

            switch (event_type)
            {
            case WIFI_EVENT_STA_START:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::READY_TO_CONNECT;
                break;
            }

            case WIFI_EVENT_STA_CONNECTED:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::WAITING_FOR_IP;
                break;
            }

            case WIFI_EVENT_STA_DISCONNECTED:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::DISCONNECTED;
                break;
            }

            default:
                break;
            }
        }
    }

    void Wifi::ip_event_handler(void *arg, esp_event_base_t event_base,
                                int32_t event_id, void *event_data)
    {
        if (IP_EVENT == event_base)
        {
            const ip_event_t event_type{static_cast<ip_event_t>(event_id)};

            switch (event_type)
            {
            case IP_EVENT_STA_GOT_IP:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::CONNECTED;
                break;
            }

            case IP_EVENT_STA_LOST_IP:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                if (state_e::DISCONNECTED != _state)
                {
                    _state = state_e::WAITING_FOR_IP;
                }
                break;
            }

            default:
                break;
            }
        }
    }

    esp_err_t Wifi::Begin(void)
    {
        std::lock_guard<std::mutex> connect_guard(_mutx);

        esp_err_t status{ESP_OK};

        switch (_state)
        {
        case state_e::READY_TO_CONNECT:
        case state_e::DISCONNECTED:
            status = esp_wifi_connect();
            if (ESP_OK == status)
            {
                _state = state_e::CONNECTING;
            }
            break;
        case state_e::CONNECTING:
        case state_e::WAITING_FOR_IP:
        case state_e::CONNECTED:
            break;
        case state_e::NOT_INITIALIZED:
        case state_e::INITIALIZED:
        case state_e::ERROR:
            status = ESP_FAIL;
            break;
        }
        return status;
    }

    esp_err_t Wifi::_init()
    {
        std::lock_guard<std::mutex> mutx_guard(_mutx);

        esp_err_t status{ESP_OK};

        if (state_e::NOT_INITIALIZED == _state)
        {
            status |= esp_netif_init();
            if (ESP_OK == status)
            {
                const esp_netif_t *const p_netif = esp_netif_create_default_wifi_sta();

                if (!p_netif)
                {
                    status = ESP_FAIL;
                }
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_init(&_wifi_init_cfg);
            }

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(WIFI_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &wifi_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(IP_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &ip_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_set_mode(WIFI_MODE_STA);
            }

            if (ESP_OK == status)
            {
                _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
                _wifi_cfg.sta.pmf_cfg.capable = true;
                _wifi_cfg.sta.pmf_cfg.required = false;

                status = esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg);
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_start(); // start Wifi
            }

            if (ESP_OK == status)
            {
                _state = state_e::INITIALIZED;
            }
        }

        else if (state_e::ERROR == _state)
        {
            _state = state_e::NOT_INITIALIZED;
        }

        return status;
    }

    void Wifi::SetCredentials(const char *ssid, const char *password)
    {
        memcpy(_wifi_cfg.sta.ssid, ssid, std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid)));

        memcpy(_wifi_cfg.sta.password, password, std::min(strlen(password), sizeof(_wifi_cfg.sta.password)));
    }

    esp_err_t Wifi::Init()
    {
        return _init();
    }

    // Get default MAC from API and convert to ASCII HEX
    esp_err_t Wifi::_get_mac(void)
    {
        uint8_t mac_byte_buffer[6]{};

        const esp_err_t status{esp_efuse_mac_get_default(mac_byte_buffer)};

        if (ESP_OK == status)
        {
            snprintf(_mac_addr_cstr, sizeof(_mac_addr_cstr), "%02X%02X%02X%02X%02X%02X",
                     mac_byte_buffer[0],
                     mac_byte_buffer[1],
                     mac_byte_buffer[2],
                     mac_byte_buffer[3],
                     mac_byte_buffer[4],
                     mac_byte_buffer[5]);
        }

        return status;
    }

} // namespace WIFI

Don’t fear if it looks intimidating, it’s fairly simple when we look at each method.

Let’s take a closer look at the methods:

    Wifi::Wifi(void)
    {
        if (!_mac_addr_cstr[0])
        {
            if (ESP_OK != _get_mac())
            {
                esp_restart();
            }
        }
    }

The class constructor is called when an object of the class is created. It is then that we attempt to get the MAC address of the ESP32 device. If this fails then we restart the device.

    void Wifi::wifi_event_handler(void *arg, esp_event_base_t event_base,
                                  int32_t event_id, void *event_data)
    {
        if (WIFI_EVENT == event_base)
        {
            const wifi_event_t event_type{static_cast<wifi_event_t>(event_id)};

            switch (event_type)
            {
            case WIFI_EVENT_STA_START:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::READY_TO_CONNECT;
                break;
            }

            case WIFI_EVENT_STA_CONNECTED:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::WAITING_FOR_IP;
                break;
            }

            case WIFI_EVENT_STA_DISCONNECTED:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::DISCONNECTED;
                break;
            }

            default:
                break;
            }
        }
    }

    void Wifi::ip_event_handler(void *arg, esp_event_base_t event_base,
                                int32_t event_id, void *event_data)
    {
        if (IP_EVENT == event_base)
        {
            const ip_event_t event_type{static_cast<ip_event_t>(event_id)};

            switch (event_type)
            {
            case IP_EVENT_STA_GOT_IP:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                _state = state_e::CONNECTED;
                break;
            }

            case IP_EVENT_STA_LOST_IP:
            {
                std::lock_guard<std::mutex> state_guard(_mutx);
                if (state_e::DISCONNECTED != _state)
                {
                    _state = state_e::WAITING_FOR_IP;
                }
                break;
            }

            default:
                break;
            }
        }
    }

These are the two event handlers that are triggered whenever a Wifi or IP event gets triggered on the default event loop. Both these event handlers do the same thing with the only difference being the event base that triggers each.

The event ID indicates which event in the event base triggered the event handler. This information tells us which state the wifi is in.

The if (WIFI_EVENT == event_base) and if (IP_EVENT == event_base) methods should be redundant because we specify which event base triggers with event handler in the _init() method, however, we keep it in to handle false triggers even though it should not happen.

Finally, we enable a mutex lock, std::lock_guard<std::mutex> state_guard(_mutx), before we change the _state variable to ensure that the state is never in an undefined state should two threads attempt to change the state at the same time.

    esp_err_t Wifi::Begin(void)
    {
        std::lock_guard<std::mutex> connect_guard(_mutx);

        esp_err_t status{ESP_OK};

        switch (_state)
        {
        case state_e::READY_TO_CONNECT:
        case state_e::DISCONNECTED:
            status = esp_wifi_connect();
            if (ESP_OK == status)
            {
                _state = state_e::CONNECTING;
            }
            break;
        case state_e::CONNECTING:
        case state_e::WAITING_FOR_IP:
        case state_e::CONNECTED:
            break;
        case state_e::NOT_INITIALIZED:
        case state_e::INITIALIZED:
        case state_e::ERROR:
            status = ESP_FAIL;
            break;
        }
        return status;
    }

This method attempts to create a connection to the access point by doing a few things:

  • Locks any variable with a mutex
  • Check if the wifi is in an appropriate state to attempt to make a connection
  • Call the API function to connect to the wifi and set the state to CONNECTING if the wifi is in the READY_TO_CONNECT or DISCONNECTED state.
  • If the wifi is in the CONNECING, WAITING_FOR_IP or CONNECTED state then the method does nothing.
  • If the wifi is in the NOT_INITIALIZED, INITIALIZED or ERROR state then the method returns an error code.
    esp_err_t Wifi::_init()
    {
        std::lock_guard<std::mutex> mutx_guard(_mutx);

        esp_err_t status{ESP_OK};

        if (state_e::NOT_INITIALIZED == _state)
        {
            status |= esp_netif_init();
            if (ESP_OK == status)
            {
                const esp_netif_t *const p_netif = esp_netif_create_default_wifi_sta();

                if (!p_netif)
                {
                    status = ESP_FAIL;
                }
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_init(&_wifi_init_cfg);
            }

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(WIFI_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &wifi_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(IP_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &ip_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_set_mode(WIFI_MODE_STA);
            }

            if (ESP_OK == status)
            {
                _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
                _wifi_cfg.sta.pmf_cfg.capable = true;
                _wifi_cfg.sta.pmf_cfg.required = false;

                status = esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg);
            }

            if (ESP_OK == status)
            {
                status = esp_wifi_start(); // start Wifi
            }

            if (ESP_OK == status)
            {
                _state = state_e::INITIALIZED;
            }
        }

        else if (state_e::ERROR == _state)
        {
            _state = state_e::NOT_INITIALIZED;
        }

        return status;
    }

This method initializes the wifi. The method does several things and checks for success after most steps before continuing to the next. Let’s look at the individual parts:

std::lock_guard<std::mutex> mutx_guard(_mutx);

Place a mutex lock on the method to make a thread-safe.

if (state_e::NOT_INITIALIZED == _state)

Ensure that the wifi is in the NOT_INITIALIZED. If the wifi is in any other state then the method returns without doing anything else.

            status |= esp_netif_init();
            if (ESP_OK == status)
            {
                const esp_netif_t *const p_netif = esp_netif_create_default_wifi_sta();

                if (!p_netif)
                {
                    status = ESP_FAIL;
                }
            }

Start the NETIF service. NETIF is an intermediary between the IO driver and the network stack. If this fails then the method returns with an error code.

            if (ESP_OK == status)
            {
                status = esp_wifi_init(&_wifi_init_cfg);
            }

Initialize the wifi with the settings contained in _wifi_init_cfg.

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(WIFI_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &wifi_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

            if (ESP_OK == status)
            {
                status = esp_event_handler_instance_register(IP_EVENT,
                                                             ESP_EVENT_ANY_ID,
                                                             &ip_event_handler,
                                                             nullptr,
                                                             nullptr);
            }

Here we register our two event handlers to the default loop. This will enable events to be triggered whenever the state of the wifi change.

            if (ESP_OK == status)
            {
                status = esp_wifi_set_mode(WIFI_MODE_STA);
            }

Here we set the wifi to station mode. Station mode means that the ESP device will connect to another device such as a router or an access point. The ESP device can also be configured as an access point that allows other devices to connect to the ESP device.

An example of this would be your phone. When you connect your phone to a wifi network, then your mobile phone operates in station mode. But when you set your phone as a mobile hotspot, then your phone is operating in AP mode and other devices can connect to your phone.

            if (ESP_OK == status)
            {
                _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
                _wifi_cfg.sta.pmf_cfg.capable = true;
                _wifi_cfg.sta.pmf_cfg.required = false;

                status = esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg);
            }

Here we configure the wifi with the _wifi_cfg variable. The _wifi_cfg variable also contains the SSID and password.

            if (ESP_OK == status)
            {
                status = esp_wifi_start(); // start Wifi
            }

With all the configuration done we can now start the wifi.

            if (ESP_OK == status)
            {
                _state = state_e::INITIALIZED;
            }

With the initialization completed, we can now update the state to INITIALIZED. When the wifi driver has started, an event will trigger and the state will be set to READY_TO_CONNECT in the event handler that we created earlier.

    void Wifi::SetCredentials(const char *ssid, const char *password)
    {
        memcpy(_wifi_cfg.sta.ssid, ssid, std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid)));

        memcpy(_wifi_cfg.sta.password, password, std::min(strlen(password), sizeof(_wifi_cfg.sta.password)));
    }

In this method, we accept SSID and password as arguments and set the relevant fields in the _wifi_cfg wifi configuration struct. Because we need to use c-style strings, we have to use memcpy.

However, we need to be careful to not exceed the buffer length of the variables in the _wifi_cfg struct because that will cause a buffer overrun error, or worse not trigger an error and cause undefined behavior. That is why we limit the size to be copied to the sizes of the variable in the structs.

    esp_err_t Wifi::Init()
    {
        return _init();
    }

This public Init method only calls the private init method.

    esp_err_t Wifi::_get_mac(void)
    {
        uint8_t mac_byte_buffer[6]{};

        const esp_err_t status{esp_efuse_mac_get_default(mac_byte_buffer)};

        if (ESP_OK == status)
        {
            snprintf(_mac_addr_cstr, sizeof(_mac_addr_cstr), "%02X%02X%02X%02X%02X%02X",
                     mac_byte_buffer[0],
                     mac_byte_buffer[1],
                     mac_byte_buffer[2],
                     mac_byte_buffer[3],
                     mac_byte_buffer[4],
                     mac_byte_buffer[5]);
        }

        return status;
    }

This method gets the MAC address of the ESP device, formats it into a more readable format. Having the MAC address is useful to use a unique identifier.

Connect to a Wifi network

That’s it for the wifi class. However, we need to do some things outside of the class to enable it to work. Notably, we need to create the default event loop for our event handlers to function and initialize the non-volatile storage.

I did not add the event loop or nvs initialization to the wifi class because it will typically also be used by other components.

main.h

Copy the following code into main.h in the src folder:

#include <string>
#include <iostream>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"

#include "wifi.h"

class Main final
{
private:
public:
    void run(void);
    void setup(void);

    WIFI::Wifi::state_e wifiState { WIFI::Wifi::state_e::NOT_INITIALIZED };
    WIFI::Wifi Wifi;
};

Here we have only created an object of the wifi class and a variable to keep track of the state of the wifi initialized to state: NOT_INITIALIZED.

main.cpp

Copy the following code into main.cpp in the src folder:

#include "main.h"

Main App;

void Main::run(void)
{
    wifiState = Wifi.GetState();

    switch (wifiState)
    {
    case WIFI::Wifi::state_e::READY_TO_CONNECT:
        std::cout << "Wifi Status: READY_TO_CONNECT\n";
        Wifi.Begin();
        break;
    case WIFI::Wifi::state_e::DISCONNECTED:
        std::cout << "Wifi Status: DISCONNECTED\n";
        Wifi.Begin();
        break;
    case WIFI::Wifi::state_e::CONNECTING:
        std::cout << "Wifi Status: CONNECTING\n";
        break;
    case WIFI::Wifi::state_e::WAITING_FOR_IP:
        std::cout << "Wifi Status: WAITING_FOR_IP\n";
        break;
    case WIFI::Wifi::state_e::ERROR:
        std::cout << "Wifi Status: ERROR\n";
        break;
    case WIFI::Wifi::state_e::CONNECTED:
        std::cout << "Wifi Status: CONNECTED\n";
        break;
    case WIFI::Wifi::state_e::NOT_INITIALIZED:
        std::cout << "Wifi Status: NOT_INITIALIZED\n";
        break;
    case WIFI::Wifi::state_e::INITIALIZED:
        std::cout << "Wifi Status: INITIALIZED\n";
        break;
    }
}

void Main::setup(void)
{
    esp_event_loop_create_default();
    nvs_flash_init();

    Wifi.SetCredentials("your_ssid", "your_password");
    Wifi.Init();
}

extern "C" void app_main(void)
{
    App.setup();
    while (true)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        App.run();
    }
}

In this main method we do the following in order:

  1. Create the event loop
  2. Initialize the non-volitile storage
  3. Enter our wifi credentials (SSID and Password)
  4. Initialise the wifi
  5. Wait for the wifi state to change to READY_TO_CONNECT
  6. Connect to a wifi network
  7. Try to reconnect if the ESP32 is dropped and the state changes to DISCONNECTED

Run the program

You can now compile the project and upload the program to your ESP32 device. The terminal output should update the state of the wifi every second.

Ready to connect terminal output
Connected terminal output

Conclusion

This class is all that we need to connect to a wifi network with our ESP32 devices. We can now connect to an online NTP server to get the time or useful thing that required network or internet connectivity.

To download this project from Github, click here.

Thanks for reading

Similar Posts