11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

開発環境(ESP-IDF)によるESP32のWiFi機能の使用方法

Last updated at Posted at 2022-02-06

記事の概要

ESP32のWiFi機能の基本的な使い方について説明します。
開発環境はESP-IDFとVScodeのPlatformIOを使用します。

0. ESP32のWiFi機能

ESP32のWiFi機能にはSTAモードとAPモードがあります。

STAモードは、ESP32がWiFiルータなどのアクセスポイントに接続し、ノードとして機能します。
APモードは、ESP32自体がアクセスポイントになって、スマホやPCがWiFiネットワークに接続できます。

この2つの違いは以下の動画を見ると分かりやすいです。
前半に紹介されるのがAPモードで、後半に紹介されるのがSTAモードです。

Arduino環境を使用すると簡単にできますが、今回はESP-IDF環境を使用する場合を説明します。
ESP-IDF環境の構築方法は以下を参照してください。

1. STAモード

以下のサンプルプログラムを例にSTAモードの使用方法を見てみます。

動作確認したい場合は、以下のサンプルのWiFi IDとパスワード、最大再接続回数を自分で設定して、自分のEPS32に書き込んでみてください。
シリアルモニタに

I (2089) wifi station: connected to ap SSID:myssid password:mypassword

が表示されれば成功です。

1.1 WiFiの初期化

1.1.1 NVSの初期化

EPS32に内蔵されたFlashメモリのnvs (non volatile storage)領域を初期化します。
nvsはWiFiライブラリで使用します。

nvs_flash_init関数を実行して、返り値に問題がなければ終了し、エラーならばnvs_flash_erase関数でメモリを消去してから、再度nvs_flash_init関数を実行します。

main.c
    //Initialize NVS
    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);

1.1.2 WiFiイベントの作成とWiFiの初期設定

WiFiイベントを作成し、イベントに対応する処理を紐付けます。
また、WiFi接続前にしておくべき幾つかの設定を行います。

まずWiFiイベント変数を作成します。

main.c
    s_wifi_event_group = xEventGroupCreate();

イベント検知するための初期設定関数'esp_event_loop_create_default'、およびnetifやSTAの初期化関数などを実行します。

main.c
    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

証明書を扱う変数を作成します。

main.c
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

そしてWIFI_EVENTIP_EVENTに対応する割り込み処理を登録します。

main.c
    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));

1.1.3 WiFiの設定

WiFi接続の設定をします。

main.c
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
	        .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false
            },
        },
    };

自分のWiFiの設定を定義しておきます。
安全性のためにはIDとパスワードをコードの中に含めず、外部から入力できるようにした方がいいと思いますが、今はこのままにします。

#define EXAMPLE_ESP_WIFI_SSID      "Matsui's WiFi"
#define EXAMPLE_ESP_WIFI_PASS      "abcdefg123456"

1.1.4 WiFi接続の開始

esp_wifi_set_mode関数でSTAモードに設定し、esp_wifi_set_config関数でWiFiの設定を有効化し、esp_wifi_start関数でWiFi接続を開始します。

main.c
    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() );

1.1.5 WiFiイベントの待機

WiFiイベントの応答を検知して、WiFi接続の成否を判定します。

xEventGroupWaitBits関数により、イベントの応答を待機して、変数`bits'に結果を格納します。

main.c
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

変数`bits'を確認して、WiFi接続に成功したか、失敗したかで処理を分岐させます。

main.c
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

1.2 割り込み処理

WiFiイベント発生時には割り込み処理が実行されます。
この割り込み処理は、xEventGroupWaitBits関数を実行中に発生します。

main.c
static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)

WIFI_EVENT_STA_STARTイベント発生時には、esp_wifi_connect関数を実行してWiFiネットワークに接続します。

main.c
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();

WIFI_EVENT_STA_DISCONNECTEDイベント発生時には、EXAMPLE_ESP_MAXIMUM_RETRYで設定した最大回数まで再接続を試みて、それにも失敗すればxEventGroupSetBitsでWiFiイベント変数のWiFi接続に関するビットにWIFI_FAIL_BITを設定して、再接続を終了します。

main.c
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");

WIFI_FAIL_BITを設定したことで、xEventGroupWaitBits関数を抜けて、1.1.5に先述した以下の分岐処理が実行されます。

else if (bits & WIFI_FAIL_BIT)

WiFi接続に成功後にはIP_EVENT_STA_GOT_IPイベントが発生するはずです。
イベントデータを取得し、xEventGroupSetBitsでWiFiイベント変数のWiFi接続に関するビットにWIFI_CONNECTED_BITを設定します。

main.c
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);
    }

WIFI_CONNECTED_BITを設定したことで、xEventGroupWaitBits関数を抜けて、1.1.5に先述した以下の分岐処理が実行されます。

if (bits & WIFI_CONNECTED_BIT)

1.3 ページリクエスト

ESP32はWiFiルータをアクセス・ポイントにして、他のサイトのデータ転送をリクエストできます。

そのためには、あらかじめWiFi接続成功時に、FreeRTOSを利用して、ページリクエスト用のタスクを生成しておくといいでしょう。
WIFI_CONNECTED_BITの分岐処理にタスク生成関数を追加します。

main.c
    if (bits & WIFI_CONNECTED_BIT) {        
        xTaskCreate(request_http, "request http", 10*configMINIMAL_STACK_SIZE, NULL, 10, NULL);
    }

そしてrequest_http関数を以下のように作成します。

configにアクセスするサイトのURLを設定してください。
なお、以下では証明書の要らないhttpサイトを対象にしています。
(httpsサイトにアクセスするには、アクセスしたいサイトのpem ファイルを入手して、以下の関数に設定を追加しないといけませんが、ここでは説明を省略します。)

