esp-idf/examples/wifi/getting_started/station at master · espressif/esp-idf
Espressif IoT Development Framework. Official development framework for Espressif SoCs. - espressif/esp-idf
예제
Wi-Fi Driver - ESP32-S3 - — ESP-IDF Programming Guide latest documentation
문서

거의 모든 ESP32 시리즈는 와이파이 연결을 기본적으로 지원합니다.

ESP32 는 아래와 같은 모드를 지원합니다.

  • STA: ESP32 가 AP(공유기) 에 연결되는 모드
  • AP: ESP32 가 AP가 되는 모드
  • STA/AP: STA, AP 모드가 공존하는 모드

이번에는 ESP32 기기에서 와이파이 AP 에 연결하는 방법을 정리해 보았습니다.

세부적으로 파면 복잡하기 때문에 예제 소스코드에서 나온 내용만 우선 다뤄보겠습니다.

기본적인 Wi-Fi 연결 시나리오

Wi-Fi Driver - ESP32-S3 - — ESP-IDF Programming Guide latest documentation

Init, Config, Start Phase

와이파이 연결 전, 아래 리소스를 초기화 하고, 할당하는 작업이 선행되어야 합니다.

  • NVS
  • Default Event Loop
  • NetIf/LwIP
  • Wi-Fi

NVS

ESP32 에서 와이파이 연결정보는 NVS 에 저장됩니다.

NVS 와이파이 라이브러리가 사용할 수 있도록 초기화 과정이 필요합니다.

이때 사용하는 함수는 nvs_flash_init() 함수로, ESP32 에서 와이파이 기능을 사용하기 전 한번 호출해 주어야 합니다.

esp_err_t ret = nvs_flash_init();
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);

Default Event Loop

와이파이등의 시스템 라이브러를 위한 이벤트 루프를 생성해 주어야 합니다.

esp_event_loop_create_default() 함수를 호출하여 시스템 이벤트 루프를 만들어 주면 됩니다.

ESP_ERROR_CHECK(esp_event_loop_create_default());

NetIf, LwIP

NetIF 는 쉽게 말해 LwIP 의 네트워크 인터페이스 입니다.

TCP/IP 스택 제공, 실제 통신 기기의 추상화 계층 제공, 네트워크를 위한 여러가지 함수를 제공해주며, 마찬가지로 와이파이 연결전 초기화를 해주어야 합니다.

esp_netif_init() 함수를 사용하여 NetIF 초기화를 진행해 주면 됩니다.

ESP_ERROR_CHECK(esp_netif_init());

Wi-Fi

물리적인 와이파이 통신 모듈부터, 와이파이 연결을 위한 정보를 라이브러리에 전달해 주어 기기가 와이파이 연결이 가능하도록 설정해 주어야 합니다.

사용자 이벤트 그룹 생성

인터넷에 연결되기 전에 기기는 인터넷 관련 작업을 진행 할 수 없습니다.

와이파이 가 성공적으로 연결 혹은 실패 하기 전에 메인 태스크를 일시 정지 하고, 와이파이 이벤트 태스크로 부터 성공, 실패 여부를 전달 받아야 합니다.

이를 위해서 세마포어, 뮤텍스, 메세지큐 등 다양한 방법을 사용할 수 있지만 예제 소스코드에서는 이벤트 그룹을 사용하여 이를 구현하였습니다.

이벤트 그룹의 대한 내용은 추후 다루겠습니다.

당장은 와이파이 연결 성공, 혹은 실패 전 까지 메인 태스크를 Wait 하기 위해 사용하기 위해 이벤트 그룹을 사용한다고만 알고있어도 무방합니다.

전역 변수

// 이벤트 그룹 변수
EventGroupHandle_t s_wifi_event_group;

// 이벤트 비트 정의
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

초기화 과정

// 이벤트 그룹 생성
s_wifi_event_group = xEventGroupCreate();

이벤트 태스크

// WIFI_FAIL_BIT 설정
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);

// WIFI_CONNECTED_BIT 설정
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);

메인 태스크

// WIFI_CONNECTED_BIT 혹은 WIFI_FAIL_BIT 가 설정될 때 까지 wait
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
	WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
    pdFALSE,
    pdFALSE,
    portMAX_DELAY);

Wi-Fi Netif 생성

esp_netif_create_default_wifi_sta() 함수는 와이파이 연결에 사용할 NetIF 를 만드는 함수 입니다.

Wi-Fi 물리적 설정

기기의 와이파이 모듈의 연결 설정을 하는 과정으로, Rx/Tx 버퍼의 크기, 전용적인 리소스를 할당하는 과정 입니다.

