ESP32でのWi-Fi接続

概要

ESP32のWi-Fiまわりを調べ直してみました。モード名が変わっていたり、複数のSSIDを登録できたりといろいろ進化していました。接続方法やSSIDなどの設定方法まで

Wi-Fiのモード

https://github.com/espressif/arduino-esp32/blob/master/docs/en/api/wifi.rst

上記にまとまっていました。

#define WiFiMode_t   wifi_mode_t
#define WIFI_OFF     WIFI_MODE_NULL
#define WIFI_STA     WIFI_MODE_STA
#define WIFI_AP      WIFI_MODE_AP
#define WIFI_AP_STA  WIFI_MODE_APSTA
https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/src/WiFiType.h

WiFi.mode(WIFI_STA)などで使っていたモード名が変更されていました。defineされているので古い名前でも使えますが、なるべくなら新しい名前を使ったほうがよいと思います。ただし、公式のスケッチ例でも古い名前が多発していますのでひたすらPRを投げるのもいいのかもしれません。

typedef enum {
    WIFI_MODE_NULL = 0,  /**< null mode */
    WIFI_MODE_STA,       /**< WiFi station mode */
    WIFI_MODE_AP,        /**< WiFi soft-AP mode */
    WIFI_MODE_APSTA,     /**< WiFi station + soft-AP mode */
    WIFI_MODE_MAX
} wifi_mode_t;
https://github.com/espressif/esp-idf/blob/master/components/esp_wifi/include/esp_wifi_types_generic.h

上記に定義されています。Arduiono内部で定義していたものをESP-IDFと同じ定義を利用するように変更されていますね。

auto mode = WiFi.getMode();

上記で取得できます。

WiFi.mode(WIFI_MODE_APSTA);

上記で設定できます。WIFI_MODE_NULL以外のモードに変更したところからWi-Fi関連の初期化がはいり、消費電力が若干あがります。

WIFI_MODE_NULL

Wi-Fiを利用していない状態です。
WiFi.mode(WIFI_MODE_NULL)を呼び出すのではなく、WiFi.disconnect()を呼び出すことが多いです。
WiFi.getMode()などで現在の状況を確認するときには利用します。

WIFI_MODE_STA

ステーションモードで、Wi-Fiアクセスポイントのルーターなどに接続するときのモードです。

WIFI_MODE_AP

APモードで、ESP32自体がWi-Fiアクセスポイントになるモードです。
他の端末からの接続を受け付けます。

WIFI_MODE_APSTA

ステーションモードとAPモードを同時に利用する場合のモードです。

WIFI_MODE_MAX

enumの最後であることを表す名前であり、実際には利用できません。

切断

接続はちょっといろいろあるので、切断方法から説明します。

WiFi.mode(WIFI_MODE_NULL)

WiFi.mode(WIFI_MODE_NULL);

モードでOFFにする方法です。あまり利用されていないのでオススメしません。

WiFi.disconnect()

WiFi.disconnect();

よく使われている切断方法です。modeで切断するよりは明示的な切断になります。ただし、すべてのWi-Fi処理がオフになっているわけではなく、若干消費電力が上がった状態になっています。

WiFi.disconnect(true)

WiFi.disconnect(true);

完全にWi-Fi関連の電源をオフにして切断します。しばらくWi-Fiを利用しない場合には明示的に電源オフにしたほうが消費電力的に有利です。
とくにバッテリーで動かす場合にはこの切断の差を意識する必要があります。

APモード起動

WiFi.mode(WIFI_MODE_AP); // or WIFI_MODE_APSTA
WiFi.softAP(ssid, password);

上記で待ち受けるssidとパスフレーズを指定して起動させます。
ssidの名前は周りに公開されるので、実際には接続はさせませんが、名前を広報してMACアドレスを教えてあげることなどの利用方法もあります。ESP-NOWのスケッチ例などは子供側がAPモードで立ち上げて、親側がAPを検索して特定の名前を持つ端末を探すなどの例があります。

bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, bool ftm_responder = false)

定義は上記になっていました。

ssid

待ち受けをするアクセスポイントの名前です。1文字から63文字まで設定可能なはずです。

passphrase

