ESP32 を使って乾電池駆動で動作するワイヤレス温度計を作る方法を紹介します.
背景
「Fluentd と Raspberry Pi で作るセンサーネットワーク」では,Raspberry Pi を使って WiFi 経由でセンシングする方法を紹介しましたが,この方法では電源がとれない場所に設置できないという欠点がありました.Raspberry Pi は消費電力が大きすぎ,長期間の電池駆動には適さないためです.
そこで今回は,低消費電力で動作する ESP32 を使い,乾電池で動作する WiFi 温湿度計の作り方を紹介します.単3乾電池2本で約3ヶ月程度は動作するので,電源の無い屋外等に設置するのにぴったりです.
ホスト側のソフトとしては,Fluentd を使用します.
必要な部品
必要な部品はこんな感じ.
AliExpress で入手するもの
- ESP-WROOM-32
-
格安の WiFi モジュールです.Arduino IDE で開発することができます.
- ESP-32S Adapter Board
-
ESP32 を一般的な 2.54mm ピッチの端子に変換する基板です.端子間に半田をはじくレジストがないので,半田付けがちょっと難しいです.安価なので多めに買っておきましょう.
- ESP32 test board burn fixture
-
ESP32 に半田付けせずに,パソコンと接続したり,2.54mm ピッチの端子に変換したりできるボードです.モジュールを押し込むだけで使えます.無くても良いですが,手元にあると開発が楽になるのでオススメです.
Strawberry Linux で入手するもの
- TPS61291 超ローパワー昇圧コンバータ(2.5V/3V/3.3V)
-
乾電池2個から 3.3V を生成できるモジュールです.ESP32 自体は 3V を切っても動作できますが,センサー類はそうはいかないので,安定した測定のために組み込んで起きます.入れておくと,乾電池の容量を絞りきれる,というメリットもついてきます.
Tindie で入手するもの
- SHT31-D (Digital) Humidity & Temperature Sensor
-
温湿度計のモジュールです.制度は,温度±0.3℃ 湿度±2%RH.温度計用ととしては十分過ぎるスペック.
このモジュールの場合,水や埃からセンサーを保護するフィルターをつけることができるので,屋外に設置する場合にはこのモジュールがオススメです.フィルターはセンサチップメーカの純正品になります.
ソフト
Arduino IDE を使って,ESP32 から SHT-31 をたたいて,結果を WiFi 経由で Fluentd に投げるコードを書きます.
ポイントは,次の 2 点です.
- 消費電力を抑えるため,測定は5分ごとに行い,その間は消費電力を数 uA オーダに押さえる Deep Sleep Mode に入れる
- WiFi 接続の途中で固まることがあるので,タイマーを使って一定時間経過したら無条件に Deep Sleep Mode に入れる
具体的なコードは下記のようになります.
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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
/* * SHT-31 を使って,温度・湿度を取得し,それをあらかじめ指定された IP アドレスの * Fluentd に WiFi で定期送信します. */ #include <Wire.h> #include <WiFi.h> #include <HTTPClient.h> #include <esp_deep_sleep.h> #include <ArduinoJson.h> // 追加インストール要 #define I2C_SDA 25 #define I2C_SCL 26 #define WIFI_HOSTNAME "ESP32-outdoor" #define FLUENTD_IP "192.168.2.20" #define FLUENTD_TAG "/sensor" #define FLUENTD_PORT 8888 #define SHT31_DEV_ADDR 0x44 #define INTERVAL_MIN 5 typedef struct { float temp; float humi; } sht31_value_t; bool postToFluentd(String jsonStr) { HTTPClient http; http.begin(FLUENTD_IP, FLUENTD_PORT, FLUENTD_TAG); http.addHeader("Content-Type", "application/x-www-form-urlencoded"); int code = http.POST("json=" + jsonStr); http.end(); if (code != HTTP_CODE_OK) { log_e("Faile to POST to " FLUENTD_IP "."); } return true; } uint8_t crc8(const uint8_t *data, int len) { static const uint8_t POLY = 0x31; uint8_t crc = 0xFF; for (int i = 0; i < len; i++) { crc ^= data[i]; for (int j = 0; j < 8; j++) { if (crc & 0x80) { crc = (crc << 1) ^ POLY; } else { crc <<= 1; } } } return crc; } const char *createJsonStr(sht31_value_t& sht31_value) { static char buffer[256]; DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); json["temp"] = sht31_value.temp; json["humi"] = sht31_value.humi; json["hostname"] = WIFI_HOSTNAME; json.printTo(buffer); return buffer; } bool senseSht31(sht31_value_t& sht31_value) { uint8_t data[6]; Wire.begin(I2C_SDA, I2C_SCL); Wire.setClock(100000); Wire.beginTransmission(SHT31_DEV_ADDR); Wire.write(0x24); Wire.write(0x00); Wire.endTransmission(); delay(20); Wire.requestFrom(SHT31_DEV_ADDR, 6, 1); if (Wire.available() != 6) { log_e("Faile to read from SHT-31."); return false; } for (int i = 0; i < sizeof(data); i++) { data[i] = Wire.read(); } if ((crc8(data, 2) != data[2]) || (crc8(data + 3, 2) != data[5])) { log_e("Faile to validate CRC."); return false; } // disable pull up pinMode(I2C_SDA, INPUT); pinMode(I2C_SCL, INPUT); sht31_value.temp = -45 + (175 * (((uint16_t)data[0]) << 8 | data[1])) / (float)((1 << 16) - 1); sht31_value.humi = 100 * (((uint16_t)data[3]) << 8 | data[4]) / (float)((1 << 16) - 1); return true; } void WiFiEvent(WiFiEvent_t event) { switch (event) { case SYSTEM_EVENT_STA_START: WiFi.setHostname(WIFI_HOSTNAME); break; default: break; } } bool sense_and_send() { sht31_value_t sht31_value; WiFi.mode(WIFI_STA); WiFi.onEvent(WiFiEvent); WiFi.begin(); // 一度は下記のようにして,SSID・パスワードを指定する必要有り. // WiFi.begin("SSID", "パスワード"); if (!senseSht31(sht31_value)) { log_e("Failed to communicate with SHT-31."); return false; } String jsonStr(createJsonStr(sht31_value)); for (int i = 0; i < 50; i++) { if (WiFi.status() == WL_CONNECTED) { return postToFluentd(jsonStr); } delay(200); } log_e("Failed to connect WiFI."); return false; } void IRAM_ATTR onTimer() { esp_deep_sleep_enable_timer_wakeup(INTERVAL_MIN * 60 * 1000 * 1000); esp_deep_sleep_start(); } void setup() { // 2.5 秒後に onTimer を呼び出して deelp sleep に入る hw_timer_t * timer = NULL; timer = timerBegin(0, 80, true); timerAttachInterrupt(timer, &onTimer, true); timerAlarmWrite(timer, 2500000, true); timerAlarmEnable(timer); esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); esp_deep_sleep_pd_config(ESP_PD_DOMAIN_MAX, ESP_PD_OPTION_OFF); sense_and_send(); delay(1000); } void loop() { } |
環境に応じて書き換えないといけないのは,下記の部分です.
1 2 3 4 5 |
#define WIFI_HOSTNAME "ESP32-outdoor" #define FLUENTD_IP "192.168.2.20" #define FLUENTD_TAG "/sensor" #define FLUENTD_PORT 8888 |
1 2 3 |
WiFi.begin(); // 一度は下記のようにして,SSID・パスワードを指定する必要有り. // WiFi.begin("SSID", "パスワード"); |
元のコードのままだと 192.168.2.1 のポート 8888 で待ち受けている http インプットプラグインに対して,「{“temp”:24.92,”humi”:63.07,”hostname”:”ESP32-outdoor”}」のような JSON を 5 分おきに投げつけます.
SHT-31 の SDA, SCL とはポート 25, 26 を使って接続します.
まずは,上で紹介した ESP32 test board burn fixture とブレッドボードを使ってささっと回路をくみ上げて動作確認するのがお勧めです.
その際,FLUENTD_TAG を “/test” のようにしたうえで,サーバ側の Fluentd に以下の設定をするのがオススメです.ESP32 が送信したデータがファイルに書き出されますので,tail -f しておけばリアルタイムに通信内容がモニタできます.
1 2 3 4 5 |
<match test.**> @type file tag_keys ["hostname"] path /tmp/fluentd_test.log </match> |
回路
ソフトの動作確認ができたら組み上げます.普通に結線するだけです.こんな感じで,100均のタッパーに収められます.
動作確認
Fluentd で収集したデータを Grafana でグラフ化するとこんな感じです.
今回作ったセンサーのデータは赤色の線の「屋外」のものになります.温度はなめらかに変化しており,精度良く測定できています.逆に湿度は小刻みな変化をしっかり捉えており,フィルター越しでも応答性をしっかり確保できていることが確認できます.
消費電力確認
乾電池駆動でどの程度もつか確認するため,3.3V の消費電流を測定してみました.
動作時は 150mA 近くまでいきますが,スリープ時は約 8uA まで落ち,平均すると約 1mA です.
単三アルカリ乾電池だと約 2000mAh なので,2個使いだとざっくり 2000 * (1.5*2 / 3.3) / 24 = 75日程度持つ計算になります.電源ICの効率とかの問題はありますが,TPS61291 が昇圧して乾電池を完全に使い切ることも考えると,約3ヶ月程度は動作しそうです.
雑感
ESP32 はなかなかいけてると感じました.
ZigBee 等と比べると WiFi は消費電力が断然多いので,格安 WiFi モジュールにあまりよいイメージなかったのですが,WiFi 電波が届く範囲で間欠動作させるのであれば,欠点はあまり感じません.
電力問題が足かせにならないのであれば逆に下記のようなメリットが光ってきます.
- Arduino IDE を使って開発できるので,作業がとてもスムーズ.
(ワンクリックでコンパイル,書き込み,ソフト動作が可能) - I2C や SPI といった通信が簡単に使えるのでセンサーとの接続性が良い.
- センサーデータを使うホスト側とダイレクトに接続するので,開発時の見通しが良い.
一部ですごく注目されているのも納得です.
コメント
[…] 回路については『ESP32 と Fluentd で作る高精度 WiFi 温湿』,ケースについては『WiFi 温湿度計の防水化』にて紹介しています. […]