ESP32 Wi-Fi

Em ESP32 Wi-Fi será feita uma análise e exploração técnica deste recurso de comunicação wireless disponível no ESP32, a fim de entender seu funcionamento e também compreender as características e benefícios na adoção deste hardware em projetos de conectividade, IoT e indústria 4.0.

A seguir, os tópicos que serão tratados neste artigo:

 

1. Análise das características do ESP32 Wi-Fi

2. Exemplo de aplicação com ESP32 Wi-Fi

 

Para o melhor aproveitamento deste conteúdo e também para agilizar o acesso, aqui estão alguns artigos relacionados para auxiliar o desenvolvimento:

 

Artigos relacionados

 

ESP-IDF

ESP32 DevKitC v1

Como é o fluxo de um programa para ESP32

ESP GPIO

 

1. Análise das características do ESP32 Wi-Fi

O ESP32 Wi-Fi implementa inteiramente os protocolos TCP/IP e o Wi-Fi MAC 802.11 b/g/n. Além disso, este módulo suporta as operações STA (Station) e SoftAP (Access Point), ou seja, modo estação o qual faz conexão com um roteador Wi-Fi e também o modo de ponto de acesso, o qual torna-se uma ponte entre dispositivos Wi-Fi e uma rede local.

Características do ESP32 Wi-Fi:

  • Suporte ao WPA/WPA2/WPA2-Enterprise e o WPS;
  • Suporte ao Modem-sleep;
  • Suporte ao protocolo específico Espressif, o qual se ativado, pode atingir até 1km de distância no tráfego de dados;
  • Throughput de até 20Mbit/s no TCP e 30Mbit/s no UDP;
  • Suporta o modo sniffer;
  • Suporta o fast scan e o all-channel scan;
  • Múltiplas antenas;
  • Informação do estado do canal;
  • 802.11 b/g/n;
  • 802.11n MCS0-7 em ambas largura de frequências 20MHz e 40MHz;
  • 802.11n MCS32 (RX);
  • 802.11n 0.4 us guard-interval;
  • Taxa de transmissão de até 150Mbps;
  • Potência de transmissão de até 20.5 dBm;
  • Diversidade de antenas (ESP32 suporta uma diversidade de antenas externas por meio de RF switch).

Características do Wi-Fi MAC:

  • 4 x interfaces Wi-Fi virtual;
  • Modo insfraestrutura simultânea BSS (Basic Service Set) Station, SoftAP e o Promiscuous mode;
  • Proteções RTS e CTS e Block ACK imediato;
  • Desfragmentação;
  • TX/RX A-MPDU, RX A-MSDU;
  • TXOP;
  • WMM;
  • Monitoramento de beacon automático (hardware TSF).

Dentre as várias características e vantagens listados acima, destaca-se os modos Station e SoftAP os quais flexibilizam a operação de um projeto e a respectiva conectividade, a stack TCP/IP embarcada bem como os mecanismos de criptografica, o throughput de dados tanto para TCP quanto para UDP, a potência máxima de trabalho e a flexibilidade de antenas para o rádio.

 

2. Exemplo de aplicação com ESP32 Wi-Fi

O exemplo de aplicação com o ESP32 Wi-Fi, a seguir, é baseado no código desenvolvido pela própria Espressif, porém com algumas modificações as quais não descaracterizam o objetivo deste projeto, ou seja, a conexão com um roteador Wi-Fi comportando-se com um device station. O exemplo original pode ser encontrado neste caminho …\esp-idf\examples\wifi\getting_started\station.

 

