koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

ESP8266をリモコンする~204の衝撃~

差し出がましくも ESP8266/ESP-wroom-02 Advent Calendar 2016 - Qiita 参加記事です。

長々と書いていますが、要はこのツィートを太らせただけです。 サンプルコードは後ろの方に貼っておくので、そこだけ気になる方は飛ばし読みください。

ESP8266は言わずと知れた安価ながら技適が通っているWi-Fiモジュールです。お手軽にインターネットにモノを繋げられてIoT遊びにとても便利です。 色んな使い方があると思いますが、僕は遊び用途です。

プログラムすることなく動作させられるような便利ボードもがありますが、Arduino IDEで直接プログラムするのが好みなので ESPr Developer を愛用しています。

さて、本題。

通常、マイコンに何かを入力する場合、入力機器を接続しなくてはなりません。 物理的にスイッチを繋ぐ、場合によってははんだ付けも必要になってきます。 僕のようなソフトウェアエンジニアにとって、それは結構大変です。

ESP8266の場合、Wi-Fi経由で情報をやりとりすることができるので、ソフトウェアだけで入力(当然出力も)できます。 加えて、自分自身がWi-Fiアクセスポイントになることができるので、他の機器の力を借りることなく、スタンドアローンでそれらを行えるのが利点だと思います。

いちいち接続先アクセスポイントを切り替えないといけないのが少々面倒ですが、これを回避するにはインターネット接続というやや面倒くさい裏技を使わないといけないので忘れます。 IoTどこいった? という感じですが、これだけでも使い出がある、と思っています。

マニマムなコードはこんな感じになります。 Arduino周りはESP8266向けもサンプルコード(スケッチ)が充実していていいですね。 これも ESP8266WebServer の HelloServer をちょっといじったものです。

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char* ssid = "MyESP8266";
const char* password = "";

ESP8266WebServer server(80);

// 定数でフォーム内容を定義すると楽
const char* form = "<html>"
                   "<head>"
                   "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
                   "<style>input { display: block; margin: 4px; }</style>"
                   "</head>"
                   "<body>"
                   "<form method=\"post\">"
                   "<input type=\"number\" name=\"param\" value=\"0\" >"
                   "<input type=\"submit\" value=\"Send\" >"
                   "</form>"
                   "</body>"
                   "</html>"
                   ;
void handleRoot() {
  Serial.println("handleRoot");
  if (server.method() != HTTP_GET) {
    int param = -1;
    for ( uint8_t i = 0; i < server.args(); i++ ) {
      Serial.print(server.argName(i));
      Serial.print("=");
      Serial.print(server.arg(i));
      if (server.argName(i)[0] == 'p') {
        param = server.arg(i).toInt();
      }
    }
    // XXX LED光らせるとか、サーボ回すとか、色々
  }
  server.send(200, "text/html", form);
}

void setup(void) {
  Serial.begin(115200);
  WiFi.softAP(ssid, password);
  //  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());

  // XXX なんか名前解決できない→普段IPでアクセスいているのがバレバレ
  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.begin();

  Serial.println("HTTP server started");

  MDNS.addService("http", "tcp", 80);
}

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

これで、スマホからNeoPixcelの色を変えたり、サーボモーターを動かしたりして遊んでいます。

で、困ったこと。

手を抜いて定数を送出しているので、フォーム内容が送信のたびに初期値になるんですね。 前回値なんだっけ? となるわけです。

f:id:koudenpa:20161203215714p:plain

入力して送信すると、

f:id:koudenpa:20161203215716p:plain

元に戻る。

動的にフォーム内容作れ? 面倒くさいのは嫌なんだ。貧乏性だからメモリもそんなに贅沢には使いたくない。

そんなときにふと、WebサービスAPI作ってる時にノーコンテンツっての見たな、と思いステータスコードをぐぐったらこんな記述が。

HTTPステータスコード - Wikipedia

204 No Content

内容なし。 リクエストを受理したが、返すべきレスポンスエンティティが存在しない場合に返される。

例: POSTメソッドでフォームの内容を送信したが、ブラウザの画面を更新しない場合に返される。

あなたが神か?

204を試す。

     // XXX LED光らせるとか、サーボ回すとか、色々
+    server.send(204, "");
+  } else {
+    server.send(200, "text/html", form);
   }
-  server.send(200, "text/html", form);
 }

POST しても入力そものまま、すばらしい。

※ 画は全く面白くないので省きました

以上、POSTに対してはステータスコード204を返すようにしてみたら、ブラウザの表示はそのままになって万々歳と言う衝撃でした。

長々と書いた分、自分が受けた衝撃も薄くなってしまった気がするけれど、まぁ、いいか。

ESP8266/ESP-wroom-02 Advent Calendar 2016 - Qiita この記事を書いている時点ではかなりスカスカなのでささいなネタでも書いてみてはいかがでしょうか。

それでは。