パスワードです。パスワード設定なしのNULLか、WPA2の場合8文字以上63文字以下を指定します。

channel

1から13までのチャンネルを指定します。デフォルトは1になっています。しかし干渉を考えると1, 6, 11以外は指定しないほうが無難です。

ssid_hidden

SSIDを隠すかを選択します。通常は0で公開されていますので、Wi-Fiアクセスポイント一覧などに表示されます。1にして非公開にすると一覧には表示されませんので、明示的に指定して接続をする必要があります。

max_connection

最大同時接続数で1から4を指定できます。デフォルトは4なので変更する必要はあまりありません。

ftm_responder

Wi-Fiの電波を利用して、伝送時間から距離を計算するためのFTM(File Timing Measurement)を有効にするかのフラグです。機能になります。IEEE 802.11mcやWi-Fi RTTなどとも呼ばれています。

ESP32 SoC Table

上記で確認をしたところ、現在はESP32-S2、S3、C2、C3のみのサポートみたいです。C6がサポートしていないのはちょっと意外です。データシートを確認したところ、ハードウエア的にはサポートしていますが、ESP-IDFだとデフォルトではnの利用しないになっていました。

STAモード接続

WiFi.begin()

WiFi.begin();

Wi-Fiに接続したことがあるESP32の場合には初期化しない限り最後に利用したSSIDとパスフレーズを保存しているので、その情報で再接続を行います。
ブログなどではこの接続方法をよく使っています。自宅のSSIDなどを誤って書き込まないようにソースコードの中に埋め込まないですむからです。

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  delay(500);

  WiFi.begin("SSID", "KEY");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.println("WiFi Connected.");
  Serial.printf("IP Address  : ");
  Serial.println(WiFi.localIP());
}

void loop() {
}

ただし、初回は接続ができませんので、こんな感じで自宅のSSIDを埋め込んでスケッチを一度動かして本体に保存しておきます。

WiFi.begin(SSID, KEY)

#include <WiFi.h>

const char* ssid     = "your-ssid"; // Change this to your WiFi SSID
const char* password = "your-password"; // Change this to your WiFi password

void setup()
{
    Serial.begin(115200);
    delay(500);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
}

上記のようにdefineやconstでSSIDなどをプログラムの中に埋め込んで接続する方法です。従来はこの方法が一般的でしたが、いまではもう少し新しい方法が提供されています。

wl_status_t begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true)

あまり他の項目は利用しませんが、上記の定義になっています。

ssid, passphrase, channel

上記はAPモードのときと同じ情報です。

bssid

APの識別子で、通常はMACアドレスになります。同じSSIDが複数あり、特定のAPにのみ接続する場合にはこちらを指定します。

connect

接続をするかの設定になります。デフォルトはtrueで、falseにすると接続されませんので明示的に接続をしたい場合にはesp_wifi_connect()を呼び出すか、WiFi.reconnect()で接続をします。

WiFiMulti.run()

#include <WiFi.h>
#include <WiFiMulti.h>

WiFiMulti WiFiMulti;

void setup()
{
    Serial.begin(115200);
    delay(500);

    WiFiMulti.addAP("SSID", "passpasspass");
    WiFiMulti.addAP("SSID2", "passpasspass2");

    while(WiFiMulti.run() != WL_CONNECTED) {
        Serial.print(".");
        delay(500);
    }

    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
}

複数のSSIDを登録して、一番強いアクセスポイントに接続してくれるWiFiMultiクラスが提供されています。職場や自宅など端末を移動する場合にはこちらの接続方法を利用してください。

WiFi.begin(ssid, WPA2_AUTH_PEAP, EAP_IDENTITY, EAP_USERNAME, EAP_PASSWORD)

#include <WiFi.h>

#define EAP_IDENTITY "login"
#define EAP_USERNAME "login"
#define EAP_PASSWORD "password"
const char* ssid = "eduroam";
const char* host = "arduino.php5.sk";
int counter = 0;

void setup() {
  Serial.begin(115200);
  delay(500);

  WiFi.disconnect(true);
  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, WPA2_AUTH_PEAP, EAP_IDENTITY, EAP_USERNAME, EAP_PASSWORD);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address set: "); 
  Serial.println(WiFi.localIP());
}

