こちらの記事に書いたが、現在家ではIRKitとArduinoを使って、(すごく物理的な)ホームオートメーションを実現している。
Google Home とIRKitとArduinoでホームオートメーション
↓Youtubeです
スマートでない壁スイッチをarduinoとIRKitとGoogleHomeで動かす。
#しかし、だ。
arduinoで壁スイッチを物理的に押しているわけだが、このarduino、長時間動かすと高確率で止まる。
フロー的には、Google Home→IRKit→赤外線発信→arduinoでサーボモータを動かす
という感じなのだが、IRKitで赤外線を出すところまでは動いているのだが、肝心のarduinoが赤外線を認識してくれない。
大体、半日ほど動かしていると、止まる事が多いようだ。(時間はばらつきがあるようだ。)
原因は複数考えられる、
・プログラム的になにかメモリリーク?的なことを起こしている?
・電力が足りない(サーボモータ2つと別のスイッチコントロール用にリレーを動かしているので、当然足りない。当たり前だ。)
おそらく電力が足りないのが原因なのだが、止まったらリセットボタンを押してやると直るので、
定期的にリセットをかけるようにしたいと思う。
#ウォッチドッグタイマをつかう。
こちらの記事を参考にさせていただいた。(丸パクリ)
Arduinoでソフトウェアからリセットをかける方法
#include <Servo.h>
#include <avr/wdt.h>
Servo servo1;
Servo servo2;
#define IR_PIN 11 // 赤外線受信モジュール接続ピン番号
#define DATA_POINT 3 // 受信したデータから読取る内容のデータ位置
// 初期化処理
void setup()
{
pinMode(7, OUTPUT);
pinMode(2, OUTPUT);
Serial.begin(9600) ; // パソコン(ArduinoIDE)とシリアル通信の準備を行う
pinMode(IR_PIN, INPUT) ; // 赤外線受信モジュールに接続ピンをデジタル入力に設定
software_reset();
}
// メインの処理
void loop()
{
//なにもしましぇーーーーん
}
//リセット用
void software_reset() {
wdt_disable();
wdt_enable(WDTO_8S);
Serial.print("リセットしました");
while (1) {
int ans ;
ans = IRrecive() ; // 赤外線リモコンのデータを受信する
if (ans != 0) Serial.println(ans, HEX) ; // リモコンからデータを受信したら表示する
if (ans != 0) {
switch (ans) {
case 0x84: // スクリーンを下げる
digitalWrite(7, 1);
delay(500);
digitalWrite(7, 0);
break ;
case 0x85: //スクリーンを上げる
digitalWrite(2, 1);
delay(500);
digitalWrite(2, 0);
break ;
case 0x86: //ライトオン
servo1.attach(9);
servo1.write(105);
delay(500);
servo1.write(90);
delay(100);
servo1.detach();
break ;
case 0x87: //ライトオフ
servo1.attach(9);
servo1.write(70);
delay(500);
servo1.write(90);
delay(100);
servo1.detach();
break ;
case 0xE: //調光暗く
//dim down
servo2.attach(5);
servo2.write(180);
delay(500);
servo2.detach();
delay(100);
break ;
case 0x1A: //調光明るく
//dim up
servo2.attach(5);
servo2.write(0);
delay(500);
servo2.detach();
break ;
default:
return 0;
break;
}
}
}
}
// 赤外線リモコンのデータを受信する処理関数
int IRrecive()
{
unsigned long t ;
int i , j ;
int cnt , ans ;
char IRbit[64] ;
ans = 0 ;
t = 0 ;
if (digitalRead(IR_PIN) == LOW) {
// リーダ部のチェックを行う
t = micros() ; // 現在の時刻(us)を得る
while (digitalRead(IR_PIN) == LOW) ; // HIGH(ON)になるまで待つ
t = micros() - t ; // LOW(OFF)の部分をはかる
}
// リーダ部有りなら処理する(3.4ms以上のLOWにて判断する)
if (t >= 3400) {
i = 0 ;
while (digitalRead(IR_PIN) == HIGH) ; // ここまでがリーダ部(ON部分)読み飛ばす
// データ部の読み込み
while (1) {
while (digitalRead(IR_PIN) == LOW) ; // OFF部分は読み飛ばす
t = micros() ;
cnt = 0 ;
while (digitalRead(IR_PIN) == HIGH) { // LOW(OFF)になるまで待つ
delayMicroseconds(10) ;
cnt++ ;
if (cnt >= 1200) break ; // 12ms以上HIGHのままなら中断
}
t = micros() - t ;
if (t >= 10000) break ; // ストップデータ
if (t >= 1000) IRbit[i] = (char)0x31 ; // ON部分が長い
else IRbit[i] = (char)0x30 ; // ON部分が短い
i++ ;
}
// データ有りなら指定位置のデータを取り出す
if (i != 0) {
i = (DATA_POINT - 1) * 8 ;
for (j = 0 ; j < 8 ; j++) {
if (IRbit[i + j] == 0x31) bitSet(ans, j) ;
}
}
}
return ( ans ) ;
}
リセット部分のメインはここみたいだ。
#include <avr/wdt.h>//ライブラリのインクルード
//リセット用
void software_reset() {
wdt_disable();
wdt_enable(WDTO_8S);
Serial.print("リセットしました");
while (1) {}
}
software_reset()という関数を作って、中で、
wdt_disable();
→ウォッチドッグタイマを無効に(初期化処理)
wdt_enable(WDTO_8S);
→ウォッチドッグタイマを有効に。()の中身は時間を指定する。今回は8S(8秒)
Serial.print("リセットしました");
→リセットしたらログを吐く
while (1) {}
→普段はloop関数をメインにつかうが、今回はウォッチドッグタイマをループさせるので、ここがメインループになる。{}の中に好きな処理を書こう!!
#include <avr/wdt.h>
→avr/wdt.hというライブラリをつかうぞ!忘れずにインクルードしよう!
で、あとはこれをvoid setup()の中で呼んであげれば、このウォッチドッグタイマのループがスタートする。
これで、8秒ごとに自分でリセットを掛けて初期化できるようになった!!はず。
しばらく運用して試してみる。