거의 모든 ESP32
시리즈는 와이파이 연결을 기본적으로 지원합니다.
ESP32
는 아래와 같은 모드를 지원합니다.
- STA:
ESP32
가 AP(공유기) 에 연결되는 모드 - AP:
ESP32
가 AP가 되는 모드 - STA/AP: STA, AP 모드가 공존하는 모드
이번에는 ESP32
기기에서 와이파이 AP 에 연결하는 방법을 정리해 보았습니다.
세부적으로 파면 복잡하기 때문에 예제 소스코드에서 나온 내용만 우선 다뤄보겠습니다.
기본적인 Wi-Fi 연결 시나리오
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");
}
}