IoT開発(5)
ESP-WROOM-02をDeepSleepで長時間駆動させる

2021年6月12日

どもです。
この記事は、以下の記事の続きです。

本エントリでは、ESP-WROOM-02を電池で長時間させる方法について書きます。

1.DeepSleep

ESP-WROOM-02には、「DeepSleep」というモードがあります。
ESP-WROOM-02/ESP8266の仕様書によれは、このモードは以下のような場合に使用します。

  • WiFi接続を維持する必要が無い
  • データ送信の間隔が長い

具体例として、

  • 100秒ごとに温度を測定する
  • 300秒スリープし、APに接続するために0.3~1.0秒を要する

という場合が挙げられています。

1.1.復帰の方法

DeepSleepモードから復帰する方法は、ボタンでの復帰とタイマーでの復帰があります。
今回のエントリでは、「タイマーでの復帰」を採用します。

2.回路

DeepSleepモードを使用する際には、回路をこれまで使用してたものから変更する必要があります。
変更点は、「IO16」を「RST」につなげる、という点です。
「IO16」と「RST」をつなげることで、DeepSleepモードから復帰させられるようになります。
具体的な回路図は、下記になります。
iot_at_home_005_esp_wroom_02_001

3.プログラム

DeepSleepモードを使用するにあたり、コードに対して変更を行っています。
変更した後のコードは、下記になります。

#include <SPI.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include "Ambient.h"

extern "C" {
#include "user_interface.h"
}

#define _DEBUG 0
#if _DEBUG
#define DBG(...) { Serial.print(__VA_ARGS__); }
#define DBGLED(...) { digitalWrite(__VA_ARGS__); }
#else
#define DBG(...)
#define DBGLED(...) { digitalWrite(__VA_ARGS__); }
//#define DBGLED(...)
#endif /* _DBG */

#define LED 4
#define ERR_LED 5
#define CS_E  15

const char* ssid = "Buffalo-G-D0F8";
const char* password = "prjhgbvtvcich";

unsigned int channelId = 23197;
const char* writeKey = "39f9577973a01df0";

WiFiClient client;
Ambient ambient;

const float temperature_min = -55.0;
const float temperature_max = 150.0;

void setup()
{
	#ifdef _DEBUG
	Serial.begin(115200);
	delay(20);
	#endif
	DBG("Start");
	pinMode(LED, OUTPUT);
	pinMode(ERR_LED, OUTPUT);

	WiFi.begin(ssid, password);

	int i = 0;
	while (WiFi.status() != WL_CONNECTED) {
		delay(500);

		DBGLED(LED, i++ % 2);
		DBG(".");
	}

	DBGLED(LED, LOW);
	DBGLED(ERR_LED, LOW);
	DBG("WiFi connected\r\n");
	DBG("IP address: ");
	DBG(WiFi.localIP());
	DBG("\r\n");

	ambient.begin(channelId, writeKey, &client);

	DBG("Set SPI to communicate with MDK001");
	DBG("\r\n");
	pinMode(CS_E, OUTPUT);
	digitalWrite(CS_E, HIGH);

	//Initialize SPI configuration.
	SPI.begin();
	SPI.setDataMode(SPI_MODE3);
	SPI.setBitOrder(MSBFIRST);
	SPI.setClockDivider(SPI_CLOCK_DIV8);

	digitalWrite(CS_E, LOW);
	SPI.transfer(0x54);

	delay(240);
}

float get_temperature()
{
	uint16_t readVal = 0;
	float temp = 0;

	readVal = (uint16_t)SPI.transfer(0) << 8; //Upper 8bit.
	readVal = readVal | SPI.transfer(0);
	DBG("readVal = ");
	DBG(readVal, HEX);
	DBG("\r\n");

	readVal = readVal >> 3;

	if (0x1000 & readVal) {
		readVal = (uint32_t)readVal - 8192;
	}
	temp = ((float)((int16_t)readVal)) / 16.0;

	return temp;
}

void loop()
{
	DBGLED(LED, 1);
	DBGLED(ERR_LED, 0);
	uint16_t readVal = 0;

	float temp = 0;

	/*
	 *  測定結果が、センサーの「精度」の範囲に収まっているか確認する。 
	 *  収まっていない場合には、再度測定を実施しなおす。
	 *  (ただし、測定の間隔は1秒空ける。)
	 */
	do {
		DBGLED(ERR_LED, 1);
		temp = get_temperature();
		DBG("temp = ");
		DBG(temp, HEX);
		DBG("\r\n");

		delay(1000);
	} while((temp < temperature_min)|| (temperature_max < temp));
	DBGLED(ERR_LED, 0);

	ambient.set(1, temp);
	ambient.send();

	DBGLED(LED, 0);

	//Sleep 5 minutes.
	ESP.deepSleep(5 * 60 * 1000 * 1000, WAKE_RF_DEFAULT);  

	delay(1000);  //Dummy.
}

3.1.コードの解説

簡単ですが、変更点を中心にコードを解説します。
これまでのコードからの主な変更点として、下記が挙げられます。

  • wifi_set_sleep_type()の削除
  • 測定結果の判定~やり直し

3.1.1.wifi_set_sleep_type()の削除

これまでのコードでは、setup()の先頭で、wifi_set_sleep_type()メソッドを削除しています。
wifi_set_sleep_type()メソッドで指定可能な引数は、以下のように定義されています。

typedef enum {
	NONE_SLEEP_T	= 0,
	LIGHT_SLEEP_T,
	MODEM_SLEEP_T
} sleep_type_t;

見ての通り、DeepSleepモードに対応する値がありません。
今回の目的である、DeepSleepモードを使用するにあたり、この設定は不要なので、削除しました。

3.1.2.測定結果の判定~やり直し

これまで使用してきた温度センサーMDK001(ADT7310)の測定範囲は、-55℃~+150℃となっています。
これまでのエントリで使用してきたプログラムでは、この判定を行わず、取得できた値をそのまま出力/送信していましたが、それではまずいので、これを機に修正を行っています。
なお、範囲外の値を取得した場合には、「正常な値が取得できるまで測定をやり直す」という動作にしています。
この処理についても、測定の再実行の回数に制限を設けるなどの対応が本来は必要です。

4.測定結果

ここまでで書いてきた回路、およびプログラムを使用して、1日気温を測定した結果をグラフ化したのが、下記です。
iot_at_home_005_esp_wroom_02_002
途中で値が「0」に落ちてしまっています。
しかしそれ以外は、温度が測定ができています。
最高気温も30℃を越えており、とても暑い一日でした。
測定結果も、その体感に一致しています。