Arduino - リレー回路を使った待ち時間の省電力化を行ってみた。

#概要
今回、WioLTEでの省電力化を調査していて、リレー回路を用いた電源をＯＮ／ＯＦＦする消費電力の少ない回路を作れれば、Sleep時の電力を大幅に削ることができるのではないかと思い調査・実験をして見ました。

#構成
・リレー制御マイコンボード：ArduinoNano
・リレーモジュール：[SCMの開発/家電制御用(Arduinoのための)5Vのリレーモジュール](https://www.miniinthebox.com/ja/p/arduino-5v-relay-module-for-scm-development-home-appliance-control_p903299.html?currency=JPY&litb_from=paid_adwords_shopping&country_code=jp&utm_source=google_shopping&utm_medium=cpc&adword_mt=&adword_ct=202527949640&adword_kw=&adword_pos=1o18&adword_pl=&adword_net=g&adword_tar=&adw_src_id=14001329091_866084530_46433169769_aud-344923713519:pla-330135169593&gclid=Cj0KCQjw6MHdBRCtARIsAEigMxHC8i4BhASvpRWBHDs12zbjc6es1j27vZ5nQE4EApCftDNEpi1JIukaAiB3EALw_wcB)

・メイン処理マイコンボード（仮）：ArduinoNano

#制御フロー
左側のメイン制御部分は仮でArduinoNanoを使用しテストを行う。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/0df3cc88-4628-d408-9dcb-6ffbe1a2210e.png)


#結果
まずは結果から報告します。

＜消費電流の確認結果＞
・メインマイコンボード稼動時：83mA（メインマイコンボードの電流は含んでいません）
・スリープ時：16mA

###●リレースイッチがＯＮになり、下側のメインボードが稼動しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/34f0c880-ed42-c36f-f64a-84c597c9677e.png)

###●リレースイッチがＯＦＦになり、下側のメインボードの電源がＯＦＦになっています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/681c04ba-aab8-a783-ab72-6fdf1252bbaf.png)

現在の制御では、上記の結果となりました。
WioLTEの場合、下記の記事で省電力化を行った場合でも待機時に40mA～50mAは消費するとあるので、待機時間が長いセンサーの場合は非常に有効ではないかと思います。
当然、センサーの実行頻度によってはこの装置を使ったほうが消費電力が多くなってしまうこともあるので、そこは都度判断して行きたいと思います。
参考：https://technologicaladvance.blog.fc2.com/blog-entry-155.html

