Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
サンプルプログラム解説

概要

Webを使ったOTAアップデート(Over The Air: この場合無線でスケッチを書き込むこと)の使い方です。

プログラム

定義等

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>

const char* host = "esp32";
const char* ssid = "xxx";
const char* password = "xxxx";

WebServer server(80);

/*
 * Login page
 */

const char* loginIndex = 
 "<form name='loginForm'>"
    "<table width='20%' bgcolor='A09F9F' align='center'>"
        "<tr>"
            "<td colspan=2>"
                "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                "<br>"
            "</td>"
            "<br>"
            "<br>"
        "</tr>"
        "<td>Username:</td>"
        "<td><input type='text' size=25 name='userid'><br></td>"
        "</tr>"
        "<br>"
        "<br>"
        "<tr>"
            "<td>Password:</td>"
            "<td><input type='Password' size=25 name='pwd'><br></td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
            "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
        "</tr>"
    "</table>"
"</form>"
"<script>"
    "function check(form)"
    "{"
    "if(form.userid.value=='admin' && form.pwd.value=='admin')"
    "{"
    "window.open('/serverIndex')"
    "}"
    "else"
    "{"
    " alert('Error Password or Username')/*displays error message*/"
    "}"
    "}"
"</script>";
 
/*
 * Server Index Page
 */
 
const char* serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
   "<input type='file' name='update'>"
        "<input type='submit' value='Update'>"
    "</form>"
 "<div id='prg'>progress: 0%</div>"
 "<script>"
  "$('form').submit(function(e){"
  "e.preventDefault();"
  "var form = $('#upload_form')[0];"
  "var data = new FormData(form);"
  " $.ajax({"
  "url: '/update',"
  "type: 'POST',"
  "data: data,"
  "contentType: false,"
  "processData:false,"
  "xhr: function() {"
  "var xhr = new window.XMLHttpRequest();"
  "xhr.upload.addEventListener('progress', function(evt) {"
  "if (evt.lengthComputable) {"
  "var per = evt.loaded / evt.total;"
  "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  "}"
  "}, false);"
  "return xhr;"
  "},"
  "success:function(d, s) {"
  "console.log('success!')" 
 "},"
 "error: function (a, b, c) {"
 "}"
 "});"
 "});"
 "</script>";

hostは、mDNSで登録するESP32のホスト名です。

ssidとpasswordは、WiFiアクセス用のSSID/パスフレーズです。

WebServer型の変数serverを定義します。ポート番号80で待ち受けます。

loginIndexはログインページで表示するHTMLデータ/JavaScriptプログラム、serverIndexはファイルを選択、送信する際のHTMLデータ/JavaScriptプログラムです。

setup()

/*
 * setup function
 */
void setup(void) {
  Serial.begin(115200);

  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /*use mdns for host name resolution*/
  if (!MDNS.begin(host)) { //http://esp32.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");
  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();
}

106行目から113行目はWiFiアクセスポイントへの接続です。

WiFi.begin()でアクセスポイントに接続します。WiFi.status()は、現在の接続状態を返却します。アクセスポイントに接続しているときは、WL_CONNECTEDが返ってきます。

MDNS.begin()で、mDNSにホスト名を登録します。

129行目から161行目はHTTPアクセス時のハンドラ定義です。server.on()で、指定したURIにアクセスがあった時に呼び出す関数を登録します。server.sendHeader()でHTTPヘッダを追加し、server.send()で、実際にレスポンスを返します。

129行目は、/にアクセスがあった場合に呼び出される関数として、loginIndex()を登録します。この関数は、ログインページを表示します。下記に、画面を示します。

本当であれば、http://esp32.local/で呼び出すことができるはずなんですが、私の環境ではうまく動作しなかったので、IPアドレスを指定しています。

loginIndexで設定したHTML/JavaScriptプログラムでは、Username、Passwordともadminを入力することで、/serverIndexに遷移することになっています。ただし、セッション等は利用していないので、/serverIndexに直接アクセスすれば、ログインページをスキップできてしまいますが…

133行目は、ログイン後に表示する/serverIndexを登録しています。この関数は、アップロードするプログラムの選択と、アップロード状態を示すページです。以下に、画面を示します。

このページに遷移直後は、ファイルは選択されておらず、ファイルの送信の進捗率も0%です。

138行目から161行目は、/updateに対するPOSTメソッドの定義を行っています。これは、serverIndexのページで、Updateボタンを押したときに呼び出されます。

第3引数の関数(138行目から141行目)はPOSTメソッドでリクエストボディが送信された後に呼び出される関数です。Update.hasError()で、更新にエラーがあるかどうかを判定しエラーがある場合はFAIL、ない場合は、OKをクライアントに返却します。その後、ESP.restart()でESP32を再起動します。

第4引数の関数は、リクエストボディを受信中に実行する関数です。

server.upload()により、リクエストボディ受信中の状態を取得します。

状態が、UPLOAD_FILE_STARTの場合は、Update.begin()により、スケッチの更新を開始します。更新の開始に失敗したときは、Update.printError()により、エラー情報を出力します。

状態が、UPLOAD_FILE_WRITEの場合は、Update.write()を使い、スケッチを書き込みます。

状態が、UPLOAD_FILE_ENDの場合は、Update.end()で、スケッチの更新を終了します。

スケッチの更新中は、以下のように更新状況を表示します(これは、ESP32側の処理ではなく、ブラウザ側の処理です)。

更新が完了すると、progressが100%になって終了します。

最後に、server.begin()で、ウェブサーバを開始します。

loop()

void loop(void) {
  server.handleClient();
  delay(1);
}

server.handleClient()で、HTTPリクエストを処理します。

バージョン

1.0.0



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2018 garretlab all rights reserved.
inserted by FC2 system