void app_main(void)
{
    //Initialize NVS (Non-Volatile Storage)
    esp_err_t ret = nvs_flash_init();

    // Verify return
    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // LED config
    gpio_config_t sLed;
    
    sLed.intr_type       = GPIO_INTR_DISABLE;    //disable interrupt    
    sLed.mode            = GPIO_MODE_OUTPUT;     //set as output mode    
    sLed.pin_bit_mask    = GPIO_OUTPUT_PIN_SEL;  //bit mask of the pins that you want to set,e.g.GPIO18/19    
    sLed.pull_down_en    = 0;                    //disable pull-down mode    
    sLed.pull_up_en      = 0;                    //disable pull-up mode    
    gpio_config(&sLed);                          //configure GPIO with the given settings

    // Task return variable
    BaseType_t xReturned;
    
    // Create Wi-Fi task
    xReturned = xTaskCreate(wifi_task   ,
                            "Wi-Fi task", 
                            4096        , 
                            NULL        , 
                            5           , 
                            NULL);
    // Error
    if(xReturned != pdPASS)
    {
        // Error: "xEventGroupCreate()" didn't return the handle
        ESP_LOGI(TAG, "Task memory allocation failed! Reset now...");
        
        // Reset ESP32
        esp_restart();
    }
    else
    {    
        // Create a event group to be used by "wifi_task()"
        s_wifi_event_group = xEventGroupCreate();

        // Check the returning value
        if(s_wifi_event_group == NULL)
        {
            // Error: "xEventGroupCreate()" didn't return the handle
            ESP_LOGI(TAG, "xEventGroupCreate failed! Reset now...");
            
            // Reset ESP32
            esp_restart();
        }
    }
}

 

A aplicação é iniciada com a chamada da função nvs_flash_init(), isto é, a inicialização da memória NVS ou Non-Volatile Storage, a NVS utiliza uma porção da memória Flash para armazenar dados que não podem ser perdidos com a desnergização do circuito eletrônico, pois é utilizado na inicialização aplicação, dados como o SSID e o PASSWORD da rede. Em seguida, o retorno desta função é testado para prosseguir ou não com o fluxo do programa.

A primeira diferença entre essa aplicação e a original feita pela Espressif, já pode ser vista na configuração do GPIO 2, pois este é o pino do LED azul do kit de desenvolvimento ESP32 DevikitC v1, assim, o configuramos para acionamento do LED quando o dispositivo estiver conectado a rede local. Outra diferença nesta aplicação é o fato de testar todos os retornos de funções, especialmente, na app_main() quando a task e o event group são criados, isto cria uma tratativa quando houver problemas na criação de ambos.

 