#プログラム参考サイト
・Sleep時の省電力化（[URL](https://diy.smart-agri.info/2017/06/26/%E3%80%90arduino%E3%80%91sleep%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E9%9B%BB%E5%8A%9B%E6%B6%88%E8%B2%BB%E3%82%92%E6%8A%91%E3%81%88%E3%81%A6%E3%81%BF%E3%81%9F/)）
・２台のArduinoのシリアル通信（[URL](https://shizenkarasuzon.hatenablog.com/entry/2018/07/24/225915)）
・シリアル通信で受け取った文字列を数値に変換（[URL](https://qiita.com/e-katayama/items/338065a1b3d52b173c2d)）

#スケッチ（リレー制御マイコンボード側）
```c++:relay_nano.ino

#include "common.h"

const int RELAY_PIN = 2;

String g_strRet;
unsigned long g_delayMilliseconds;

/**
 * setup
 */
void setup() {
  Serial.begin(9600); 
  pinMode(RELAY_PIN, OUTPUT);

  // WDT Setup（固定8秒）
  delayWDT_setup(9); // ウォッチドッグタイマー割り込み条件設定

  digitalWrite(RELAY_PIN, HIGH);  
}

/**
 * loop
 */
void loop() {
  while(Serial.available()){
    g_strRet = Serial.readStringUntil(';');
    Serial.println(g_strRet);
    delay(30);
    
    // 停止時間を取得
    g_delayMilliseconds = convNumFromString(g_strRet);

    // sleep中はRelayの電源をOFF
    digitalWrite(RELAY_PIN, LOW);

    // 停止時間スリープ
    delayWDTMinits(g_delayMilliseconds);    

    // Relayの電源をON
    digitalWrite(RELAY_PIN, HIGH);
  }
  
  delay(100);
}
```

```c++:common.h
#include "Arduino.h"


unsigned long convNumFromString(String strVal);
void delayWDTMinits(unsigned long milliseconds);
void delayWDT_setup(unsigned int ii);
void delayWDT();

```

```c++:common.cpp
#include "common.h"
#include <avr/sleep.h>
#include <avr/wdt.h>

/**
 * 文字列を数値に変換(整数のみ）
 * 
 * 0 ～ 4294967295　まで変換可能
 */
unsigned long convNumFromString(String strVal){
  int idx;
  int len = strVal.length();
  unsigned long ret = 0;
  unsigned long val;
  unsigned long powVal;
  const unsigned long num = 10;
  int i;
  
  for(idx = 0;idx < (len - 1);idx++){
    val = strVal.charAt(idx) - 0x30;
    if (val >= 0 && val <= 9){
      if(val != 0){
        powVal = 1;
        for(i = 0;i < len - idx - 1;i++){
          powVal = powVal * 10;
        }
        ret += val * powVal;
      }
    }
    else{
      Serial.print(strVal);
      Serial.print(",");
      Serial.print( pow(10,len - idx - 1));
      Serial.print(",");
      Serial.println(val);
      return 0;
    }
  }
  val = strVal.charAt(len - 1) - 0x30;
  ret += val;

  if(strVal != String(ret)){
    return 0;
  }
  
  return ret;
}

/**
 * ミリ秒でDelayする。
 */
void delayWDTMinits(unsigned long milliseconds){
  int cnt = milliseconds / 8000;
  int i;

  for(i = 0;i < cnt;i++){
    // 8秒停止
    delayWDT();
  }
}

/*
 * ウォッチドッグ処理
 */
void delayWDT_setup(unsigned int ii) { // ウォッチドッグタイマーをセット。
  // 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記
  // 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
  // 6=1sec, 7=2sec, 8=4sec, 9=8sec
  byte bb;
  if (ii > 9 ){ // 変な値を排除
    ii = 9;
  }
  bb =ii & 7; // 下位3ビットをbbに
  if (ii > 7){ // 7以上（7.8,9）なら
    bb |= (1 << 5); // bbの5ビット目(WDP3)を1にする
  }
  bb |= ( 1 << WDCE );
  
  MCUSR &= ~(1 << WDRF); // MCU Status Reg. Watchdog Reset Flag ->0
  // start timed sequence
  WDTCSR |= (1 << WDCE) | (1<<WDE); // ウォッチドッグ変更許可（WDCEは4サイクルで自動リセット）
  // set new watchdog timeout value
  WDTCSR = bb; // 制御レジスタを設定
  WDTCSR |= _BV(WDIE);
} 
ISR(WDT_vect) { // WDTがタイムアップした時に実行される処理
 // wdt_cycle++; // 必要ならコメントアウトを外す
}
  
 
void delayWDT() { // パワーダウンモードでdelayを実行
  //delayWDT_setup(t); // ウォッチドッグタイマー割り込み条件設定
  ADCSRA &= ~(1 << ADEN); // ADENビットをクリアしてADCを停止（120μA節約）
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // パワーダウンモード
  sleep_enable();
  
  sleep_mode(); // ここでスリープに入る
  
  sleep_disable(); // WDTがタイムアップでここから動作再開 
  ADCSRA |= (1 << ADEN); // ADCの電源をON (|=が!=になっていたバグを修正2014/11/17)
}

```
#スケッチ（メイン処理マイコンボード（仮）)
```c++:nano_sirial_out.ino
const int LED_PIN = 13;
int flg = 0;
void setup(){
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
}

void loop(){
  // 一度実行したら２度目は実行しない。
  if(flg != 0){
    delay(1000);
    return;
  }
  
  // メイン処理の代わり（ＬＥＤ５回点滅）
  for(int i=0;i < 5;i++){      
    digitalWrite(LED_PIN, HIGH);
    delay(200);
    digitalWrite(LED_PIN, LOW);
    delay(200);
  }
  delay(5000);

  // 30秒Sleepする。
  Serial.println("30000;");
  flg = 1;
}

```


#まとめ
この回路が本番で使えるかはまだ調整が必要だと思いますが、消費電力を抑える一つの方法としては有りではないかなと思います。

現在、Sleepも8秒ごとの停止を指定時間分繰り返すということをしているので、正確な時間での停止はできていないと思います。３０秒くらいだとズレは無かったですが、、たとえば１時間ごとに実行などの場合は、メイン処理内で現在時刻を取得し、微調整を行う必要があると思われます。
あと現在使っているリレーモジュールが結構、電力を消費するので、もっと消費電力の少ないものがあれば、そちらを活用したいと思います。

未実施：
・連続稼動時間テスト（リレーモジュールの寿命も考慮）
・稼動環境テスト（湿度や温度の高い場所での稼動）

あと、電力を調べるのに電流計を購入したのですが、最初に買った安い電流計はArduinoNanoではなぜか稼動しなかったので、結局、高かったですが、[AVHzY USBテスター](https://goo.gl/kYqCn3)を購入して使用しました。安定して動作してくれるので、今後の電力調査に活躍してくれそうです！