WPA2-Enterpriseでの接続をするモードです。企業内やeduroamなどの学術無線LANローミングなどで利用されています。WiFiMultiだとWPA2-Enterpriseでの接続はできません。

SSIDの設定方法

SSIDをどうESP32に設定するのかは難しいところです。

事前設定+WiFi.begin()

WiFi.begin();

途中で紹介してありますが、1つしかSSIDを設定できないのと、事前に設定する必要があるのですこし面倒な方法です。ただし、ブログやGitなどにアップするコードの場合には安全です。

プログラム埋め込み

WiFi.begin("SSID", "KEY");

上記のように直接プログラムに埋め込むのが一番かんたんですが、複数の場所で利用する場合に不便です。

WiFiMulti.addAP("SSID", "passpasspass");
WiFiMulti.addAP("SSID2", "passpasspass2");

WiFiMultiをつかって、接続する可能性のあるSSIDをすべて登録するのが楽そうです。ただし、新しいSSIDに接続するためにはプログラムを転送しなおす必要があります。

事前にPreferencesに保存

#include <WiFi.h>
#include <WiFiMulti.h>
#include <Preferences.h>

WiFiMulti WiFiMulti;
Preferences preferences;

void setup() {
  Serial.begin(115200);
  delay(500);

  char wifi_ssid[37] = {};
  char wifi_key[66] = {};
  preferences.begin("wifi", true);

  preferences.getBytes("sta.ssid", wifi_ssid, sizeof(wifi_ssid));
  preferences.getBytes("sta.pswd", wifi_key, sizeof(wifi_key));
  if (wifi_ssid[0] !='') {
    WiFiMulti.addAP(wifi_ssid, wifi_key);
  }

  preferences.getBytes("sta.ssid2", wifi_ssid2, sizeof(wifi_ssid2));
  preferences.getBytes("sta.pswd2", wifi_key2, sizeof(wifi_key2));
  if (wifi_ssid[0] !='') {
    WiFiMulti.addAP(wifi_ssid, wifi_key);
  }

  while (WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
}

事前設定とかわりませんが、Preferencesクラスを利用するとESP32本体の中に設定を保存できます。自由な名前で保存可能ですので、複数のSSIDを保存しておきWiFiMultiを使うことも可能です。

シリアル経由で設定

上記にサンプルがありますが、シリアルからSSIDとパスフレーズを送信して、WiFi.begin(SSID, KEY)を実行させます。次回以降はWiFi.begin()で接続することで、変更する場合だけシリアルで設定しなおすことができます。

APモード+ブラウザで設定

APモードで立ち上げて、ESP32をWebサーバーとして動かします。HTMLのフォームからINPUTなどを利用してSSIDとパスフレーズを設定してもらい、その情報でWiFi.begin(SSID, KEY)をするか、複数のSSIDの場合にはPreferencesに保存してWiFiMultiで接続します。

SmartConfig

https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiSmartConfig/WiFiSmartConfig.ino

本家のスケッチ例をみてください。

  WiFi.mode(WIFI_AP_STA);
  WiFi.beginSmartConfig();

  //Wait for SmartConfig packet from mobile
  Serial.println("Waiting for SmartConfig.");
  while (!WiFi.smartConfigDone()) {
    delay(500);
    Serial.print(".");
  }

ESP32には標準機能としてスマホなどからSSIDを設定する仕組みが用意されています。上記のようにWiFi.beginSmartConfig()を呼び出すとすぐに待ち受け状態になります。

ただし、スマホやPCをESP32を接続したいアクセスポイントに2.4GHzで接続することが必要です。最近のメッシュWi-Fiルーターの場合には2.4GHzと5GHzが同じSSIDで動いており、5GHz優先のためSmartConfigで設定できない場合があります。

原理的には接続したいAPへSmartConfig用のパケットをスマホなどから投げて、それをESP32が盗み見してSSIDとパスフレーズを確認しています。暗号化されていない部分にSSIDとパスフレーズを送信することで実現しているので、セキュリティー的にはあまり好ましくないかもしれません。

WiFi.beginSmartConfig(SC_TYPE_ESPTOUCH_V2, "1234567890123456");

また、ESPTouch v2を利用することで事前に16文字の鍵情報を利用して暗号化することも可能です。
この他にWiFi.beginSmartConfig(SC_TYPE_AIRKISS )などでAirKissという似たプロトコルも利用可能です。

Wi-Fi Provisioning

SmartConfigと似ているのですが、利用するアプリが異なります。こちらはESP32にてsoftAPを立ち上げて、HTTP経由で設定を行うWi-Fiを利用したモードと、Bluetoothを利用して設定するモードがあります。

5GHzのメッシュWi-Fiルーターなどを利用していても設定できる利点があるのですが、スマホやPCをESP32のsoftAPに接続するのがちょっと面倒です。Bluetoothを利用するモードはかなり利用感もよいのですが、Bluetoothを使うとプログラムのサイズがかなり大きくなるので、標準設定だとフラッシュメモリの上限でエラーになる可能性があります。プログラムを修正して転送する時間も増えるので、他にBluetoothを利用していない場合には微妙な選択肢となります。

WiFi Easy Connect(DPP)

Device Provisioning Protocol(DPP)とも呼ばれますが、Wi-Fi Allianceの認定されたプロトコルです。QRコードなどを利用してWi-Fi設定が可能になります。Wi-FiアクセスポイントにあるQRコードをスマホで読み込んで、その後にESP32で鍵情報を含むQRコードを画面上もしくは、別途印刷したものなどを利用してスマホで読み込むことで、Wi-FiアクセスポイントにESP32を登録してくれる仕組みです。

WPA3 | TP-Link 日本

ただし、明示的にサポートを表明しているルーターが非常に少ないです。TP-Linkは対応を明記しています。またAndroid端末の場合にはWi-Fi設定の中から共有という機能を利用して、端末にWi-Fi接続情報を受け渡すことができるようです。

iOSはルーター側が対応している必要があり、対応ルーターが少ないので使いにくいです。Androidの場合にはどんなルーターでも端末の設定を転送できるので便利な機能となっています。

WPS

https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WPS/

ルーター側のボタンを押すと、2分以内はWi-Fiの設定ができる機能です。公式スケッチ例があります。
近年はセキュリティー的に誰でもボタンを押せば接続できるのは脆弱性なんじゃないかということで、利用が減ってきています。

(要カメラ)QRコード

Barcode Contents
ZXing ("Zebra Crossing") barcode scanning library for Java, Android - zxing/zxing

上記にありますが、近年はQRコードでWi-Fiアクセスポイント情報を受け渡すのが一般的です。WPSと違うのはゲスト用のQRコードを準備しておくことができることでしょう。

WIFI:T:WPA;S:mynetwork;P:mypass;;

文字列としてSのあとにSSID、Pのあとにパスフレーズを設定しておくとiOSでもAndroidでも読み込み可能です。TとSとPの順番は順不同なのでS、T、Pの順でも処理できるようにしておく必要があります。

カメラ付きのM5Stack CoreS3とかだとこの方法でいけそうですね。iPhoneからもQRコードは作成できるようです。ただし、あまりカメラ付きのESP32は少ないと思います。

(要マイク)サウンド通信

GitHub - ggerganov/ggwave: Tiny data-over-sound library
Tiny data-over-sound library. Contribute to ggerganov/ggwave development by creating an account on GitHub.

Arduinoライブラリにも複数登録がありましたが、ggwaveが使いやすそうでした。ESP32での送受信スケッチとブラウザやiOS、Androidでの送受信アプリがあります。

こちらのライブラリを使って、Wi-Fi設定のQRコードと同じ文字列を音声でESP32に流し込む方法も良さそうです。マイクが必要になりますがAIスタックチャンなどの用途の場合にはマイク搭載のESP32を利用するはずで問題にはなりにくいと思います。

まとめ

非常に雑多なエントリーになってしまいましたが、ESP32でこれだというSSIDなどを受け渡すWi-Fiプロビジョニングの方法はありません。

タッチパネルなどを搭載している場合にはSSIDを選択させてから手で入力する方法も取れるとは思いますがなかなか大変だと思います。

コメント