ESP8266でWebサーバを利用してOTAアップデートを行う

# はじめに
IoT機器のアップデートは，IoT機器が急速に拡大すると同時に大きな注目を浴びています．大量に存在するIoT機器を手元に持ってきてアップデートするのは非常に効率が悪いため，OTA(Over-the-Air)と呼ばれる遠隔アップデートする方法が一般的になっています．

ESP8266はArduinoを用いて安価に開発可能なチップであり，IoT関連の研究でも実験機器としてよく登場します．

ESP8266+ArduinoでOTAをする方法は，

1. Arduino IDEを利用する
2. 機器をhttpサーバとしてWebブラウザ経由で送信
3. 機器がhttpサーバから入手 

の3種類が存在しています．1の手法についてはすでにQiitaにも記事が掲載されています．

本記事では3の手法でのアップデートをする方法についてまとめています．基本的に3の手法はデバイスを大量に扱う場合に推奨される手法のため，本記事のように1台の機器をアップデートする際には手間が余分にかかるものとなっています．

それぞれの手法についての具体的な中身については以下のページを参考にしています．
https://arduino-esp8266.readthedocs.io/en/latest/ota_updates/readme.html


# アップデートの流れ
## 準備編
### アップデート機器
今回アップデートに利用する機器は[ESP-12F DEVKIT V3](https://www.amazon.co.jp/H-HILABEE-ESP12E-ESP-12F%E9%96%8B%E7%99%BA%E3%83%9C%E3%83%BC%E3%83%89WiFi-ESP8266%E3%83%9C%E3%83%BC%E3%83%89%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB/dp/B07VSD81NF/ref=sr_1_14?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=3F2SS2BFE3O38&dchild=1&keywords=esp12f&qid=1593763044&sprefix=ESP12%2Caps%2C231&sr=8-14)です．サードパーティ製のESP8266モジュールで，Micro-USBで接続可能であり，Wi-Fiにもつながるボードとなります．他のESP8266でもWi-Fi機能が利用可能であれば問題ないとは思います．(未確認)

### 利用したアプリケーションおよびライブラリ
* Arduino 1.8.9
* Python2.7およびPython3.6
* ESP8266のArduinoライブラリ
    * https://github.com/esp8266/Arduino
* PCはOSXおよびUbuntu18.04で確認

### ESP8266をArduinoで実行するまで
上記の[ESP8226+Arduinoのライブラリ](https://github.com/esp8266/Arduino)のページを参考．
具体的な流れとして， 
1. Arduino IDEを立ち上げてPreferences(環境設定)を開きます．
2. ```https://arduino.esp8266.com/stable/package_esp8266com_index.json```をAdditional Borad Manager URLsの欄にコピペします．
3. ツール > ボードマネージャー > からesp8266 platformをインストールします．

ここまでくるとArduinoでESP8266の開発を行うことができます． ここまでの段階で一度Lチカなどのサンプルプログラムを実行することをお勧めします．


## アップデート編
### ESP8266での流れ
OTA用のソースコードは[こちら](http://esp8266.fancon.cz/esp8266-ota-from-server-arduino/arduino-simple-server-ota.html)を参考にしました．

SSIDとPASSWORDを自分が利用するLANの設定にして，用意するhttpサーバのパスおよびファイル名をArduinoのソースコードに記載します．

```C++ 
#include <ESP8266httpUpdate.h>
const char* ssid =     "YourSSID";     // Set your router SSID
const char* password = "YourPassword"; // Set your router password
void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  /*connection to WiFi*/
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }

  Serial.println("Update Start");
  /* warning - the update is done after each reboot now */
  t_httpUpdate_return ret = ESPhttpUpdate.update("http://server/file.bin"); //Location of your binary file
  //t_httpUpdate_return  ret = ESPhttpUpdate.update("https://server/file.bin");
  /*upload information only */
  switch (ret) {
    case HTTP_UPDATE_FAILED:
      Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
      break;
    case HTTP_UPDATE_NO_UPDATES:
      Serial.println("HTTP_UPDATE_NO_UPDATES");
      break;
    case HTTP_UPDATE_OK:
      Serial.println("HTTP_UPDATE_OK");
      break;
  }
}
void loop() {
// ...
}
```

#### アップデート後に実行されるソース
今回は以下のソースコードをhttpサーバから入手することで，アップデートの完了をデバイスの振る舞いおよびシリアルモニタの出力から確認したいと思います．中身はLチカに，デバッグ用の出力メッセージを加えただけです．

```C++
/*
  ESP8266 Blink by Simon Peter
  Blink the blue LED on the ESP-01 module
  This example code is in the public domain

  The blue LED on the ESP-01 module is connected to GPIO1
  (which is also the TXD pin; so we cannot use Serial.print() at the same time)

  Note that this sketch uses LED_BUILTIN to find the pin with the internal LED
*/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
  Serial.begin(115200);
}

// the loop function runs over and over again forever
void loop() {
  Serial.println("Loop Start");  
  digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on (Note that LOW is the voltage level
  // but actually the LED is on; this is because
  // it is active low on the ESP-01)
  delay(1000);                      // Wait for a second
  digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
  delay(2000);                      // Wait for two seconds (to demonstrate the active low LED)
  Serial.println("Loop End");
}
```


Arduino IDEから スケッチ > コンパイルしたバイナリを出力 を実行することでアップデートで送信するバイナリファイルを出力することができます．出力したバイナリはhttpサーバ上に置くことで，ESP8266から入手可能となります．

### httpサーバの立ち上げ
ドキュメントでは，サーバ側でPHPのスクリプトが乗っていますが，Python3のhttp.serverで問題なく動作するので，今回はこちらを利用します．もちろん他のhttpサーバを利用しても大丈夫だと思います．(未確認)

ターミナルからバイナリファイルがおいてあるディレクトリで以下のコマンドを実行します．

```bash
% python3 -m http.server <port番号>
```
これでhttpサーバが立ち上がりアップデートの実行準備は完了です．

##実行編
Arduino IDEから1つ目のソースコードを直接書き込みます．書き込んだあとにシリアルモニタを立ち上げ出力を確認します．
今回の手順で出力された結果が以下のとおりです．
![Screenshot from 16-22-53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233856/64ca6b92-2317-9d82-774c-2ee5162f6c4c.png)

実行結果からOTAによって，Lチカのプログラムが動作していることが確認できました．



# まとめ
ESP8266とArduinoを用いて，httpサーバ経由でOTAを行う方法の確認ができました．実際に運用する際はセキュリティ麺を考慮して，出力したバイナリに署名を行うことが必須かと思われます．そのあたりの手法についても今後まとめていきたいです．