void wifi_init_sta(void)
{
    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

 

A função wifi_init_sta() contida dentro da wifi_task() fará todas as configurações necessárias para o funcionamento do ESP32 Wi-Fi,  começando por: esp_netif_init() que inicializa a stack TCP/IP subjacente, depois é chamada a função esp_event_loop_create_default() a qual cria um mecanismo de eventos gerados por uma fonte ou outros componentes, previamente configurados, possam ser tratados em um event handler também previamente indicado e configurado. Em seguida, é chamada esp_netif_create_default_wifi_sta() cria uma instância de interface de rede do tipo Station com a pilha TCP/IP. Depois é chamada a macro WIFI_INIT_CONFIG_DEFAULT(), esta macro permite a inicialização da estrutura do ESP32 Wi-Fi com todos os valores padrões, ou seja: buffers de recepção e transmissão, tipo de criptografia, ponteiros de funções relacionados, etc. Na sequência é a vez da esp_wifi_init() que carrega a estrutura do ESP32 Wi-Fi com os valores contidos em WIFI_INIT_CONFIG_DEFAULT(). Em esp_event_handler_instance_register() é passado por argumento os valores e tipos de eventos, bem como, a ISR (Interrupt Service Routine) ou handler que tratará a recepção desses eventos.

 

    wifi_config_t wifi_config = 
    {
        .sta = 
        {
            .ssid       = EXAMPLE_ESP_WIFI_SSID,
            .password   = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	        .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = 
            {
                .capable    = true,
                .required   = false
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wi-fi station init. finished.");
}

 

A variável de estrutura wifi_config informa os valores de login da rede local, ou seja, o SSID e o PASSWORD para conexão. Por fim, esses valores assim como o tipo de comportamento do device, ou seja, Station serão carregados em esp_wifi_set_config() e inicia o periférico através do esp_wifi_start() .

 

void wifi_task(void *pvParam)
{
    ESP_LOGI(TAG, "Wi-Fi task started!\n");

    wifi_init_sta();
	
    for(;;) 
    {
        /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
        * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
        EventBits_t bits = xEventGroupWaitBits( s_wifi_event_group                  ,
                                                WIFI_CONNECTED_BIT | WIFI_FAIL_BIT  ,
                                                pdTRUE                              ,
                                                pdFALSE                             ,
                                                portMAX_DELAY                       );

        /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
        * happened. */
        if (bits & WIFI_CONNECTED_BIT) 
        {
            ESP_LOGI(TAG                                , 
                    "connected to AP SSID: %s, PASS: %s",
                    EXAMPLE_ESP_WIFI_SSID               , 
                    EXAMPLE_ESP_WIFI_PASS);
            
            // Turn-on blue LED
            gpio_set_level(LED, 1);
        } 
        else if (bits & WIFI_FAIL_BIT) 
        {
            ESP_LOGI(TAG, 
                    "Failed to connect to SSID: %s, PASS: %s",
                    EXAMPLE_ESP_WIFI_SSID, 
                    EXAMPLE_ESP_WIFI_PASS);
            
            // Turn-off blue LED
            gpio_set_level(LED, 0);
        } 
        else 
        {
            ESP_LOGI(TAG, "UNEXPECTED EVENT");
        }		
		vTaskDelay(10/portTICK_PERIOD_MS);
    }
}

 

A wifi_task() se mantém bloqueada até que o valor do event group s_wifi_event_group seja atualizado, assim prosseguindo para informar ao console se o device desconectou da rede ou conectou acende o LED azul.

 

static void event_handler(  void* arg                   , 
                            esp_event_base_t event_base ,
                            int32_t event_id            , 
                            void* event_data)
{
    if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 
    {
        esp_wifi_connect();
    } 
    else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 
    {
        if(s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) 
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } 
        else 
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } 
    else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
    {
        // Notify IP received
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "IP received:" IPSTR, IP2STR(&event->ip_info.ip));

        // Set event group
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);

        // Reset couting variable
        s_retry_num = 0;
    }
}

 

O código acima refere-se ao event_handler() onde são tratados os eventos.

 

De maneira geral, o funcionamento da aplicação é dado da seguinte maneira: após o start do ESP32 Wi-Fi, a aplicação aguardará algum evento deste periférico para tratamento, assim se o event_handle() receber WIFI_EVENT_STA_START como evento, a tratativa é de conexão com o roteador local com o login previamente configurado em esp_wifi_set_config(), logo haverá outro momento de espera, seja de conectado ou não. Se o evento recebido for de conexão, em event_handle() o event group s_wifi_event_group será setado, assim desbloqueando a task wifi_task(), informando ao console que o device está conectado na rede CONFIG_ESP_WIFI_SSID com a respectiva senha CONFIG_ESP_WIFI_PASSWORD e, por fim, acionará o LED azul como indicador de conexão Wi-Fi. Se o evento for WIFI_EVENT_STA_DISCONNECTED, haverá N tentativas de conexão, valor configurado em EXAMPLE_ESP_MAXIMUM_RETRY, através da função esp_wifi_connect(), se ainda assim não houver sucesso na conexão, o s_wifi_event_group será atualizado, a task será desbloqueada e o console será informado que o device não obteve sucesso e se mantém desconectado.

 

A seguir, o código completo, veja:

 

