{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# 0Y-ESP8266_Part_II" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## トラ技2017/03号のおさらい\n", "トラ技の2017/03のESP8266の特集記事は、消費電力を抑えることと、\n", "既存のツールとの組み合わせをうまく使った例だと思います。\n", "\n", "ここでは、以下の処理をトラ技の2017/03の特集記事を参考に試してみます。\n", "- Deep Sleepからの復帰\n", "- UDPブロードキャストを使った通知\n", "- 消費電力を抑えたカメラモジュールの使い方\n", "- data upload plugin を使ったWebサーバの構築\n", "- dockerを使ったデータ収集\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 開発環境インストール\n", "前回から時間が経っているので、環境を再構築しました。\n", "ArduinoIDE 1.8.1、ESP8266は安定バージョンをインストールしました。\n", "\n", "Arduino IDEを起動し、Arduino Preferrencesを開き、\n", "Additional Boards Manager URLs: に以下のURLをコピーしてください。\n", "```\n", "http://arduino.esp8266.com/stable/package_esp8266com_index.json\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ブレッドボードの作成\n", "最初にLチカからはじめましょう。\n", "\n", "\n", "\n", "秋月のAE-ESP-WROOM-02モジュールを使って、ブレッドボードに結線します。\n", "一度、このような基本形を作っておくと次に部品を追加するときに楽になります。\n", "私の場合、シリアルにはaitendoで買ったPL2303を使って3.3Vを供給しています。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 例題を使った動作確認\n", "最近のArduinoの例題は、LEDのピン番号が番号がLED_BUILTINとなっているので、\n", "昔のスケッチのように13番ピンを#define文で定義します。\n", "\n", "```C++\n", "#define LED_PIN 13\n", "\n", "void setup() {\n", " pinMode(LED_PIN, OUTPUT); \n", "}\n", "\n", "void loop() {\n", " digitalWrite(LED_PIN, LOW); \n", " delay(1000); \n", " digitalWrite(LED_PIN, HIGH); \n", " delay(1000); \n", "} \n", "```\n", "\n", "Arduino IDE に ESP8266 のボードマネージャーを設定する\n", "ツール > マイコンボード で、Generic ESP8266 Module を選択します。\n", "それから、 Reset Method を nodemcu に設定します。\n", "\n", "Blinkをコンパイル、BOOT(黄色)ボタン、RESET(赤)の順で押し、RESETを離してから書き込み書き込みが開始されたらBOOTも離します。\n", "\n", "Arduino IDEに...が表示され、書き込みの経過が%で表示されます。\n", "\n", "```\n", "最大434160バイトのフラッシュメモリのうち、スケッチが222249バイト(51%)を使っています。\n", "最大81920バイトのRAMのうち、グローバル変数が31640バイト(38%)を使っていて、ローカル変数で50280バイト使うことができます。\n", "Uploading 226400 bytes from /var/folders/jx/7nsrq4lw8xj553006s7bvdb80000gn/T/arduino_build_172714/Blink.ino.bin to flash at 0x00000000\n", "................................................................................ [ 36% ]\n", "................................................................................ [ 72% ]\n", ".............................................................. [ 100% ]\n", "``` \n", "\n", "まずは、Lチカの動作を確認しましょう。\n", "\n", "\n", "\n", "スケッチは以下に置きました。\n", "- ESP8266/Blink" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## WiFiの例題\n", "WiFiのテストは、nc(net cat)コマンドを使用します。ncは、TCP/UDPの送・受信を簡単に行うことができるツールです。\n", "\n", "ncの使い方は、以下のサイトを参考しました。\n", "\n", "- [ncnetcatコマンドで覚えておきたい使い方8個](https://orebibou.com/2015/11/ncnetcat%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E4%BD%BF%E3%81%84%E6%96%B98%E5%80%8B/\n", ")\n", "\n", "```bash\n", "$ nc \n", "usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port] [--apple-delegate-pid pid] [--apple-delegate-uuid uuid]\n", " [-s source_ip_address] [-w timeout] [-X proxy_version]\n", " [-x proxy_address[:port]] [hostname] [port[s]\n", "```\n", "\n", "サーバとしてデータを受信する場合、-lオプションを付けます。UDPの待ち受けの場合には、-ulを付けます。\n", "ユーザが自由に使えるポート番号は、49152番~65535番(ダイナミックポート)なので、今回は50000番を使うことにします。\n", "\n", "ESP8266はブロードキャストでメッセージを送るので、ネットワークアドレスを調べる必要はありません。\n", "\n", "```bash\n", "$ nc -ul -w 0 50000\n", "Hello World!\n", "$\n", "```\n", "\n", "トラ技のサンプルを参考にUDPClientのスケッチは、ブロードキャストを255.255.255.255にしました。これを実行すると上記のターミナルに\"Hello World!\"と表示されるはずです。\n", "\n", "```C++\n", "#include \n", "#include \n", "\n", "const char *ssid = \"WiFiのSSID\";\n", "const char *password = \"WiFiのパスワード\";\n", "\n", "#define SENDTO \"255.255.255.255\"\n", "#define PORT 50000\n", "\n", "void setup() {\n", " Serial.begin(115200);\n", " delay(500);\n", "\n", " WiFi.begin(ssid, password);\n", " WiFi.mode(WIFI_STA);\n", " Serial.println();\n", " Serial.println();\n", " Serial.print(\"Wait for WiFi... \");\n", "\n", " while(WiFi.status() != WL_CONNECTED) {\n", " Serial.print(WiFi.status());\n", " Serial.print(\".\");\n", " delay(500);\n", " }\n", " Serial.println(\"\");\n", " Serial.println(\"WiFi connected\");\n", " Serial.println(\"IP address: \");\n", " Serial.println(WiFi.localIP());\n", " delay(500);\n", "}\n", "\n", "void loop() {\n", " WiFiUDP udp;\n", "\n", " udp.beginPacket(SENDTO, PORT);\n", " udp.println(\"Hello World!\");\n", " udp.endPacket();\n", " delay(5000);\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## dockerを使う\n", "WindowsユーザがUNIXツールを使う場合には、dockerが便利です。\n", "dockerのインストール方法は、以下のサイトを参考にしてください。\n", "- [Docker for Windows のインストール](http://docs.docker.jp/windows/step_one.html)\n", "\n", "dockerは、仮想マシン環境を使うツールですが、サイズの小さいalpineのイメージを使えば、簡単にUNIXの環境を利用することができます。\n", "\n", "更にalpineのサイズは、3.98MBと非常にコンパクトです。\n", "\n", "```bash \n", "$ docker images alpine\n", "REPOSITORY TAG IMAGE ID CREATED SIZE\n", "alpine latest 88e169ea8f46 2 months ago 3.98 MB\n", "```\n", "\n", "dockerを使ってUDPのメッセージを出力するには、以下の様に入力します($はプロンプト)。\n", "-p 50000:50000/udp は、ホストマシンの50000番ポートに届いたUDPメッセージを仮想マシンの\n", "50000番に転送するように指定しています。\n", "\n", "```bash\n", "$ docker run -it --rm -p 50000:50000/udp alpine:latest /bin/sh -c \"nc -ul -p 50000\"\n", "```\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## SPIFFS版Helloサーバ\n", "ESP8266には、フラッシュをメモリカードのように使えるSPIFFSというファイルシステム\n", "が提供されています。\n", "さらに、PCのdataフォルダ以下をSPIFFSにアップロードするプラグインを使用すると\n", "簡単にESP8266にPCのファイルをアップロードすることができます。\n", "\n", "- https://github.com/esp8266/arduino-esp8266fs-plugin\n", "\n", "アップロードは、BOOTボタンを押しながら、RESETボタンをクリックし、「EPS8266 sketch data upload」を選択します。\n", "\n", "以下のHello world!を出力するだけのindex.htmlを使ってSPIFFS版Helloサーバを動かして見ましょう。\n", "\n", "```html\n", "\n", "\n", "

Hello World!

\n", "\n", "\n", "```\n", "\n", "Arduinoのスケッチフォルダにdataという名前でフォルダを作成し、そこにアップロードしたいファイルやフォルダをコピーします。\n", "\n", "Helloサーバのloopは、以下の様になります。\n", "```C++\n", "void loop() {\n", " // クライアントからの接続要求があるかチェック\n", " WiFiClient client = server.available();\n", " if (!client) {\n", " return;\n", " }\n", " \n", " // クライアントからのHTTP要求を待つ\n", " Serial.println(\"new client\");\n", " while(!client.available()){\n", " delay(1);\n", " }\n", "\n", " // リクエスを読み込む\n", " String req = client.readStringUntil('\\r');\n", " Serial.println(req);\n", " client.flush();\n", "\n", " // index.htmlの内容を返す\n", " File f = SPIFFS.open(\"/index.html\", \"r\");\n", " if (!f) {\n", " Serial.println(\"file open failed\");\n", " return;\n", " } \n", " Serial.println(\"====== Reading from SPIFFS file =======\");\n", "\n", " while(f.available()) {\n", " String line = f.readStringUntil('\\n');\n", " client.print(line);\n", " }\n", " f.close();\n", " \n", " delay(1);\n", " Serial.println(\"Client disonnected\");\n", "}\n", "```\n", "\n", "Helloサーバの完全なスケッチは、以下にあります。\n", "- ESP8266/WiFiHelloServer\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ゲージで温度を表示してみよう\n", "SPIFFSにファイルをアップできると、javascriptを使った凝った表示可能になります。\n", "Google APIのゲージは、インターネットに繋がっていないと使えないため、\n", "以下に紹介されているD3.js版のゲージを使って温度を表示して見ます。\n", "\n", "- [google style gauges using javascript d3.js](http://bl.ocks.org/tomerd/1499279)\n", "\n", "\n", "\n", "ゲージを表示するjavascriptは、以下の様にします。\n", "\n", "```javascript\n", " \n", "```\n", "\n", "温度センサには、LM35を使いました。ESP8266のアナログの読み込みは、system_adc_readを使用します。\n", "\n", "スケッチのloopは、以下の様になります。温度は、/getTemp要求にJSON形式で返します。\n", "```C++\n", "void loop() {\n", " // クライアントからの接続要求があるかチェック\n", " WiFiClient client = server.available();\n", " if (!client) {\n", " return;\n", " }\n", " // クライアントからのHTTP要求を待つ\n", " Serial.println(\"new client\");\n", " while(!client.available()){\n", " delay(1);\n", " }\n", " // リクエスを読み込む\n", " String req = client.readStringUntil('\\r');\n", " Serial.println(req);\n", " // HTTP要求からURIを取り出す\n", " char *uri = getReqURI(req);\n", " if (strcmp(uri, \"/getTemp\") == 0) {\n", " float t = ((float)system_adc_read())/1023.0*100;\n", " // float t = 21.0;\n", " client.print(\"{ \\\"temperature\\\" : \");\n", " client.print(t);\n", " client.println(\"}\");\n", " }\n", " else {\n", " // Prepare the response\n", " File f = SPIFFS.open(uri, \"r\");\n", " if (!f) {\n", " Serial.println(\"file open failed\");\n", " return;\n", " } \n", " Serial.println(\"====== Reading from SPIFFS file =======\"); \n", " while(f.available()) {\n", " if (!client.connected())\n", " break; // 切断されたら転送終了\n", " int len = f.read(line, sizeof(line));\n", " if (len > 0)\n", " client.write((const uint8_t*)line, len);\n", " delay(1); // WDT対策\n", " }\n", " f.close(); \n", " }\n", " client.flush();\n", " client.stop();\n", " \n", " delay(1);\n", " Serial.println(\"Client disonnected\");\n", "}\n", "```\n", "\n", "完全なスケッチは、以下に置いてあります。\n", "- ESP8266/WiFiWebServer (カメラ取り込みも含んでいます)\n" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## カメラも付けて完成\n", "トラ技のサンプルでは、カメラの取り込みタイミングをUDPで送り、PCからwgetで取り込むというアイデアはすごいなぁと思いました。\n", "\n", "ここでは、先の温度計にカメラを取り付けた例をお見せしておしまいにします。\n", "\n", "\n", "\n", "ただし、USBからの電源供給でたくさん繋げすぎたのか、立ち上がりが不安定になりました。\n", "\n", "" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "SageMath 7.5.1", "language": "", "name": "sagemath" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 0 }