小嶋秀樹 | 研究室
日本語 | English
WiFi 経由で ESP にプログラムを書き込む

トンペイーノにプログラムを書き込むには USB-シリアル変換器(AE-UM232R)が必要です.デバッグ情報をモニタするときにも,この変換器を介したシリアル通信が便利でしょう.一方,一度プログラムを書き込んでしまえば,3.3V の電源を与えるだけで,ESP は単体(USB-シリアル変換器なし)で WiFi を含めた全機能を発揮できます.また小さなケース等に ESP 単体を組み込むケースもあるでしょう.

USB-シリアル変換器を持たない ESP に,プログラムを WiFi 経由で書き換えられると便利です.ここでは,WiFi 経由で ESP にプログラムの書き込み(upload)を可能にする方法を解説します.いくつかの方法がありますが,ここでは,ウェブブラウザからプログラムをアップロードする方法を解説します.

【ウェブアップロードの概要】

この方法は,ESP でウェブサーバの機能を動かしておき,http://192.168.1.129/update のようにウェブブラウザからアクセスすることで,新しいプログラム(ここにもウェブサーバ機能を含めておく)をアップロードできるようにするものです.アップロードの際にパスワードを設定することもできます.

その手順はおよそつぎのようになります.まず,ウェブアップロード機能(後述)を埋めこんだプログラムを ESP で実行させておきます.最初に書き込むときは,Arduino IDE と USB-シリアル変換器を使う必要があります.この ESP はウェブサーバとして機能します.

つぎに,現在動作している ESP のプログラムを WiFi 経由で書き換えます.まず,Arduino IDE で新しいプログラムを作り,バイナリファイル(.bin)として保存します.

適当なウェブブラウザから ESP に接続します.http://.../update にアクセスすると,プログラム更新のページが表示されるので,ここで上述のバイナリファイルを指定して,Update を押します.ファイル転送が終わると ESP は自動的にリセットされ,新しいプログラムの動作が始まります.これでウェブアップロードは完了です.

【ウェブアップロードの準備】

上で述べたウェブアップロードの手順を踏めば,最初の1回だけは USB-シリアル経由での書き込みになりますが,それ以降は WiFi 経由でプログラムの更新が可能となります.ただし,ウェブアップロードを実行するには,以下の条件を満たす「副業」をスケッチに組込んでおく必要があります.これを満たせば,「本業」がウェブサーバでも OSC センシングでも構いません.

  1. ウェブアップロード機能を埋めこんだプログラムを ESP で実行させておく.最初に書き込むときは,USB-シリアル変換器を使う必要がある.
  2. WiFi 経由で書き込むプログラムにもウェブアップロードの機能を埋めこんでおく.こうしないと,次回以降のウェブアップロードができなくなる.
  3. コンパイル後のプログラムのサイズは,実行中のものも新しく書き込むものも,それぞれスケッチ領域の 1/2 未満とする.

上記の条件 1 と 2 を満たすために追加するコードは,つぎのようになります.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>

const char *ssid = "yourSSID";       //  *** 書き換え必要 ***
const char *pass = "yourPassword";   //  *** 書き換え必要(8文字以上)***
ESP8266WebServer Server(80);         //  ウェブサーバ@ポート番号(HTTP)
ESP8266HTTPUpdateServer Updater;     //  ウェブアップデート

void setup() {
  //  無線 LAN への接続
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);             //  接続確立まで待つこと
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  //  本業のセットアップ
  ...
  //  ウェブアップロードの仕込み
  //  (ユーザ名 "xkozima", パスワード "password")
  Updater.setup(&Server, "xkozima", "password");
  Server.begin();
}
void loop() {
  //  クライアントからの要求を処理する
  Server.handleClient();
  //  本業の処理
  ...
}

もし ESP をアクセスポイントにする場合は,setup() を次のように変更してください.

void setup() {
  //  無線 LAN AP の立ち上げ
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, pass);
  //  本業のセットアップ
  ...
  //  ウェブアップロードの仕込み
  //  (ユーザ名 "xkozima", パスワード "password")
  Updater.setup(&Server, "xkozima", "password");
  Server.begin();
}
【ウェブアップロードの実践】

たとえばウェブサーバ(コンテンツは SPIFFS に格納)を「本業」とし,ウェブアップロードを「副業」とすれば,つぎのようなプログラムになるでしょう.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <FS.h>

const char *ssid = "yourSSID";       //  *** 書き換え必要 ***
const char *pass = "yourPassword";   //  *** 書き換え必要(8文字以上)***
ESP8266WebServer Server(80);         //  ウェブサーバ@ポート番号(HTTP)
ESP8266HTTPUpdateServer Updater;     //  ウェブアップデート

