2019年1月14日月曜日

ESP32/arduino : ソフトウェアリセット

目的:
 プログラムからリセットをかけてリスタートを行う。

背景:
 WiFi 接続が ほぼ 1回置きに失敗する。
 ( WiFi.begin(ssid, password); 後の
  WiFi.status() で WL_CONNECTED にならない。
  リセットスイッチ, 電源 ON/OFF (USB抜き差し) 共に、1回置きに失敗 )
 原因は不明だが、取り敢えず、接続できない場合にリスタートを行いたい。
 [2019/2/24 追記]
 WL_CONNECTED にならない場合、再度 WiFi.begin(ssid,passwork); を実行すれば接続できた。接続できない場合は WiFi.begin の再実行の方がよいかも。
 [2020/8/11 追記]
  WiFi.begin の再実行の前に WiFi.disconnect(true,true); を実行した方が良いようです。
 また、この現象(1回置きに接続失敗) は、WiFiルータ(親機) に依存する様に思えます。
可能であれば、異なる WiFiルータに接続先を変えてみても良いかもしれません。


方法:
 ESP.restart();
 を実行する。

例: ( 「LED 調光 (PWM)」をベースに、失敗時のソフトウェアリセット を 追加 )

/*
 *    WiFi LED ON/OFF TEST
 *      PWM Control 
 */
  
#include <WiFi.h>
 
const char* ssid = "hogehoge";
const char* password = "hogehogepaswd";

IPAddress ip(192, 168, 1, 32);           // for fixed IP Address
IPAddress gateway(192,168, 1, 1);        //
IPAddress subnet(255, 255, 255, 0);      //
IPAddress DNS(192, 168, 1, 90);          //
 
WiFiServer server(80);
 
// -- for ledc
byte br_table[11] = {0,25,50,75,100,125,150,175,200,225,255} ;
// -- for sigmaDelta
//byte br_table[6] = {0,150,180,205,230,255} ;
int  n = 0;
 
void setup()
{
    int lpcnt=0 ;
    
    Serial.begin(115200);
    pinMode(2, OUTPUT);      // set the LED pin mode
     
    WiFi.config(ip, gateway, subnet, DNS);   // Set fixed IP address
    delay(10);
 
    // We start by connecting to a WiFi network -----------------------------
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
 
    WiFi.begin(ssid, password);
 
    while (WiFi.status() != WL_CONNECTED) { 
        delay(500);
        lpcnt +=1 ;
        if (lpcnt>10) { ESP.restart(); }
        Serial.print(".");
    }
 
    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
     
    server.begin();
 
    // for LED PWM Control ---------------------------------------------------
    // -- for ledc
    ledcSetup(0, 5000, 13);        // setup channel 0 with frequency 5000 Hz, 13 bit precission for LEDC timer
    ledcAttachPin(2,0);            // attach pin 2 to channel 0
    ledcWrite(0, 0);               // initialize channel 0 to off     
    // -- for sigmaDelta
    //sigmaDeltaSetup(0, 312500);    // setup channel 0 with frequency 312500 Hz
    //sigmaDeltaAttachPin(2,0);      // attach pin 2 to channel 0
    //sigmaDeltaWrite(0, 0);         // initialize channel 0 to off     
    
    n=0 ;
}
 