/* WiFi station Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define LED                         2
#define GPIO_OUTPUT_PIN_SEL         (1ULL << LED)

#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY  CONFIG_ESP_MAXIMUM_RETRY

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "[Wi-Fi log]: ";

static int s_retry_num = 0;

static void event_handler(  void* arg                   , 
                            esp_event_base_t event_base ,
                            int32_t event_id            , 
                            void* event_data)
{
    if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 
    {
        esp_wifi_connect();
    } 
    else if(event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 
    {
        if(s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) 
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } 
        else 
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } 
    else if(event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
    {
        // Notify IP received
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "IP received:" IPSTR, IP2STR(&event->ip_info.ip));

        // Set event group
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);

        // Reset couting variable
        s_retry_num = 0;
    }
}

void wifi_init_sta(void)
{
    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = 
    {
        .sta = 
        {
            .ssid       = EXAMPLE_ESP_WIFI_SSID,
            .password   = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	        .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = 
            {
                .capable    = true,
                .required   = false
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wi-fi station init. finished.");
}


void wifi_task(void *pvParam)
{
    ESP_LOGI(TAG, "Wi-Fi task started!\n");

    wifi_init_sta();
	
    for(;;) 
    {
        /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
        * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
        EventBits_t bits = xEventGroupWaitBits( s_wifi_event_group                  ,
                                                WIFI_CONNECTED_BIT | WIFI_FAIL_BIT  ,
                                                pdTRUE                              ,
                                                pdFALSE                             ,
                                                portMAX_DELAY                       );

        /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
        * happened. */
        if (bits & WIFI_CONNECTED_BIT) 
        {
            ESP_LOGI(TAG                                , 
                    "connected to AP SSID: %s, PASS: %s",
                    EXAMPLE_ESP_WIFI_SSID               , 
                    EXAMPLE_ESP_WIFI_PASS);
            
            // Turn-on blue LED
            gpio_set_level(LED, 1);
        } 
        else if (bits & WIFI_FAIL_BIT) 
        {
            ESP_LOGI(TAG, 
                    "Failed to connect to SSID: %s, PASS: %s",
                    EXAMPLE_ESP_WIFI_SSID, 
                    EXAMPLE_ESP_WIFI_PASS);
            
            // Turn-off blue LED
            gpio_set_level(LED, 0);
        } 
        else 
        {
            ESP_LOGI(TAG, "UNEXPECTED EVENT");
        }		
		vTaskDelay(10/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    //Initialize NVS (Non-Volatile Storage)
    esp_err_t ret = nvs_flash_init();

    // Verify return
    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) 
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    // LED config
    gpio_config_t sLed;
    
    sLed.intr_type       = GPIO_INTR_DISABLE;    //disable interrupt    
    sLed.mode            = GPIO_MODE_OUTPUT;     //set as output mode    
    sLed.pin_bit_mask    = GPIO_OUTPUT_PIN_SEL;  //bit mask of the pins that you want to set,e.g.GPIO18/19    
    sLed.pull_down_en    = 0;                    //disable pull-down mode    
    sLed.pull_up_en      = 0;                    //disable pull-up mode    
    gpio_config(&sLed);                          //configure GPIO with the given settings

    // Task return variable
    BaseType_t xReturned;
    
    // Create Wi-Fi task
    xReturned = xTaskCreate(wifi_task   ,
                            "Wi-Fi task", 
                            4096        , 
                            NULL        , 
                            5           , 
                            NULL);
    // Error
    if(xReturned != pdPASS)
    {
        // Error: "xEventGroupCreate()" didn't return the handle
        ESP_LOGI(TAG, "Task memory allocation failed! Reset now...");
        
        // Reset ESP32
        esp_restart();
    }
    else
    {    
        // Create a event group to be used by "wifi_task()"
        s_wifi_event_group = xEventGroupCreate();

        // Check the returning value
        if(s_wifi_event_group == NULL)
        {
            // Error: "xEventGroupCreate()" didn't return the handle
            ESP_LOGI(TAG, "xEventGroupCreate failed! Reset now...");
            
            // Reset ESP32
            esp_restart();
        }
    }
}

 

Referências

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html

 

confira mais artigos

compartilhe e comente

Compartilhar no linkedin
Compartilhar no whatsapp
Compartilhar no facebook
Compartilhar no telegram

O uso e reprodução deste conteúdo deve creditar o autor e está licenciado conforme a licença Creative Commons – Atribuição Compartilhada 4.0 Internacional (CC BY-SA 4.0). 

Olá, meu nome é Eder Andrade.