ESP8266でArduinoからhttpsにアクセス


ESP8266は十年ちょっと前に発売されたが、簡単にwifiにつなげるということで、私も十年前ぐらいから使い始めた。 その頃は、まだhttpが主流だったので、ArduinoIDEを使ってESP8266からhttpにアクセスするIoT機器などを作ったりしていた。 しばらくすると、ESP8266でmicropythonを使えるようになり、プログラムはpythonの方が楽なので、ESP8266はmicropythonから使うようになった。 その頃に、ESP32やmrubyの事を知って、ESP32は使ってみたが、mrubyは敷居が高い気がして使って来なかった。

昨年、久々にIoT機器を作る必要性が出て来たが、ほとんどのサイトはhttpsとなっていた。 始めはESP8266を使おうとしたけど、メモリ不足のためかmicropythonからはhttpsにアクセスすることは出来無かった。 結局、ESP32でmicropythonを使ってhttpsにアクセスして、IoT機器を作った。 ちなみにその後、アクセスしていたwebサーバが更新されたようで、通信が不安定になったので、原因を調べないといけないのだけれど、まだできていない。 しかし、ESP8266でhttpsにアクセスできないとなると、ESP8266を使う用途が非常に限定されてしまう。 最近、mruby/cをArduinoから扱うようになって、mruby/cやArduinoはmicropythonよりメモリの使用量が少ないはずだから、mrubyc_arduinoならESP8266でもhttpsにアクセスできるのでは無いかと思うようになった。

それを確かめる準備として、まずはArduinoからhttpsにアクセスする方法を試してみた。 少し調べてスケッチ例のESP8266WiFi-HTTPSRequestを見付けたが、これはあらかじめ証明書を準備しておく方法で、面倒な上に期限がある。 もう少し探すと、 6年前の記事 を見付けて、この方法なら証明書なども不要で、いつでもアクセスできそうだったので、これを元に試してみた。

Arduinoの宣言部分では、ライブラリを組み込んで、wifiやアクセスしたいサイトの情報を書いておく。

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid     = "SSID";
const char* password = "PASSWORD";

const char* host = "host";
String url = "/index.html";

httpsを読み取ってファイルの内容を返す関数は以下のように定義した。

String https_get(String host, String url){
  BearSSL::WiFiClientSecure client;
  client.setTimeout(500);
  client.setInsecure();
  if (!client.connect(host.c_str(), 443)) {
    Serial.println("failed");
    return "failed";
  }
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
     "Host: " + host + "\r\n" + 
     "User-Agent: ESP8266/1.0\r\n" + 
     "Connection: close\r\n\r\n");
  unsigned long timeout = micros();
  while (client.available() == 0) {
    if ( micros() - timeout  > 5000000) {
      Serial.println("timeout");
      client.stop();
      return "timeout";
    }
  }
  bool body=false;
  String ret="";
  while(client.available()){
    String line = client.readStringUntil('\r');
    if(body) ret+=line;
    if(line.length()<3) body=true;
  }
  return ret;
}

X509認証を行わないようにしているので、サーバーの偽装が可能になってしまうという欠点があるが、httpよりは暗号化されている分だけ安全と言えるだろう。 また、ファイルの内容が大きい場合には、メモリの容量に気を付ける必要があるだろう。 そして、setupでSerialを開始して、loopでwifiに接続してhttpsを読み取る。

void setup() {
  Serial.begin(19200);
  Serial.println("");
}
void loop() {
  WiFi.begin(ssid, password);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println(https_get(host,url));
  WiFi.disconnect(true);
  Serial.println("Disconnected");
  for(int i=0;60>i;i++) delay(1000);
}

arduino-esp8266の説明では、 デフォルトの80MHzよりも160MHzで動かした方が良いと書いてあったが、ツール-CPU Frequencyでどちらを設定しても、うまく動いていた。 X509認証をやっていないことと関係しているかも知れない。 情報漏洩とかが関係無い場合には、アクセスが簡単なhttpにして欲しい気がするけど、途中で情報を書き換えられたり、ホストを偽装されたりするリスクがあるので、最近はhttpsになっているんだろうな。

これで、ESP8266でmrubyc_arduinoからhttpsをgetする準備が整った。 しかし、wifiやhttpのクラスをどう定義すべきか考えないといけない。 どこかにガイドラインみたいなものがあるのなら、できるだけそれに従いたいが、なかなか見付からない。 プログラムよりも、それを探すのに時間がかかりそうだ。