概要
8月も終わるが久しぶりに電子工作をやっていた。別に自由研究の宿題があったわけではないのですが。
今回は以下のようなテーマにした。
- ESP-WROOM02に温度/湿度センサーモジュールを接続し定期的に測定を行わせる。
- 測定ごとに得られた温度と湿度をwebサーバーに飛ばしデータを記憶する。
- 記憶したデータをブラウザでグラフ表示する。
いろいろな要素があって長くなりそうなので、少しずつ載せていくことにする。
※ 送信先のwebサーバーとは、WROOM02を用いたものではなく、Linuxで動くapache httpdである。データの格納にはmysqlを使う。グラフ表示には、google chartを用いる。
ハードウェア
今回使った回路は以下のとおり。AE-ESP-WROOM02を中心に、温度/湿度センサーモジュールのAE-HDC1000、USB-シリアル変換用のモジュールのAE-FT231Xを使った。いずれも秋月電子で売っているものを選択したのだけど、通販なのでまとめると送料が節約できるから。WROOM02周りで以前と相違している点は、ディープスリープモードを利用するため、IO16とRSTを接続していることだろう。
この回路の主要部分をブレッドボードで実現すると以下のようになった。なお、USBシリアル変換モジュールのAE-FT-231Xは別のブレッドボードに載せており、3本のケーブル(TxD, RxD, GND)で接続する。
この画像は、OM-D E-M5IIにMZD60mmを着けて、フォーカスブラケットで撮影してから深度合成した。絞りf/4でフォーカス移動設定を3とし8回の撮影結果を合成したもの。
このカメラのシャッターボタンは全押し時にクリック感のあるダサイものなので、マクロレンズで近接して撮影するときなど、押下時のショックの影響を受けることが多い。8枚の連写の1枚目だけが微妙にずれていたりする。リモートレリーズケーブルを使えばいいんでしょうね。どうせなら、ハイレゾモードと同様に、押下後2秒待ってから連写開始とかできればいいのだが。
電源について
外部電源として+5Vを与え、低損失三端子レギュレータ(TA48033S)により、ボード上の各モジュール用の+3.3Vを作っている。このレギュレータは最大1Aまで供給可能だから、WROOM02とその周辺の駆動には十分だろう。
外部電源の接続用には、ふつうのDCジャック(秋月のDCジャックDIP化基板を利用)を用意しており、ACアダプタやモバイル機器の充電器などから+5Vを供給する。
なお、今回は室内の温度や湿度を測定する予定なので、数分間に一回程度の頻度で測定できればよい。その間はムダな電気を使わないよう、WROOM02をスリープ状態にする。スリープ状態のときに外部電源から流れる電流は1mA以下となるので、ふつうのモバイルバッテリでの駆動は無理だろう。
温度/湿度センサーモジュール AE-HDC1000
TI社製の温度/湿度デジタルセンサーモジュールのHDC1000を、2.54mmピッチの基板やブレッドボードで使いやすくした秋月電子の製品で、センサーとしての使用を意識した変わった形状の基板になっている。ボードに立てるための5本足のピンをハンダ付けする必要があるが、+3.3Vの電源電圧で動作することと販売価格(680円)が選択の理由。
ボードに立てるためのピンをハンダ付けし、ブレッドボードに載せたところ。頭の部分の黒い四角いデバイスが、HDC1000である。基板との間にすき間をもたせてあるとのこと。
このモジュールは、I2Cというシリアル二線式のインタフェースでマイコンモジュールに接続できるので、クロック(SCLK,#3)とデータ(SDA,#2)ピンを、それぞれWROOM02のIO5とIO4に接続している。RDYピンは使わない。IO5とIO4の両ピンは、I2C接続のときによく使われるもので、I2C接続するデバイスを制御するためのArduinoのWireライブラリでもこれらのピンを使うように定義されている。WROOM02においても、Arduino用のWireライブラリがそのまま使える。
USB-シリアル変換用のモジュール
回路図のうち、右下の点線で囲んだ部分がプログラム開発時にPCと接続するための、USB-シリアル変換用のモジュールである。以前に作ったもので小さいブレッドボードに載せてある。
このモジュールの電源はUSBケーブルを介してホスト(PCやハブ)から供給される。また、+5V出力(#14)はUSBのバスにポリスイッチを介して接続されており、350mA以上の電流を流そうとすると切断される(サーボをつないだら切れました)。
+3.3V(#13)については内部のI/Oピンの消費分も含めて50mAまでとのこと。いずれもWROOM02を駆動するための電源には使えない。
USB-シリアル変換用モジュールは、WROOM02用のプログラムが動けば不要になるので別のブレッドボードになっている。WROOM02のTXDとRXDに接続し、GNDも共通化している。
ソフトウェア
ESP8266 core for Arduino 2.3.0
はじめに、WROOM-02のプログラミングをしばらくやっていなかったので、ESP8266 core for Arduino のバージョンが2.3.0になっていることに気付かなかった。以前にやったときは2.0.0だったと思う。変更点をざっと読んだ後、Arudino IDE(Arduino.cc 版の1.6.7をいまだに使用中)のツールメニュー/マイコンボード/ボードマネージャーの中にある、”esp8266 by ESP8266 Community“をクリックして選択し、「更新」ボタンをクリックした。
何の問題もなく、すんなりとversion 2.3.0となった。
このソフトウェアのドキュメントはこちら、コア部分のソースプログラムやサンプルの閲覧に便利なgithubはこちら。
HDC1000との接続
先に書いたように、I2CインタフェースをもつHDC1000は、Wireライブラリを使うことで簡単にスケッチから利用できる。さらに、HDC1000からの温度や湿度の取得については、Adafruit社か公開しているAdafruit_HDC1000_Libraryがそのものずばりだった。Wireライブラリと共に、このライブラリをArduino IDEにライブラリを読ませて使うことにした。
ライブラリのソースを眺めていると、コメントに Adafruitの製品を買ってサポートしてね! って書いてある。申し訳ないけどまだ買ったことがない。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#include <ESP8266WiFi.h> #include <Wire.h> #include "Adafruit_HDC1000.h" extern "C" { #include "user_interface.h" } #define LED1 12 #define MEASURE_INTERVAL_SECONDS 120 Adafruit_HDC1000 hdc = Adafruit_HDC1000(); // wifi const char* ssid = "ssid"; const char* password = "password"; const char* remote_host = "192.168.1.1"; void die(int msec, const char* cp) { if (cp) Serial.println(cp); for(;;) { digitalWrite(LED1, 1); delay(msec); digitalWrite(LED1, 0); delay(msec); } } void deep_sleep(int seconds) { Serial.println("deep_sleep()"); ESP.deepSleep(seconds * 1000 * 1000, WAKE_RF_DEFAULT); // 指定秒数後にリセットがかかる。 } bool wifi_ap_connect() { Serial.println("\nstart wifi_ap_connect()"); WiFi.mode(WIFI_STA); wifi_station_set_auto_connect(false); WiFi.begin(ssid, password); delay(200); int n; int count = 0; int retry = 0; while ((n = WiFi.status()) != WL_CONNECTED) { Serial.print(String(n) + " "); delay(1000); if (n == WL_NO_SSID_AVAIL || n == WL_CONNECT_FAILED) { WiFi.reconnect(); count = 0; retry++; } if (count++ > 60 || retry > 10) { Serial.println(""); return false; } } return true; } void setup() { delay(100); Serial.begin(115200); pinMode(LED1, OUTPUT); digitalWrite(LED1, 1); if (!hdc.begin()) die(500, "FATAL: Couldn't find HDC1000"); if (!wifi_ap_connect()) { deep_sleep(10); // reset myself after a while. } } bool wifi_send_data(float temp, float humi) { Serial.println("start wifi_send_data()"); WiFiClient client; if (!client.connect(remote_host, 80)) return false; String request = "/data/store_data.php?point_id=" + WiFi.macAddress() + "&T=" + String(temp) + "&H=" + String(humi); String req_line = "GET " + request + " HTTP/1.1\r\nHost: " + String(remote_host) + "\r\nConnection: close\r\n\r\n"; client.print(req_line); int count = 0; while(!client.available()) { // waif for response. delay(50); count++; if (count >= 200) { // limit = 10secs. return false; } } // HTTP/1.1 200 OK というステータス行だけ取れればいい。 while(client.available()){ String line = client.readStringUntil('\n'); delay(1); } return true; } void loop() { float temp = hdc.readTemperature(); float humi = hdc.readHumidity(); Serial.println("\nTemperature = " + String(temp) + " , Humidity = " + String(humi)); wifi_send_data(temp, humi); WiFi.disconnect(true); delay(100); deep_sleep(MEASURE_INTERVAL_SECONDS); delay(MEASURE_INTERVAL_SECONDS * 1000); } |
※ ssidとpasswordにはWiFiルーターに接続するためのパラメータを、remote_hostにはwebサーバーのipアドレスを指定のこと。
ソース コードの概要
今回のプログラムは、以下の5つ機能を順次実行する。MEASURE_INTERVAL_SECONDS で定義している秒数ごとに測定とデータ送信を行い、その合間には、ESP8266EXがもつディープスリープモードに入るようにしている。
- 初期化
setup();
開発時のためのシリアルポート初期化、GPIOの設定、Adafruit_HDC1000のインスタンスを使ったWireライブラリの初期化とHDC1000の存在確認を実行。 - アクセスポイント接続
wifi_ap_connect();
setup()内から呼び出すことで、所定のアクセスポイントに接続する。
今回利用したアクセスポイントがしょうしょうつながり難かったこともあり、WiFi.begin(ssid, password); 後の接続待ちを工夫した。単純に WL_CONNECTED を待ち続けるよりは、接続失敗が減ったようである。
wifi_ap_connect()が接続失敗(false)を返したら、setup() では、deep_sleep(); を呼び出すことでディープスリープモードに入り、10秒後の再起動によって最初からやり直す。 - 温度と湿度を測定
loop()内。HDC1000から温度と湿度を取得。
このプログラム自体は繰り返しを行わずに直線的に進むが、テスト時にはdelay()によって間を置きながら繰り返し測定を行ったりしたので、測定とそれ以降の処理はloop() 内に配置した。 - テータをwebサーバーに送信
wifi_send_data();
loop()内から呼び出すことで、所定のリモートホストに接続し、GETリクエストでデータを送信。
/data/store_data.php?point_id=MAC_ADDR&T=28.54&H=57.29
というリクエストを投げると、サーバー側で温度と湿度を保存する。
測定機器を一意に表すためにpoint_idとしてMACアドレスを使っており、パラメータTが温度、Hが湿度である。測定日時としては、サーバー側でdbにデータをinsertした日時を記録し利用する。 - ディープスリープ
deep_sleep();
loop()内の最後、および、wifi_ap_connect()失敗時に呼び出し、WROOM02をディープスリープモードとする。
ディープスリープモードについて
ディープスリープモードでは、WROOM02自体の消費電力が大きく抑制される。秋月電子のWi-Fiモジュール ESP-WROOM-02 DIP化キットのページに含まれているデータシート(ESP-WROOM-02 WiFi Module Version 0.4)によれば、wifi接続は維持できず、各GPIO出力も無電源状態になるものの、モジュールは10μAしか流れないという。
ESP8266 core for Arduino では、EspClass ( Esp.cpp )に用意されている、
void EspClass::deepSleep(uint32_t time_us, WakeMode mode);
を呼び出すことで、WROOM02をディープスリープモードとすることができる。
第1パラメータにウェークアップまでの時間をμ秒単位で指定し、第2パラメータには、ウェークアップ時のWiFi用RFモジュールのキャリブレーションの有無を指定する。第2パラメータに指定できる値(0~4)については、Esp.hに定義されており、RF_DEFAULT (= 0) を指定しておけば、通常のリセット時と同様に処理されるようである。
ディープスリープモード時にも、WROOM02 (ESP8266EX)の内部ではタイマーモジュール (RTC) に電力が与えられており、指定の時間が経過した時点でGPIO16(ESP8266EXのXPD_DCDC)をLOWレベルとする仕組みになっている。回路としてGPIO16をRST (EXT_RSTB) に接続しておくことで、GPIOやRFモジュールが初期化され、setup() から処理が再開することになる。
動作状況
USBを介して接続するPCのシリアルモニタには、以下のように表示される。
シリアルモニタには、
- リセットによる文字化け表示
- wifiアクセスポイントへの接続開始を示す表示と、接続または失敗までのWiFi.status()の値の列(1がWL_NO_SSID_AVAIL、6がWL_DISCONNECTED)。
- HDC1000から得た温度および湿度
- webサーバーへのデータ送信開始を示す表示。
- ディープスリープの開始
が、繰り返して表示される。(2)のステータス値の列が延々と続いているのは、アクセスポイントになかなかつながらないか、接続失敗を示しており、失敗した場合には温度/湿度を計測せずにディープスリープに入っている。
PCの側に置いてシリアルモニタをつなげると、今回使用しているアクセスポイント(WIFIルーター)につながり難くなるようだった。ただ、その次のタイミングではうまく接続できたりするので、ちょっと微妙な場所なのだろう。
まとめ
- I2Cを使った温度/湿度センサーモジュールの利用は簡単だった。
- ディープスリープモードにより、WROOM02の筐体がほとんどまったく熱を持たない。
- 次は、WEBサーバー側でのデータの取得と格納、ブラウザでのグラフ表示などを載せる予定です。