省電力化:マスターとスレーブの分離
ここで、フィールドにて1時間に1回、あるいは2時間に1回程度起動し、センサーの値を記録する装置を長期間駆動することを考えます。大部分の時間は寝ているだけなのですが、第13回で学んだようなスリープを使っても、Arduinoに接続したモジュールは電力を消費し続けます。これを完全に切断できればよいのですが、なかなか難しいようです。これを克服する手っ取り早いやり方としては、ロギングを行う本体と、それのスイッチをON/OFFするだけの電源管理モジュールを分離することです。前者にはいろいろなモジュールがついているわけですが、そもそも全体の電源が切られるわけなので、起動時以外は全く電力を消費しません。後者はスリープ状態で待機するわけですが、アラーム用のRTCモジュールだけ接続しているので、消費電力は抑えられます。電源をつかさどる後者をマスター(主)、それに命令されて起動・切断を繰り返す前者をスレーブ(従)とよぶことにします。ここでは、例として、スレーブには第8回の差動入力版電圧ロガーを使うことにします。マスターのほうは、第13回ですでに完成しています。リレーを1時間に一度、10秒だけONするシステムをつくったので、このリレーをスイッチにしてスレーブと電池をつなげばよいのです(10秒あれば、だいたいのロギングは終わります)。これらをまとめて絵にすると、

ここではスレーブにUNO(もちろんPro Miniなどでもいいです)、マスターにPro Mini(こちらがUNOだと省電力化できません)を使用しています。スケッチは、スレーブには第8回(差動入力版)、マスターには第13回のものを入れます。
ここで、システムの稼働時間について計算してみます。消費電流は、バッテリー電圧やつくるものによって異なるのですが、ざっと
マスター:起動時(3600秒のうち10秒)約100mA、スリープ時(3600秒のうち3590秒)約1mA
スレーブ:起動時(3600秒のうち10秒)約50mA、切断時(3600秒のうち3590秒)0mA
マスターはリレーを駆動するので、第11回に書いた通り、短時間ながらかなりの電流を消費します。スレーブは完全切断されている間は、当然消費電力は0です。1時間あたりの平均電流に直すと、
マスター:(10×100+3590×1)/3600 = 1.3mA
スレーブ:(10×100+3590×1)/3600 = 0.14mA
合わせて約1.5mA
よって、2500mAhの公称容量をもつエネループプロでは、駆動時間は2500mAh/1.5mA=1667h=70日(約2か月)となります。微妙?同じ市内での観測なら悪くないですが、山間部まで2か月に1度の電池交換をしに行っているヒマはありませんね。
TPL5110という秘密兵器
ここまでは、管理人も電子工作を始めて数か月でたどり着きました。しかし、この「駆動時間2か月」の壁をなかなか破れないでいました。もちろん、太陽光電池を使うなどの手段もあるのですが、だんだんと機器が大がかりになっていきます。このとき見つけたのが、TPL5110という電源管理用チップでした。これこそまさに欲していたものでした。
この製品は、Amazonに出ていないことも多いです。スイッチサイエンス(Amazonに出ているときは、この会社が出しているのですが)かDigikey、あるいはChip1Stopなどで買えます。ここではTPL5110のモジュール版を扱いますが、基板に実装されていないチップそのものとして売っていることもあるので注意してください。この製品は、一言でいえば、上で自作したマスターがそのまま製品になったものです。しかし、スリープ時の待機電力が非常に小さく、5V電源に対して公称20μAですので、桁違いの省電力です。この製品は、スケッチを書き込むわけではなく、以下の要領で動きます。
・Delay - GND間の抵抗に応じて起動時間間隔が変化する。この抵抗は、可変抵抗(ポテンショメーター)のつまみで変えられる
・Vcc – GNDに対して電力供給する。時間がくると、この電圧がDRV端子から出力される。
・これによって起こされたスレーブからDoneに信号を送る(スレーブのデジタルピンからHIGHの信号を送る)と再びスリープに入る
・上の黒いボタンを押すと、起動時間間隔に関わらず起動する
非常にシンプルです。より詳細は以下のAdafruit社のウェブサイトに書いてあります(英語)。
https://learn.adafruit.com/adafruit-tpl5110-power-timer-breakout
このページの表にもとづくと、起動時間間隔と可変抵抗の関係については、例えば、
10 sec: 11.2 kΩ
1 min: 22.02 kΩ
30 min: 92.43 kΩ
1 hour: 124.91 kΩ
2 hours: 170 kΩ
です。この極小のポテンショメーターを回してこれらの抵抗値をぴったり得るのは不可能なので、起動間隔は「だいたい1 hour」くらいの設定で諦めるしかありません。もちろん、ポテンショメーターをはずして、固定抵抗をつけてもよいようです。
TPL5110を使う際の接続とスケッチ例を示します。第8回の差動入力版のスケッチを原型に、TPL5110に関係した部分だけ赤字で示してあります。