//  Server.on(...) を指定せず,すべてを handleNotFound で処理する.
//  URI で指定されるファイルがあればクライアントに転送する.
//  なければ 404 エラー!
void handleNotFound() {
  if (! handleFileRead(Server.uri())) {
    //  ファイルが見つかりません
    Serial.println("404 not found");
    Server.send(404, "text/plain", "File not found in Dongbeino...");
  }
}
//  MIMEタイプを推定
String getContentType(String filename){
  if (filename.endsWith(".html") || filename.endsWith(".htm")) return "text/html";
  else if(filename.endsWith(".css")) return "text/css";
  else if(filename.endsWith(".js")) return "application/javascript";
  else if(filename.endsWith(".png")) return "image/png";
  else if(filename.endsWith(".gif")) return "image/gif";
  else if(filename.endsWith(".jpg")) return "image/jpeg";
  else return "text/plain";
}
//  SPIFSS のファイルをクライアントに転送する
bool handleFileRead(String path) {
  Serial.println("handleFileRead: trying to read " + path);
  // パス指定されたファイルがあればクライアントに送信する
  if (path.endsWith("/")) path += "index.html";
  String contentType = getContentType(path);
  if (SPIFFS.exists(path)) {
    Serial.println("handleFileRead: sending " + path);
    File file = SPIFFS.open(path, "r");
    Server.streamFile(file, contentType);
    file.close();
    Serial.println("handleFileRead: sent " + path);
    return true;
  }
  else {
    Serial.println("handleFileRead: 404 not found");
    Server.send (404, "text/plain", "ESP: 404 not found");
    return false;
  }
}
//  メインプログラム
void setup() {
  //  ファイルシステム
  SPIFFS.begin();
  //  シリアルモニタ(動作ログ)
  Serial.begin(115200);               //  ESP 標準の通信速度 115200
  delay(100);                         //  100ms ほど待ってからログ出力可
  Serial.println("\n*** Dongbeino ***");
  //  無線 LAN への接続
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);             //  接続確立まで待つこと
  Serial.println("Connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  Serial.println("Connected");
  Serial.println(WiFi.localIP());
  //  ウェブサーバの設定
  Server.onNotFound(handleNotFound);  //  ファイルアクセス時の応答関数を設定
  //  ウェブアップロードの仕込み
  Updater.setup(&Server, "xkozima", "keepon");
  Server.begin();
}
void loop() {
  //  クライアントからの要求を処理する
  Server.handleClient();
}

このプログラムを,最初は,USB-シリアル経由で書き込みます.Arduino IDE 下部にあるコンソールを見ると,コンパイル後のイメージは 314,475 Byte で,スケッチ領域(約 1MB)の 30% に収まっているのがわかります.

Sketch uses 314475 bytes (30%) of program storage space. Maximum is 1044464 bytes.
Global variables use 38548 bytes (47%) of dynamic memory, leaving 43372 bytes for local variables. Maximum is 81920 bytes.
Uploading 318624 bytes from /var/folders/d8/1x1pyc4n7lg7v6fl816hv9x40000gn/T/arduino_build_620319/esp_webFileTest_WebSTA.ino.bin to flash at 0x00000000
................................................................................ [ 25% ]
................................................................................ [ 51% ]
................................................................................ [ 76% ]
........................................................................         [ 100% ]

プログラムが動作を開始すると,シリアルモニタに IP アドレスが表示され,適当なウェブブラウザからアクセスし,ウェブコンテンツを閲覧することができます.

つぎに,このプログラムを少しだけ改造し,LED (IO13) が点灯するようにしましょう.

void setup() {
  //  LED 点灯
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  //  ファイルシステム
  ... (以下変更なし)...
}

このスケッチ(esp_webUpload.ino)をコンパイルするとき,Sketch > Export compiled Binary を選択してください.こうすることで,USB 経由で書き込む代わりに,コンパイルされたイメージを .ino ファイルのあるフォルダに .bin ファイル(esp_webUpload.ino.generic.bin)として保存してくれます.イメージのサイズがスケッチ領域の 1/2 未満となっていることを確認してください.なお,Sketch > Show Sketch Folder でそのフォルダを見ることができます.この .bin ファイルがウェブアップロードの対象となります.

このファイルを ESP にアップロードするには,まずウェブブラウザで ESP にアクセスします.このとき,IP アドレスに "/update" をつけた URL を指定してください.すると,ユーザ名とパスワードを(アクセス初回のみ)聞いてきます.

ホスト名でアクセスしたい場合: mDNS (multicast Domain Name Service) という機能を組み込むことで,たとえば "dongbei1.local" という名前で(同一のローカルネットワークから)接続できます.以下のコードを加えてください.

...
#include <ESP8266mDNS.h>

void setup() {
  ...
  //  無線 LAN への接続(または無線 LAN AP の立ち上げ)
  ...
  //  mDNS の呼びかけに応答できるように仕込む
  Serial.println("Setting up mDNS...");
  if (MDNS.begin("dongbei1"))         //  "dongbei1.local" で応答
    Serial.println("mDNS started");
  else
    Serial.println("mDNS failed to start");
  //  本業のセットアップ
  ...
  //  ウェブアップロードの仕込み
  ...
}
...

これで,IP アドレスが分からなくても,"http://donbei1.local/update" にアクセスすることで,ウェブアップロードが可能になります.

ユーザ名とパスワードを正しく入力すると,つぎのような画面に切り替わります."Choose File" ボタンを押し,上述のイメージ(esp_webUpload.ino.generic.bin)を指定し,"Update" ボタンを押してください.

ブラウザによっては,アップロードの進捗をプログレスバーあるいは数値(%)によって表示してくれるでしょう.アップロードが完了すると,下図のように,"Update Success! Rebooting..." と表示され,ESP が再起動します.

これで WiFi 経由でのプログラムのアップロード完了です.変更したプログラムを再びアップロードするには,ウェブブラウザで "http://192.168.1.129/update" のようにアクセスし,同様の手順を踏んでください.