LoginSignup
8
12

More than 5 years have passed since last update.

arduinoを長時間動かすと止まるので定期的にリセットをかけるようにした。

Last updated at Posted at 2017-11-19

こちらの記事に書いたが、現在家ではIRKitとArduinoを使って、(すごく物理的な)ホームオートメーションを実現している。
Google Home とIRKitとArduinoでホームオートメーション
↓Youtubeです
スマートでない壁スイッチをarduinoとIRKitとGoogleHomeで動かす。
スマートでない壁スイッチを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秒ごとに自分でリセットを掛けて初期化できるようになった!!はず。

しばらく運用して試してみる。

8
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12