void loop(){
 WiFiClient client = server.available();     // listen for incoming clients
 
  if (client) {                                               // if you get a client,
    Serial.println("***** Client access start *****");       // print a message out the serial port
    while (client.connected()) {                              // loop while the client's connected
      if (client.available()) {                               // if there's bytes to read from the client,
        String line = client.readStringUntil('\n');           // Get Line data until '\n'
        Serial.println(line);
        if (line.indexOf("GET /?on") != -1) {                 // Client request was "GET /?on" 
          n += 1 ;                                            // incliment table no.
          if (n>10) n=0 ;
          Serial.print("brightness value : ");
          Serial.println(br_table[n],DEC);
          // -- for ledc
          ledcWrite(0, 8191*br_table[n]/255) ;                // set PWM value to channel#0
          // -- for sigmaDelta
          //sigmaDeltaWrite(0, br_table[n]) ;
        }
        if (line.indexOf("GET /?off") != -1) {                // Client request was "GET /?off"
          n = 0 ;                                             // clear bright no.
          Serial.print("brightness value : ");
          Serial.println(br_table[n],DEC);
          // -- for ledc
          ledcWrite(0, 8191*br_table[n]/255) ;                // set PWM value to channel#0
          // -- for sigmaDelta
          //sigmaDeltaWrite(0, br_table[n]) ;
        }
 
        if (line.length() == 1 && line[0] == '\r'){           // end of HTTP request
          send_response(client) ;                             // send response to client
          break;                                              // break while loop
        }
      }
    }
    delay(1);                                        // give the web browser time to receive the data
    // close the connection:
    client.stop();
    Serial.println("Client Disconnected.");
    Serial.println("--------------------------------------------------");
  }
}
 
// ------------------------------------------------------------------
void send_response(WiFiClient client) {
    // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
    // and a content-type so the client knows what's coming, then a blank line:
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    client.println();
    // the content of the HTTP response follows the header:
    client.print("<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>" ) ;
    client.print("<style>input {margin:8px;width:100px;}div {font-size:16pt;text-align:center;width:250px;border:solid 4px #93ff93;}</style>" ) ;
    client.print("<title>Color LED Controller</title></head>" ) ;
    client.print("<body><div><p>LED ON/OFF</p><form method='get'>" ) ;
    client.print("<input type='submit' name='on' value='ON' /><input type='submit' name='off' value='OFF' /></form>" ) ;
    client.print("</div></body></html>" ) ;
    client.println();
    Serial.println( " --- send responce --- ");
}

