Arduinoで冷凍庫半開き検出ブザーを作ってみた 前編

# はじめに

事の発端は、子供が冷凍庫のドアをちゃんと閉めずに一晩放置された結果、冷凍庫の中身がだめになってしまったことです。
冷蔵庫だと半開きセンサーがついているのが普通ですが、冷凍専用だと結構ついていないようです。
市販でいいものがないか探したのですが、見つけられなかったので自作することにしました。

といっても、電子工作は初めてなので、いろいろと大変でした。

最初は汎用ICの組み合わせで作ろうとしましたが、うまくいかなかったのでarduinoでドアの半開きとブザーの制御を行うようにしました。
loopではなく、割り込み駆動で実装してみました。

## 使ったもの

* Arduino Uno(for development)/Arduino Nano(for production)
* ブレッドボード
* ユニバーサル基板
* リードスイッチ
* 電子ブザー
* 抵抗、コンデンサ、LED、すずメッキ線
* 半田ごて

## 汎用ICを使った回路

最初に参考にしたのは [Refrigerator Alarm DIY](http://chococakegateauchocolat.web.fc2.com/RefrigeratorAlarm/RefrigeratorAlarm.html) です。ほぼそのまま回路構成を真似て作ってみましたが、机上で配線したときに、基板の裏表を間違えた状態で部品を配置してしまったために動かないものができあがってしまいました・・・

## Arduinoを使ってみた

そこで、手元に仕事で使えないかと買ってそのまま眠りについていたArduinoを使ってみることにしました。
スイッチの状態検出と電子ブザーを鳴らすくらいなら、sampleに入っているコードの組み合わせですぐに実現できました。

* スケッチの例 → 02.Digital → BlinkWithoutDelay (Lチカ)
* スケッチの例 → 02.Digital → Button (入力検出)

回路は以下のような感じになります。
リードスイッチが閉じている間はLED, ブザーが鳴らないようにし、リードスイッチが開くとLEDが点灯し、リードスイッチが開いてから60秒経過すると、ブザーが鳴るようにしています。
リードスイッチが再度閉じると、LED, ブザーが止まるようにしました。

![freezer_alarm_dev_ブレッドボード.jpg](https://qiita-image-store.s3.amazonaws.com/0/35314/365e0f14-5569-10ed-3ad7-9897fffd0e3b.jpeg)
![DSC_3180.JPG](https://qiita-image-store.s3.amazonaws.com/0/35314/7014dd1e-22df-d5a1-580d-7d902bc6c3b4.jpeg)

スイッチのところにコンデンサを入れていますが、チャタリング対策です。後で触れます。

### 作成したコード

コードは以下のようにしました。
まずはsetup()とloop()です。

```c
/* pin definition */
const unsigned int SWITCH_PIN = 2;
const unsigned int BUZZER_PIN = 12;
const unsigned int LED_PIN = 13;

/* switch open duration */
const unsigned int SWITCH_OPEN_TIMEOUT_MSEC = 60 * 1000;
const unsigned int BUZZER_ENABLE_INTERVAL_MSEC = 500;
const unsigned int BUZZER_CLOSED_DURATION_MSEC = 100;

/* function definitions */
void handleSwitchStateChanged();

void setup() {
  Serial.begin(115200);

  pinMode(SWITCH_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);  
  pinMode(LED_PIN, OUTPUT);  

  /* enable interrupt for external switch */
  attachInterrupt(digitalPinToInterrupt(SWITCH_PIN), handleSwitchStateChanged, CHANGE);
}

void loop() {
  /* do nothing in loop */
}
```

割り込み駆動で動かすようにしたので、loopの中では何もしていません。
attachInterrupt()でリードスイッチが接続されたピンの状態に応じて、handleSwitchStateChanged()という割り込みハンドラが呼ばれるようにしています。

割り込みハンドラは以下のようにしました。

```c
bool isOpenedSwitch();

void enableLed();
void disableLed();
bool isEnableBuzzer();
void enableBuzzer();
void disableBuzzer();
void handleBuzzerTimer();
void handleSwitchClosedBuzzerTimer();

void enableTimer(const unsigned long duration, void (*func)());
void disableTimer();

void handleSwitchStateChanged()
{
  if (isOpenedSwitch()) {
    disableBuzzer();
    disableTimer();
    enableTimer(SWITCH_OPEN_TIMEOUT_MSEC, handleBuzzerTimer);
    enableLed();
  } else {
    disableTimer();
    if (!enabled_buzzer_timer) {
      enableBuzzer();
      enableTimer(BUZZER_CLOSED_DURATION_MSEC, handleSwitchClosedBuzzerTimer);
    } else {
      disableBuzzer();
    }
    enabled_buzzer_timer = false;
    disableLed();
  }
}

void handleBuzzerTimer()
{
  enabled_buzzer_timer = true;
  disableTimer();
  enableTimer(BUZZER_ENABLE_INTERVAL_MSEC, handleBuzzerTimer);

  if (isEnableBuzzer()) {
    disableBuzzer();
  } else {
    enableBuzzer();
  }
}

void handleSwitchClosedBuzzerTimer()
{
  enabled_buzzer_timer = false;
  disableBuzzer();
  disableTimer();
}
```

handleSwitchStateChanged()でリードスイッチのOn/Offを検出しています。
Onのときはブザー動作開始タイマーを発動し、Offのときはタイマーをとめるようにしています。
Offのときに一瞬ブザーを鳴らし、ドアが閉じたことがわかるようにしています。([Refrigerator Alarm DIY](http://chococakegateauchocolat.web.fc2.com/RefrigeratorAlarm/RefrigeratorAlarm.html)の動作と同じです)
最初はコンデンサを入れていなかったのですが、リードスイッチを閉じたときに、offの割り込みが2回入っていたので、[チャタリング](https://ja.wikipedia.org/wiki/%E3%83%81%E3%83%A3%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0)が起きていると思い、チャタリング防止回路として抵抗とコンデンサをリードスイッチの間に追加しました。
これで、offの割り込みが2回から1回になり、期待する動作となりました。

リードスイッチのOnからブザーの動作開始まではタイマー割り込みを使いました。[FlexiTimer2](http://playground.arduino.cc/Main/FlexiTimer2)を使いましたが、本当はOneshot timerで十分でした。Oneshot timerライブラリを探してみましたが見つけられなかったので、タイマー割り込みの中でタイマー設定をやり直しています。
handleBuzzerTimer()がタイマー割り込みのハンドラとなっています。
ブザーを500msec周期でOn/Offを繰り返すようにしています。

handleSwitchClosedBuzzerTimer()は、リードスイッチがOffになったときに、「ピッ」と鳴らすためのタイマーハンドラで、こちらはOneshotとして動作します。

以下は、arduinoのAPIのラッパです。
直接APIを叩いていないのは、趣味の問題です。

```c
bool isOpenedSwitch()
{
  return (digitalRead(SWITCH_PIN) == HIGH);
}

void enableLed()
{
  digitalWrite(LED_PIN, HIGH);
}

void disableLed()
{
  digitalWrite(LED_PIN, LOW);
}

bool isEnableBuzzer()
{
  return (digitalRead(BUZZER_PIN) == HIGH);
}

void enableBuzzer()
{
  digitalWrite(BUZZER_PIN, HIGH);
}

void disableBuzzer()
{
  digitalWrite(BUZZER_PIN, LOW);
}

void enableTimer(const unsigned long duration, void (*func)())
{
  FlexiTimer2::set(duration, func);
  FlexiTimer2::start();
}

void disableTimer()
{
  FlexiTimer2::stop();
}
```

こちらのコードはgithubで公開しています。(記事もgithubも初めて)

https://github.com/isop/freezer-door-open-detector-arduino

## 終わりに

今回はArduino Unoとブレッドボードで原理確認を行いましたが、このままでは実際に冷凍庫につけられないので、Arduino Nanoを使い、ケースに入れて駆動させてみます。
まだArduino Nanoが届いていないので、届き次第作成をする予定です。
あと、今回作成したコードは割り込み駆動で動くようにしているので、低消費モード対応も入れようと思います。

後半へ続く。