[ESP8266 IoT] 人感センサーでESP8266の目をさまさす

人感センサーとESP8266のスリープ機能と充電池を使った、省電力の実験をしてみました。検知するとESP8266のディープスリープをリセットしMQTTを介して検知状態をネットへ通知します。

接続

電源はニッケル水素充電池4本を直列接続し、三端子レギュレーターで3.3Vにしたものを回路へ供給しています。

人感センサー(焦電センサー)には、SB412Aを使いました。検知でVOピンが「ハイ」となる仕様です。

人感センサーの検知出力でESP8266のスリープモードから復帰させるために、人感センサーの信号は、ESP8266のGPIO16のスリープモードリセット信号出力とNANDした後に、0.1μFのキャパシタを介してESP8266のリセットピンへ接続しています。

ESP8266はスリープ状態でGPIO16が「ハイ」となります。スリープ中でかつ人感センサーの出力が「ハイ」となったときに、NAND回路の出力が「ロー」となり、ESP8266がリセットされスリープ状態から復帰します。

センサー値 GPIO16 NAND回路出力
L L H
H L H
L H H
H H L

GPIO16とリセットピン間にダイオードを入れることで、スリープタイムアウト時(GPIO16が「ロー」)にもリセットされスリープから復帰するようにしました。

スケッチ例

人感センサーの状態 ONまたはOFFをMQTTブリーカーへパブリッシュしてディープスリープするスケッチです。検知しない状態でも1時間毎に人感センサーの状態をパブリッシュします。

人感センサーの検知でスリープモードから復帰させる部分はハードウェアで行うため、特別なコードを追加する必要はありません。

検知時により早くMQTTブローカーへ検知結果を通知させるため、IPアドレスはDHCPによる動的割り当てではなく、静的に設定しています。

また、WiFiやMQTTブローカーへ接続できないときは、永久に接続を試行するのではなく、何回か試行した後に短い期間ディープスリープするようにしています。

/*
 ESP8266 IR Motion deep sleep wake-up test by MONOxIT inc.
 Based on Basic ESP8266 MQTT example with pubsubclient library. 
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

//ネットワーク環境に合わせる
const char* ssid = YOUR_SSID;
const char* password = YOUR_PASSWORD;
const char* mqttServer = YOUR_MQTT_BROKER;

// 静的IPアドレス
IPAddress ip(192, 168, 1, 170); 
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

const char* mqttDeviceId = "HS02";
const char* mqttTopic = "home/sensor02";
const int motionPin = 14;

//短いスリープ(マイクロ秒) 5分
const unsigned long updateIntervalMicrosShort = 5*60*1000*1000;
//長いスリープ(マイクロ秒) 1時間
const unsigned long updateIntervalMicrosLong = 60*60*1000*1000;

WiFiClient espClient;
PubSubClient client(espClient);

uint8_t motionPinStatus = LOW;

void connectToMqttBroker() {
 for(int i = 0; (i < 3) && (!client.connected()); i++){
   if (!client.connect(mqttDeviceId)) {
   delay(5000);
   }
 }
 // MQTTブローカーへ接続できないときは、短い時間ディープスリープ
 if(!client.connected()) ESP.deepSleep(updateIntervalMicrosShort);
}

void setupWifi() {
 delay(10);
 WiFi.begin(ssid, password);

 for(int i = 0; (i < 20) && (WiFi.status() != WL_CONNECTED); i++) {
   delay(500);
 }
 // WiFi接続できないときは、短い時間ディープスリープ
 if(WiFi.status() != WL_CONNECTED) ESP.deepSleep(updateIntervalMicrosShort);
}

void updateMotionStatus(){
 String motionStr = "\"OFF\"";
 if (motionPinStatus == HIGH) {
   motionStr = "\"ON\"";
 }
 //JSON形式でセンサーの状態ONかOFFをパブリッシュ
 String payload = "{\"motion\":";
 payload += motionStr;
 payload += "}";
 client.publish(mqttTopic, (char*) payload.c_str());
}

void setup() {
 pinMode(motionPin,INPUT);
 //人感センサーの値を取得
 motionPinStatus = digitalRead(motionPin);
 WiFi.mode(WIFI_STA);
 WiFi.config(ip, gateway, subnet);
 setupWifi();// WiFi接続
 // MQTTブローカーへ接続
 client.setServer(mqttServer, 1883);
 connectToMqttBroker();
 updateMotionStatus();// 人感センサーの値をパブリッシュ
 delay(100);
 client.disconnect();// MQTTブローカーから切断
 delay(100);
 uint32_t sleepTime = updateIntervalMicrosLong;
 if (motionPinStatus == HIGH) {
   // 検知状態なら短いスリープ時間をセット
   sleepTime = updateIntervalMicrosShort;
 }
 ESP.deepSleep(sleepTime);//ディープスリープへ移行し省電力に
}

void loop() {
 client.loop();
 delay(100);
}

まとめ

三端子レギュレーターやNAND回路、ディープスリープ状態のESP8266の消費電力が常に消費される電力の主なものですが、2700mAhの単三型ニッケル水素充電池を四本直列にして電源として供給し、検知回数6回程度でおよそ6日~程稼働しました。週に1度電池を交換すればよいので実用的に使用できています。

人感センサー以外にも検知で「ハイ」となるセンサーであれば、同じ回路を使うことができます。検知で「ロー」となるセンサーでもNANDを他の論理回路にすることで対応できそうです。