main.c
void request_http(void *arg)
{
    esp_http_client_config_t config = {
        .url = "http://www.chiseki.go.jp/about/point/index.html",
        .event_handler = http_handler,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);

    if (esp_http_client_perform(client) != ESP_OK)
        ESP_LOGE(TAG, "HTTP REQUEST ERROR");
    esp_http_client_cleanup(client);

    vTaskDelete(NULL);
}

HTTPイベント発生時に実行する割り込み処理も作成します。
今回は、HTTP_EVENT_ON_DATAイベントにより、HTMLのデータ部分をコンソール画面に表示させました。
HTML構文が表示されるはずです。
他のイベント発生時には、特にすることがないのでメッセージだけを表示させています。

main.c
esp_err_t http_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id)
    {
        case HTTP_EVENT_ERROR:
            printf("HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            printf("HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            printf("HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            printf("HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            printf("%.*s\n", evt->data_len, (char *)evt->data);
            break;
        case HTTP_EVENT_ON_FINISH:
            printf("HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            printf("HTTP_EVENT_DISCONNECTED");
            break;
        default:
            break;
    }
    return ESP_OK;
}

これを利用して様々なサイトからのデータを収集することができます。
逆にこちらから相手サイトにデータ転送することもできますが、ここでは説明しません。

なお、サンプルプログラムにはページリクエストの部分はないので、以下のサイトやGithubの複数のサンプルを参照しました。

2. APモード

以下のサンプルプログラムを例にAPモードの使用方法を見てみます。

2.1 WiFiの初期化

2.1.1 NVSの初期化

STAモードの時と同様にします。

2.1.2 WiFiの初期設定

WiFi初期設定は、STAモードの時と同様です。
ただし、APモードの場合はesp_netif_create_default_wifi_ap関数を使います。

main.c
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_ap();

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

STAモードと異なりWiFiイベントは必須ではありませんが、WiFi接続時や切断時に何か処理を実行したいのならば、以下のように割り込み処理を登録しておきます。

main.c
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

2.1.3 WiFiの設定

WiFi接続の設定をします。

main.c
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };

今回は定数を以下のように設定しておきます。

#define EXAMPLE_ESP_WIFI_SSID "esp32_ap_test"
#define EXAMPLE_ESP_WIFI_PASS "my_pass_0123"
#define EXAMPLE_ESP_WIFI_CHANNEL 11
#define EXAMPLE_MAX_STA_CONN 1

2.1.4 WiFi接続の開始

esp_wifi_set_mode関数でAPモードに設定し、esp_wifi_set_config関数でWiFiの設定を有効化し、esp_wifi_start関数でWiFi接続を開始します。

main.c
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

2.2 割り込み処理

WiFi接続時にはWIFI_EVENT_AP_STACONNECTEDイベントが発生し、切断時にはWIFI_EVENT_AP_STADISCONNECTEDイベントが発生します。
以下では、これらのイベント発生時にメッセージを表示させています。
これらの処理がなくても、動作には問題ありません。

main.c
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

2.3 HTTPサーバー

以下を参考にしてHTTPサーバーを作成します。

2.3.1 HTTPサーバ設定

HTTPメソッドのPOSTとGETがリクエストされた際の割り込み処理を登録します。

main.c
httpd_uri_t uri_get = {
    .uri = "/",
    .method = HTTP_GET,
    .handler = get_handler,
    .user_ctx = NULL};

httpd_uri_t uri_post = {
    .uri = "/",
    .method = HTTP_POST,
    .handler = post_handler,
    .user_ctx = NULL};

void start_webserver(void)
{
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_handle_t server = NULL;
    if (httpd_start(&server, &config) == ESP_OK) {
        /* Register URI handlers */
        httpd_register_uri_handler(server, &uri_get);
        httpd_register_uri_handler(server, &uri_post);
    }
}

2.3.2 POSTとGETの割り込み処理

GETがリクエストされたならば、割り込み処理get_handlerを実行します。
httpd_resp_send関数により、HTMLデータを転送します。

POSTがリクエストされたならば、割り込み処理post_handlerを実行します。
httpd_req_recv関数により転送されたデータを受信します。
また、応答処理が必要ならば、httpd_resp_send関数によりデータを転送します。

main.c
static const char *html_sample 
    = "<html><form action=\"/\" method=\"post\">"
      "<label for=\"your_name\">Your Name:</label><br>"
      "<input type=\"text\" id=\"your_name\" name=\"your_name\"><br>"
      "<input type=\"submit\" value=\"Submit\">"
      "</form></html>";

esp_err_t get_handler(httpd_req_t *req)
{
    httpd_resp_send(req, html_sample , HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

esp_err_t post_handler(httpd_req_t *req)
{
    char content[100];

    size_t recv_size = MIN(req->content_len, sizeof(content));

    int ret = httpd_req_recv(req, content, recv_size);
    if (ret <= 0) {
        if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
            httpd_resp_send_408(req);
        }
        return ESP_FAIL;
    }

    const char resp[] = "Response";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

2.3.3 動作確認

まずスマホやPCのWiFi一覧から、先ほどに設定したWiFiのSSIDである"esp32_ap_test"を探して接続します。
パスワードは、先ほどに設定した"my_pass_0123"です。

次にブラウザを開いて、ホスト・アドレスに行きます。
例えば、今回私が使用しているESP32-WROOMのホスト・アドレスは、192.168.4.1です。
URLにこのアドレスを入力すれば、GETリクエストにより、html_sample で与えたHTMLが表示されます。

ブラウザのYour Nameの欄に名前を入力してSubmitボタンをクリックすれば、POSTリクエストにより、入力したデータをEPS32が受信し、"Response"と表示される応答画面に遷移します。

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?