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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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() .
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | /* 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