以下、覚書など。
  ( 行# 26 )
 失敗判定の為の 確認回数カウント用。
  ( 行# 44 )
   WiFi.status() 確認回数が 10回を超えたら、ソフトウェアリセットを発行。

[2020/8/11 追記]
尚、 再接続する場合は 行#41-46 を以下の様にする。
再接続時の例
  while (WiFi.status() != WL_CONNECTED) {    // 接続確認
      delay(500);                            //   0.5秒毎にチェック
      lpcnt += 1 ;                           //
      if (lpcnt > 6) {                       /
/ 6回目(3秒) で切断/再接続
        WiFi.disconnect(true,true) ;         //
        WiFi.begin(wifi_ssid, wifi_pass);    //
        lpcnt = 0 ;                          //
        lpcnt2 += 1 ;                        // 再接続の回数をカウント
      }                                      //
      if (lpcnt2 > 3) {                      // 3回 接続できなければ、
        ESP.restart() ;                      // ソフトウェアリセット
      }                                      //
      Serial.print(".");                     //
  }                                          //






11 件のコメント:

  1. こんばんは。参考にさせていただきました。

    >>WiFi 接続が ほぼ 1回置きに失敗する。

    当方でも同じ現象が発生し、困っておりました。ネットで探しても同じ症状の方はほとんど見つからず、ソフトリセットで乗り切ろうと、このページに辿り着きました。

    アドバイスにある通り、再度 WiFi.begin(ssid,passwork)を実行すると、たしかに接続できるようになりましたが、今度は、1回おきではなく、毎回、接続しなくなり、上記の2回めの WiFi.begin(ssid,passwork)でつながるようになりました。

    画面には毎回「..........」が表示されます。

    まずは、毎回、接続できるようになりましたので、良いとは言え、ちょっと気持ち悪いですね。

    他に何か良い対策などございましたらご教示いただければ幸いです。

    返信削除
    返信
    1. おはようございます。
      私も、1回毎置きに失敗する様な情報が見つけらず、試行錯誤しました。
      未だに原因は分かっておらず、対症療法として タイムアウト時の WiFi.begin 再発行で対応しています。

      毎回 「....」が表示されるのは、
      OFF/ON(リセット)で接続失敗(「....」表示)→ WIFI.bigin再発行 で接続成功
      の様に 1回のOFF/ON(又は リセット) で 失敗→成功 となるため、毎回 「...」が表示されるのかと思います。
      ( 結局、失敗と成功を 1回置きに繰り返している )

      削除
  2. コメントありがとうございました。さらにいろいろと試行錯誤してみました。
    当方は、処理の後にDeepSleepに入れていますが、DeepSleepに入る前に以下を追加してみたところ、「..........」が出なくなりました。

    WiFi.disconnect(true,true);

    正しい処理なのかどうかはわかりませんが、これでスッキリする感じになりました。

    どこかの英語のサイトに「connectを連続で繰り返すのは良くない。Disconnectしてから再接続」と書いてありましたので、一旦切ればいいのかな、という感じで上記の記述です。(ただ、これだと1回おきに接続できたのがなぜだかわかりませんが・・・)

    お試しいただければと思います。ありがとうございました。

    返信削除
    返信
    1. ご教示ありがとうございます。
      リセット前に WiFi.disconnect を行ってみたところ、リセット後に接続失敗すること接続できました。
      終了処理でのWiFi.disconnectは行った方が良いですね。

      ただ、終了処理がなく(LED調光のサンプルの様に)、いきなりリセットや、OFF する場合では WiFi.disconnect するタイミングがありませんので、接続時の再接続処理が必要と思います。
      ただ、再接続前に WiFi.disconnect を入れるべきかもしれません。
      (実は、WiFi.begin を続けて発行する事は気になっていたのですが、再接続だけで 接続できていたのでそのままとしていました。)

      ところで、ふと気になったので、接続先のアクセスポイント (WiFi親機) を別の物 に変えたところ、現象が発生しなくなりました。この現象の発生は、WiFiルータ(親機) に依存するかもです。

      削除
  3. ご教示ありがとうございます。

    たしかに、終了処理ができない場合は、どうしよう?と思っておりましたが、接続前にDisconnectを入れておくのはありかもしれませんね。試してみます。

    この不具合は、ルータにもよるんですね(だから、調べても同じような人がほとんどいないんでしょうか)。当方はSynologyのルータを使っておりました。

    今回は、貴重な知見が得られました。何度もありがとうございました。
    これからも拝見いたします。

    返信削除
  4. Disconnectを接続前に入れてみましたが、上手くいきませんでした。。。
    ルータとの相性もあるとなると、なかなか完全解決は難しそうですね。

    返信削除
    返信
    1. すみません。誤解させたかもです。
      Disconnect は、最初の接続(WiFi.begin) で接続できない(タイムアウト)場合に、再度 WiFi.begin を発行する前に WiFi.Disconnect を入れた方が良いかなと思った次第です。(接続できていないので 意味無いかもですが、、)

      ちなみに、こちらの環境では、
      WN-AX1167GR2 (IO DATA) : 現象発生
      Atern BL900HW (KDDI(NEC)) : 現象発生無し
      でした。

      削除
  5. 大変参考になりました.
    M5Stack ATOM Liteで似たような現象に見舞われ,問題を回避できました.
    ESP-WROOM-02ではこのような現象に見舞われたことがありませんでした.

    返信削除
    返信
    1. 参考にして頂き、ありがとうございます。
      未だ、原因は判っていませんが、回避策としています。
      ルータとデバイスの相性みたいなものがあるのかもしれませんね。

      削除
  6. ESP32は 起動時に 最初に接続した時のSSID情報を保持しいるそうです。
    その情報で、自動で再接続してしまうそうです。
    ですので、Disconnect()の必要があるようです。
    最初というのは、購入後に、最初に設定した SSIDなど情報です。

    返信削除
    返信
    1. 情報ありがとうございます。
      そうすると、起動時に最初に Disconnect()はした方が良いということですね。
      上手くいかないという話もありましたが、起動後に Disconnect() するタイミングもあるかもしれません。

      削除