ESP32の最大の魅力の一つは、Wi-Fiを標準搭載していることです。
追加の無線モジュールを用意しなくても、ESP32単体でネットワークへ接続できるため、IoT機器やクラウド連携システムを比較的容易に構築できます。
ESP32で開発を始めると、
- STAモードとは何?
- APモードとの違いは?
- Wi-Fiへ接続するにはどうすればよい?
- ESP-IDFではどのように実装するの?
といった疑問を持つ方も多いでしょう。
この記事では、ESP32のSTA(Station)モードを中心に、Wi-Fi接続の基本からESP-IDFで実装する流れ、実務で意識している設計ポイントまで分かりやすく解説します。
STAモードとは?
STA(Station)モードは、ESP32をWi-Fiクライアントとして動作させるモードです。
一般家庭やオフィスのWi-Fiルーターへ接続する場合、多くはこのSTAモードを使用します。
例えば、
- 自宅のWi-Fiへ接続する
- 社内LANへ接続する
- クラウドへデータを送信する
- MQTTブローカーへ接続する
といった用途で利用されます。
APモードとの違い
SP32には、大きく分けて2つのWi-Fi動作モードがあります。
STA(Station)モード
ESP32がWi-Fiルーターへ接続します。
一般的なIoT機器では、このモードが最もよく利用されます。
AP(Access Point)モード
ESP32自身がアクセスポイントになります。
スマートフォンやPCがESP32へ直接接続できるため、
- 初期設定画面
- Wi-Fi設定
- 製品セットアップ
などで利用されます。
Wi-Fi接続の流れ
ESP-IDFでは、一般的に次の手順でWi-Fiへ接続します。
- NVSを初期化する
- ネットワークインターフェースを初期化する
- Wi-Fiドライバを初期化する
- STAモードを設定する
- SSIDとパスワードを設定する
- Wi-Fi接続を開始する
- 接続完了イベントを受け取る
- IPアドレス取得後に通信を開始する
ESP-IDFではイベント駆動で管理されるため、接続完了や切断などをイベントハンドラで受け取ります。
イベント駆動で管理する理由
Wi-Fi接続は瞬時に完了するわけではありません。
そのためESP-IDFでは、
- 接続開始
- 接続成功
- 切断
- IP取得
といった状態変化をイベントとして通知します。
これにより、接続が完了してから通信処理を開始できるため、安定したアプリケーションを構築できます。
ESP32でSTAモードを利用するメリット
ESP-IDFではWi-Fiドライバが提供されているため、比較的少ないコードでネットワーク接続を実装できます。
主なメリットは次のとおりです。
- 一般的なWi-Fiルーターへ接続できる
- クラウドサービスと連携しやすい
- MQTTやHTTP通信へ発展しやすい
- イベント駆動で安定した通信が可能
- ESP-IDF APIで管理しやすい
開発環境
マイコン:ESP32-C5
開発環境:Visual Studio Code
フレームワーク:ESP-IDF v5.5
接続モード:Wi-Fi STA(Station)
認証方式:WPA2-PSK
接続回路
今回はESP32-C5のWi-Fi機能を利用するため、外部回路は不要です。
USBケーブルでPCへ接続し、シリアルモニターで動作を確認します。
USB(Type-C)
│
│
┌───────────┐
│ ESP32-C5 │
│ │
│ Wi-Fi (STA Mode) │
│ │
└────┬──────┘
│
│
Wi-Fi Router
サンプルコードについて
以下のサンプルコードはESP-IDF v5.5を前提とした参考実装です。
使用するESP32シリーズやESP-IDFのバージョンによってAPIや設定方法が異なる場合があります。
実際に使用する際は、ご利用の開発環境に合わせて調整してください。
CMakeLists.txt
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES
esp_wifi
esp_event
esp_netif
nvs_flash
)
main.c
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"
#define WIFI_CONNECTED_BIT BIT0
static const char *TAG = "WIFI_STA";
static EventGroupHandle_t s_wifi_event_group;
static void wifi_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_LOGI(TAG, "Connecting...");
esp_wifi_connect();
}
else if ((event_base == WIFI_EVENT) &&
(event_id == WIFI_EVENT_STA_DISCONNECTED))
{
ESP_LOGW(TAG, "Disconnected");
esp_wifi_connect();
ESP_LOGI(TAG, "Retry...");
}
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,
"IP Address : " IPSTR,
IP2STR(&event->ip_info.ip));
xEventGroupSetBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT);
}
}
static void wifi_initialize(void)
{
s_wifi_event_group =
xEventGroupCreate();
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_ERROR_CHECK(
esp_event_handler_register(
WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL));
ESP_ERROR_CHECK(
esp_event_handler_register(
IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
.threshold.authmode =
WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(
esp_wifi_set_mode(
WIFI_MODE_STA));
ESP_ERROR_CHECK(
esp_wifi_set_config(
WIFI_IF_STA,
&wifi_config));
ESP_ERROR_CHECK(
esp_wifi_start());
ESP_LOGI(TAG,
"Wi-Fi Started");
}
void app_main(void)
{
ESP_ERROR_CHECK(
nvs_flash_init());
wifi_initialize();
xEventGroupWaitBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT,
pdFALSE,
pdTRUE,
portMAX_DELAY);
ESP_LOGI(TAG,
"Connected Successfully");
while (1)
{
ESP_LOGI(TAG,
"Application Running");
vTaskDelay(
pdMS_TO_TICKS(5000));
}
}
サンプルコードのポイント
NVSを初期化する
ESP-IDFのWi-Fi機能はNVSを使用するため、Wi-Fi初期化前にnvs_flash_init()を実行します。
ESP_ERROR_CHECK(
nvs_flash_init());
NVSの空きページ不足や保存形式の変更が発生した場合に備えるなら、次のように処理します。
esp_err_t nvs_result =
nvs_flash_init();
if ((nvs_result == ESP_ERR_NVS_NO_FREE_PAGES) ||
(nvs_result == ESP_ERR_NVS_NEW_VERSION_FOUND)) {
ESP_ERROR_CHECK(
nvs_flash_erase());
nvs_result =
nvs_flash_init();
}
ESP_ERROR_CHECK(nvs_result);
NVSを消去すると、同じ領域に保存されている他の設定も失われるため注意してください。
Event Groupを作成する
Wi-Fi接続完了をapp_main()へ通知するため、FreeRTOSのEvent Groupを作成します。
s_wifi_event_group =
xEventGroupCreate();
接続完了状態を示すビットとしてBIT0を使用します。
#define WIFI_CONNECTED_BIT BIT0
IPアドレス取得後に、接続完了ビットをセットします。
xEventGroupSetBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT);
app_main()では、接続完了ビットがセットされるまで待機します。
xEventGroupWaitBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT,
pdFALSE,
pdTRUE,
portMAX_DELAY);
これにより、IPアドレス取得前にHTTPやMQTTなどの通信処理を開始することを防げます。
ネットワーク機能を初期化する
最初に、ESP-IDFのネットワークインターフェース機能を初期化します。
ESP_ERROR_CHECK(
esp_netif_init());
続いて、Wi-FiやIPアドレス取得に関するイベントを処理するデフォルトイベントループを作成します。
ESP_ERROR_CHECK(
esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
戻り値も確認する場合は、次のように記述できます。
esp_netif_t *sta_netif =
esp_netif_create_default_wifi_sta();
if (sta_netif == NULL) {
ESP_LOGE(
TAG,
"Failed to create Wi-Fi STA interface");
return;
}
Wi-Fiドライバを初期化する
Wi-Fiドライバの設定には、ESP-IDFが用意している標準設定を使用します。
wifi_init_config_t cfg =
WIFI_INIT_CONFIG_DEFAULT();
作成した設定を使って、Wi-Fiドライバを初期化します。
ESP_ERROR_CHECK(
esp_wifi_init(&cfg));
WIFI_INIT_CONFIG_DEFAULT()には、使用中のESP-IDFに対応した標準設定が含まれています。
Wi-Fiイベントを登録する
Wi-Fi開始や切断などのイベントを受け取るため、イベントハンドラを登録します。
ESP_ERROR_CHECK(
esp_event_handler_register(
WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL));
ESP_EVENT_ANY_IDを指定しているため、すべてのWi-Fiイベントが同じイベントハンドラへ通知されます。
IPアドレス取得イベントは、別途登録します。
ESP_ERROR_CHECK(
esp_event_handler_register(
IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL));
ネットワーク通信は、アクセスポイントへの接続だけでなく、IPアドレス取得後に開始することが重要です。
Wi-Fi開始後に接続する
Wi-Fiドライバが開始すると、WIFI_EVENT_STA_STARTイベントが発生します。
if ((event_base == WIFI_EVENT) &&
(event_id == WIFI_EVENT_STA_START)) {
ESP_LOGI(
TAG,
"Connecting...");
esp_wifi_connect();
}
このイベントを受信してから、esp_wifi_connect()でアクセスポイントへの接続を開始します。
else if ((event_base == WIFI_EVENT) &&
(event_id == WIFI_EVENT_STA_DISCONNECTED)) {
ESP_LOGW(
TAG,
"Disconnected");
esp_wifi_connect();
ESP_LOGI(
TAG,
"Retry...");
}
この処理により、アクセスポイントの再起動や一時的な電波低下があった場合でも、再接続を試みます。
接続状態をEvent Groupで管理する場合は、切断時にビットもクリアします。
xEventGroupClearBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT);
IPアドレスを取得する
IPアドレスが割り当てられると、IP_EVENT_STA_GOT_IPイベントが発生します。
ip_event_got_ip_t *event =
(ip_event_got_ip_t *)event_data;
取得したIPアドレスをログへ表示します。
ESP_LOGI(
TAG,
"IP Address : " IPSTR,
IP2STR(&event->ip_info.ip));
その後、接続完了ビットをセットします。
xEventGroupSetBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT);
この時点で、HTTPやMQTTなどのネットワーク通信を開始できます。
SSIDとパスワードを設定する
接続先のSSIDとパスワードは、マクロで設定します。
#define WIFI_SSID \
"YOUR_WIFI_SSID"
#define WIFI_PASSWORD \
"YOUR_WIFI_PASSWORD"
実際のアクセスポイント情報へ変更してください。
#define WIFI_SSID \
"MyAccessPoint"
#define WIFI_PASSWORD \
"MyPassword"
実際の認証情報をブログや公開リポジトリへ掲載しないよう注意してください。
Wi-Fi設定を作成する
SSID、パスワード、最低認証方式をwifi_config_tへ設定します。
wifi_config_t wifi_config = {
.sta = {
.ssid = WIFI_SSID,
.password = WIFI_PASSWORD,
.threshold.authmode =
WIFI_AUTH_WPA2_PSK,
},
};
今回のサンプルでは、最低認証方式をWPA2-PSKに設定しています。
.threshold.authmode =
WIFI_AUTH_WPA2_PSK
オープンネットワークへ接続する場合は設定変更が必要ですが、製品用途では暗号化されたネットワークを推奨します。
STAモードへ設定する
ESP32-C5をWi-Fiクライアントとして使用するため、STAモードへ設定します。
ESP_ERROR_CHECK(
esp_wifi_set_mode(
WIFI_MODE_STA));
続いて、作成したWi-Fi設定をSTAインターフェースへ反映します。
ESP_ERROR_CHECK(
esp_wifi_set_config(
WIFI_IF_STA,
&wifi_config));
Wi-Fiドライバを開始する
設定完了後、Wi-Fiドライバを開始します。
ESP_ERROR_CHECK(
esp_wifi_start());
Wi-Fiドライバが開始すると、WIFI_EVENT_STA_STARTイベントが発生します。
接続完了まで待機する
app_main()では、IPアドレス取得後に接続完了ビットがセットされるまで待機します。
xEventGroupWaitBits(
s_wifi_event_group,
WIFI_CONNECTED_BIT,
pdFALSE,
pdTRUE,
portMAX_DELAY);
接続完了後、次のログを表示します。
ESP_LOGI(
TAG,
"Connected Successfully");
この後に、HTTP、MQTT、NTP、OTAなどの処理を追加できます。
実行結果
SSIDとパスワードを設定し、ESP32-C5へ書き込むと、正常接続時には次のようなログが表示されます。
I (410) WIFI_STA: Wi-Fi Started
I (420) WIFI_STA: Connecting...
I (2350) WIFI_STA: IP Address : 192.168.1.25
I (2350) WIFI_STA: Connected Successfully
I (2350) WIFI_STA: Application Running
I (7350) WIFI_STA: Application Running
I (12350) WIFI_STA: Application Running
アクセスポイントとの接続が切断された場合は、次のようなログが表示されます。
W (15420) WIFI_STA: Disconnected
I (15420) WIFI_STA: Retry...
Wi-Fi接続でよくあるトラブル
Wi-Fiへ接続できない
次の項目を確認してください。
- SSIDとパスワード
- 大文字・小文字
- 前後の空白
- アクセスポイントの稼働状態
- 認証方式
- 電波強度
- 接続台数制限
IPアドレスを取得できない
次の初期化順序を確認してください。
ESP_ERROR_CHECK(
esp_netif_init());
ESP_ERROR_CHECK(
esp_event_loop_create_default());
ESP_ERROR_CHECK(
esp_event_loop_create_default());
ルーターのDHCPサーバーが有効になっていることも確認してください。
NVS初期化に失敗する
次のエラーが発生する場合があります。
ESP_ERR_NVS_NO_FREE_PAGES
ESP_ERR_NVS_NEW_VERSION_FOUND
その場合はNVSを消去して再初期化します。
ESP_ERROR_CHECK(
nvs_flash_erase());
ESP_ERROR_CHECK(
nvs_flash_init());
再接続を無限に繰り返す
実製品では、再接続回数を制限します。
#define WIFI_MAXIMUM_RETRY 5
static int s_retry_count = 0;
再試行回数を確認して接続します。
if (s_retry_count <
WIFI_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_count++;
}
IPアドレス取得後に再試行回数をリセットします。
s_retry_count = 0;
実務での設計ポイント
Wi-Fi接続では、アクセスポイントへ接続しただけで通信可能とは限りません。
実際のネットワーク処理は、IP_EVENT_STA_GOT_IPを受信してから開始します。
また、Wi-Fi切断時には、MQTTやTCPソケットなど上位通信も再接続が必要になる場合があります。
実務では、次の流れで管理します。
- Wi-Fi切断を検出
- 上位通信を停止
- Wi-Fi再接続
- IPアドレス取得
- MQTTやHTTPを再接続
再接続を短時間で無限に繰り返すと、CPU負荷や消費電力が増加します。
再試行回数や待機時間、指数バックオフなども検討してください。
STAモードとAPモードの使い分け
STAモード
ESP32-C5が既存のWi-Fiルーターへ接続するモードです。
主な用途は次のとおりです。
- MQTT
- HTTP
- NTP
- OTA
- クラウド通信
- スマートホーム機器
APモード
ESP32-C5自身がアクセスポイントになるモードです。
主な用途は次のとおりです。
- 初期設定
- Wi-Fi認証情報の入力
- スマートフォンとの直接接続
- 保守用Web画面
AP+STAモード
STAでルーターへ接続しながら、APとして設定画面を提供することも可能です。
ただし、メモリ使用量やチャンネル条件、通信負荷を考慮する必要があります。
まとめ
STAモードは、ESP32-C5を既存のWi-Fiアクセスポイントへ接続するための基本機能です。
今回のサンプルでは、次の処理を実装しました。
- NVS初期化
- ネットワーク機能初期化
- イベントループ作成
- STAインターフェース作成
- Wi-Fiドライバ初期化
- SSIDとパスワード設定
- 接続開始
- 切断時の再接続
- IPアドレス取得
- Event Groupによる接続完了通知
Wi-Fiでは、接続、切断、IP取得、再接続をイベントとして管理することが重要です。
STAモードを理解すれば、HTTP、MQTT、OTA、NTPなど、ESP32を使ったIoT機能へ発展させられます。
技術は、経験から価値になる。
DLROW Design | 現場エンジニア