#include <SPI.h>
#include <SD.h>
#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC
#include <Time.h>
#include <TimeLib.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h > //https://github.com/adafruit/Adafruit_ADS1X15
const int chipSelect = 10; // Arduino UNOでは10、Arduino MEGAでは53
const int donepin = 9; // TPL5110のDoneにつなぐピン
#define AVERAGENUM 20 // 取得データの平均化に用いるサンプル数
//#define CFACTOR 0.18750 // GAIN_TWOTHIRDS: +/-6.144V range: Calibration factor (mV/bit) for ADS1115
#define CFACTOR 0.12500 // GAIN_ONE: +/-4.096V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.06250 // GAIN_TWO: +/-2.048V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.03125 // GAIN_FOUR: +/-1.024V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.01563 // GAIN_EIGHT: +/-/0.512V range: Calibration factor (mV/bit) for ADS1115
//#define CFACTOR 0.00781 // GAIN_SIXTEEN: +/-0.256V range: Calibration factor (mV/bit) for ADS1115
Adafruit_ADS1115 ads; // ADS1115のインスタンスを作成
void setup(void)
{
/* ----- Setting up serial communication with PC ------ */
/* ここでUSBを介してPCとシリアル通信を始める。9600はシリアル通信のボーレート */
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
//何らかの問題があってシリアルポートに接続できないときは、このループにトラップされる
}
/* ----- Initialisation of SD card ------ */
Serial.print("Initializing SD card...");
//see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
/* ----- Setting time based on RTC ----- */
setSyncProvider(RTC.get); // ここでRTCの時刻をもとにArduinoの時刻を合わせる
if(timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
delay(1000);
/* ----- Setting of ADS1115 AD converter ----- */
ads.setGain(GAIN_ONE);
ads.begin();
pinMode(donepin, OUTPUT);
digitalWrite(donepin, LOW);
}
void loop(void)
{
/* データの読み取り */
int cnt1=0;
float read_from_ads;
float averaged_input;
averaged_input=0;
for(cnt1=0;cnt1<AVERAGENUM;cnt1++) //AVERAGENUMの数だけ繰り返す
{
read_from_ads = float(ads.readADC_Differential_0_1()); //チャネル0から差動(Differential)入力
averaged_input=averaged_input+read_from_ads/AVERAGENUM; //読んだ値を足していく:AVERAGENUMで割ることで平均化している
}
averaged_input=averaged_input*CFACTOR; //校正係数を掛けて電圧とする
/* PCのシリアルモニタに表示 */
Serial.print(year());Serial.print("/");Serial.print(month());Serial.print("/");Serial.print(day());Serial.print(" ");
Serial.print(hour());Serial.print(":");Serial.print(minute());Serial.print(":");Serial.print(second());Serial.println("");
Serial.print(averaged_input);
Serial.println(" mV"); //" mV"と表示、printではなくprintlnとすることで改行する
/* SDカードに書き込み */
File dataFile = SD.open("datalog.csv", FILE_WRITE);
if (dataFile)
{
dataFile.print(year());dataFile.print("/");dataFile.print(month());dataFile.print("/");dataFile.print(day());dataFile.print(" ");
dataFile.print(hour());dataFile.print(":");dataFile.print(minute());dataFile.print(":");dataFile.print(second());dataFile.print(" ");
dataFile.print(averaged_input);
dataFile.println(" mV"); //" mV"と表示、printではなくprintlnとすることで改行する
}
dataFile.close();
digitalWrite(donepin, HIGH); //ここでTPL5110に信号HIGHを送ると電源が切れる
delay(100);
digitalWrite(donepin, LOW); //これは不要かもしれないが、切れなかったときに再試行するためLOWに戻しておく
delay(100);
}
スリープ中は20μAとして先ほどの計算をすると、エネループプロで8か月くらい稼働するという計算になります。実績として、同じ装置を2台、冬期の間走らせたことがありますが、11月に開始し、翌年5月に見たら、1台は動き続け、もう1台は5か月くらいで止まっていました。北海道の話ですので、冬期の外気温のせいで電池容量が下がったのかもしれませんし、もしかしたらうまくスリープに入れないことがあったのかもしれません。実際の環境で信頼性をもって8か月稼働はなかなか難しいです。これが、現在のところ、「普通の」充電池でできる限界かと思います。