기본값을 사용하는 과정은 아래와 같습니다.

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

Wi-Fi 이벤트 등록

와이파이 연결 동작 중 발생하는 이벤트 처리 핸들러 함수를 등록하는 과정입니다.
Default Event Loop 에서 발생하는 이벤트를 감시하며, 이벤트 인스턴스를 만들어서 즉시 할당한 모습입니다.

이벤트 루프에 관한 내용은 추후 다루겠습니다. 지금은 WIFI_EVENT 이벤트 그룹에서 발생한 모든 이벤트와, IP_EVENT 그룹에서 IP_EVENT_STA_GOT_IP 이벤트 발생 시 event_handler 함수가 호출된다고 알고 있으면 됩니다.

event_handler 함수의 내용은 밑에서 다루겠습니다.

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));

이벤트 핸들러 설정

와이파이 연결, IP 변경, 연결 끊김 등 다양한 이벤트가 발생될 수 있습니다.

발생되는 이벤트를 적재 적소 처리해야 기기의 안정적인 와이파이 연결을 보장할 수 있습니다.

와이파이 연결이 끊어지면 기기는 WIFI_EVENT / WIFI_EVENT_STA_DISCONNECTED 이벤트를 발생하며, 와이파이 재 연결의 구현은 프로그래머의 역할 입니다.

이벤트의 대한 내용은 추후 자세히 다뤄보겠습니다.

int s_retry_num = 0;

void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
	// WIFI_EVENT 에서 WIFI_EVENT_STA_START 이벤트가 발생한 경우
    // WIFI 초기화 후 성공적으로 esp_wifi_start() 함수가 호출 되었을때 실행 됩니다.
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
    
    	// 설정한 AP에 연결 합니다.
        esp_wifi_connect();
    }
    
    // WIFI_EVENT 에서 WIFI_EVENT_STA_DISCONNECTED 가 발생한 경우
    // 연결에 실패하거나, AP와 연결이 종료될 시 발생하는 이벤트 입니다.
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < 5) {
        
        	// WiFi 연결을 재시도 합니다.
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
        
        	// s_wifi_event_group 에서 WIFI_FAIL_BIT 를 설정합니다.
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    }
    
    // IP_EVENT 에서 IP_EVENT_STA_GOT_IP 이벤트 발생한 경우
    // AP에 연결된 후, DHCP 서버에서 성공적으로 IP를 할당 받으면 호출 됩니다.
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
    
    	// 이벤트 데이터를 파싱해서 할당된 IP의 정보를 출력 합니다.
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

STA 모드로 설정

esp_wifi_set_mode(WIFI_MODE_STA) 함수로 와이파이 모듈을 STA 모드로 설정 합니다.

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );

STA 모드 설정 적용

wifi_config_t wifi_config = {
	.sta = {
    	.ssid = "AP SSID",
    	.password = "AP Password",
    },
};

ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );

연결할 AP 정보를 작성한 후, 설정을 적용합니다.

STA 모드 시작

esp_wifi_start() 함수로 설정한 와이파이를 시작합니다.

ESP_ERROR_CHECK(esp_wifi_start());

예제

static const char *TAG = "wifi station";

// User Event Group
EventGroupHandle_t s_wifi_event_group;

// Event Definition
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

// Retry Counter
int s_retry_num = 0;

// Define Event Handler
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 < 5) {
            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) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

// Main Task
void app_main(void)
{
	// NVS Init
    esp_err_t ret = nvs_flash_init();
    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);
    
    
    // Create User Event group
    s_wifi_event_group = xEventGroupCreate();
    
    
    // NetIF Init
    ESP_ERROR_CHECK(esp_netif_init());
    
    
    // Create Default Event Loop
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    
    // Create WiFi NetIF
    esp_netif_create_default_wifi_sta();
    
    
    // Create Default WiFi Config and Set config
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    
    // Create Event Instance
    esp_event_handler_instance_t instance_any_id; // For WiFi Event
    esp_event_handler_instance_t instance_got_ip; // For NetIF Event
    
    
    // Event Handler Register
    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));
	
    
    // Set WiFi STA Mode
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    
    
    // Set WiFi Config
     wifi_config_t wifi_config = {
        .sta = {
            .ssid = "AP SSID",
            .password = "AP Password",
        },
    };
    
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    
    
    // Start WiFi
    ESP_ERROR_CHECK(esp_wifi_start() );
    
    
    // Wait User Event
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);


	// Get WiFi Connection result
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap");
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to ap");
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}