1
1

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.

ESP32のWiFiおよびtzapu/WiFiManagerにおいて回復処理を試行錯誤した

Last updated at Posted at 2022-08-15

1. はじめに

 WiFiのSSID/Keyを設定する手段としてtzapu/WiFiManager1を利用しています。正常時は特に問題はなく快適です。ここでは、回復処理についていくつか試行錯誤した内容を記載しています。
M5StickC, M5StickC Plus, M5Atomの以下の環境で試行錯誤しました。

  • Arduino-IDE

1.8.19 (Widnows11)

  • Boards Manager:

M5Stack official version 2.0.4

  • Library Manager:

M5StickC 0.2.8
M5StickC Plus 0.0.8
M5Atom 0.1.0
WiFiManager 2.0.12-beta

WiFi: Wireless Fidelity
SSID: Service Set IDentifier

2. WiFiManagerのインスタンス宣言

 WiFiManagerをnon blocking modeで使用する場合、WiFiManagerのインスタンスをグローバルで宣言する必要がある2と説明にあります。WiFiManagerは、Configuration Portalを起動してこれが完了すると自身も終了する模様です。Configration Portalを起動することなくWiFi接続できた場合、WiFiManagerは動作しつづける模様です。これらのことは、WiFi接続状態が変化した際にWiFiManagerがメッセージを出すかどうかで確認できます。
 一方、blocking modeで使用する場合は、WiFiManagerのインスタンスを必ずしもグローバルで宣言する必要はありません。setup()内でWiFiManagerを完結させたい場合、setup()内でインスタンス宣言するほうが望ましいと言えます。setup()が終了した時点でWiFiManagerを破棄することができます。

  • non blocking mode: 処理を、他の処理と並行して行う
  • blocking mode: 処理が終わるまで、次の処理に移らない
  • Configuration Portal: 一時的にWiFiのアクセスポイントとなり、SSIS/Keyを設定するWebページを公開する。

3. 一時的なWiFi接続断からの回復

 以下は、arduino-esp32のWiFiType.h3からwl_status_tの定義を抜き出しました。

WiFiType.h
typedef enum {
    WL_NO_SHIELD        = 255,   // for compatibility with WiFi Shield library
    WL_IDLE_STATUS      = 0,
    WL_NO_SSID_AVAIL    = 1,
    WL_SCAN_COMPLETED   = 2,
    WL_CONNECTED        = 3,
    WL_CONNECT_FAILED   = 4,
    WL_CONNECTION_LOST  = 5,
    WL_DISCONNECTED     = 6
} wl_status_t;

 WiFi接続状態でアクセスポイントである無線ルーターの電源を抜くと、以下の様にステータスが変化しました。

WL_CONNECTED → WL_CONECTION_LOST → WL_NO_SSID_AVAIL

 この状態で無線ルーターの電源を投入すると、以下の様にステータスが変化しWiFi接続が回復しました。

WL_NO_SSID_AVAIL → WL_IDLE_STATUS → WL_CONNECTED

 以上から、WL_NO_SSID_AVAIL状態については、SSID/Keyが有効である限り、WiFiの回復機能が有効です。

4. WiFi切断状態からの回復

 WL_DISCONNECTED状態からのWiFi接続には、WiFi.reconnect()を実行する必要があります。WiFi.reconnect()は、EEPROMに保存されているSSID/Keyを使用します。保存されていない場合、WiFi.reconnect()はfalseを返します。以下は、arduino-esp32のWiFiSTA.cpp4からの抜粋です。reconnect()のなかでesp_wifi_disconnect()とesp_wifi_connect()が呼ばれています。各々、WiFi.disconnect()とWiFi.begin()で使用されている機能です。

WiFiSTA.cpp
/**
 * will force a disconnect and then start reconnecting to AP
 * @return true when successful
 */
bool WiFiSTAClass::reconnect()
{
    if(WiFi.getMode() & WIFI_MODE_STA) {
        if(esp_wifi_disconnect() == ESP_OK) {
            return esp_wifi_connect() == ESP_OK;
        }
    }
    return false;
}

EEPROM: Electrically Erasable Programmable Read-Only Memory

WiFiManagerでのWiFi接続失敗

 WiFiManagerの一連の処理でWiFi接続ができない状況としては、経験的に以下が想定できます。

  • EEPROMに保存されたSSID/Keyで、アクセスポイントの都合などで一時的に接続できなかった。このため、立ち上がってきたconfiguration portalを無視した
  • EEPROMにSSID/Keyが保存されていないか、古いなどそのままでは接続できない。このためconfiguration portalでSSID/Keyを設定しようとしたが失敗した

 WiFiManagerでWiFi接続に失敗した場合、WL_DISCONNECTEDまたはWL_CONNECT_FAIL状態となります。WL_DISCONNECTED状態に対しては以下の回復処理が考えられます。

  1. WiFi.reconnect()を実行し、EEPROMに保存されたSSID/Keyでの接続を試みる。
  2. WiFi.reconnect()に失敗した場合、システムをリブートする。

 リブートによりシステムの不安定要素が減り、confuguration portalによるSSID/Keyの再設定も可能になります。WL_CONNECT_FAILの場合、詳細原因は未追及ながらリブートによる解消が期待できます。

localtimeの影響

 現地時間localtimeは、システム時計の値に基づきますが、これが2017年以降でないとWifi.reconnect()を実行してもWL_DISCONNECTED状態から抜け出せないという不可解な現象に悩まされています。localtimeを取得するgetLocalTime()は、2016年以前の場合Falseを返す仕様であり関連性がありそうですが、詳細を突き詰めるには至っていません。
 対策としては以下を考えました。

  1. システム起動時にRTCから現在時刻を設定する
  2. RTCがないかまたはRTCが有効でない場合、システム起動時に2017年以降の仮の時刻を設定する

上記2.の場合、localtimeが正しくないですがgetLocalTime()はtrueを返します。localtimeの有効性は別途管理する必要があります。以下は、esp32-hal-time.c5からのgetLocalTime()の抜粋です。

esp32-hal-time.c
bool getLocalTime(struct tm * info, uint32_t ms)
{
    uint32_t start = millis();
    time_t now;
    while((millis()-start) <= ms) {
        time(&now);
        localtime_r(&now, info);
        if(info->tm_year > (2016 - 1900)){
            return true;
        }
        delay(10);
    }
    return false;
}

5. 実装例

 電波の届かないところにある電波時計の時刻を合わるため、NTPに同期した時刻に基づいて疑似的にJJY信号を発信する装置6を作成しました。WiFi, NTP, およびRTCを合わせて7試行錯誤して作成した処理の流れは以下です。

wifi_ntp_rtc.jpg

  1. RTCの時刻が有効の場合はシステムに設定する(localtime有効)。有効でない場合は仮の時刻(2017年以降)を設定する
  2. WiFiに接続する。WiFiManagerをblocking modeで使用し、処理終了後は破棄する
  3. WiFiの状態にかかわらず、NTPを起動する
  4. 処理ループにおいて、WiFiがWL_CONNECT_FAILの場合、原因解消を期待してリブートを行う
  5. WL_DISCONNECTEDの場合はWiFi.reconnectを行う。3回実行して回復しない場合はリブートする
  6. NTPの時刻同期が成功(localtime有効)したらRTCを更新する
  7. localtimeが有効な場合のみ、JJY信号を送信する

6. おわりに

 不可解な現象についてソースを参照しながら原因を突き止めることが重要ですが、時間がかかってしまいます。間違いや改善など皆様からアドバイスを頂ければ幸いです。

  1. tzapu/WiFiManager

  2. tzapu/WiFiManager/README.md

  3. espressif/arduino-esp32/libraries/WiFi/src/WiFiType.h

  4. espressif/arduino-esp32/libraries/WiFi/src/WiFiSTA.cpp

  5. espressif/arduino-esp32/cores/esp32/esp32-hal-time.c

  6. 標準電波 JJY もどきを M5StickC / M5Atom の Ticker で生成する

  7. ESP32 において NTP の時刻同期を捕まえて RTC を更新する

1
1
1

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?