はじめに
前回部屋の明かりをIoT化してWiFiから消したり点けたりできるようになりました。でも消そうと思うたびにスマホを取り出してブラウザで操作しないといけないので、ちょっとスマートじゃないなということで息を吹きかけると消える装置を作ってみました。

つくりたいもの
- 息を検出する
- 検出したらWiFiを介してHTTPのGETリクエストを送る(またはLEDを消す)
必要なもの
- ESP8266モジュール
- または適当なArduino(LEDを消すだけ)
- コンデンサマイク(※アンプなしのもの。)
- 抵抗(2kΩ✕1、200Ω✕2)
- コンデンサ(0.01μF✕1)
- LED✕1
- ブレッドボード、ジャンパワイヤー
↓いつもの安くておすすめなやつです
ブレッドボードを組み立てる
必要なものを揃えたらブレッドボードにさしていきましょう。回路図はこんな感じです。


最低限の部品しか使っていませんがこれで動きます。たぶん詳しい方からすればミスが多いと思いますので、ご遠慮無くご指摘ください。簡単にポイントを説明します。
ポイント① マイク部分

この部分はコンデンサマイクの基本的な回路です。データシートにも書いてあります。抵抗R3とコンデンサC1の値はデータシートとかなり違いますが、これしか手元になかったので使っています。一応問題なく動いているのでOKとします。
コンデンサマイクは極性があるのでプラスとマイナスを間違えると動きません。逆に、コンデンサC1は極性があるとだめなのでセラミックコンデンサを使います。
ポイント② バイアス回路

この部分はバイアス回路です。コンデンサマイクからの信号に一定電圧をプラスして信号を見やすくします。
僕が使っているESP8266モジュールはアナログ入力が0~3.3Vなので、その半分くらいの1.65Vが足されるようにしました。
アナログ入力が0~1Vのesp8266 もある(むしろこっちが一般的?)ので、その場合はバイアスが0.5VくらいになるようにR1とR2の値を決めてください。
ポイント③ LED
動作確認用にLEDを付けています。普通はLEDに電流が流れすぎないように数百Ωの抵抗を入れたほうが絶対いいですが、これでも動くのでテストだけなら抵抗無しでやってます。長時間使うとあんま良くないと思います。
【注意!】esp8266に電源を差し込む前に、回路が間違っていないか確認しましょう。特にesp8266の3.3VとGNDがショートしてるとヤバイです。過大な電流が流れてesp8266を壊します。僕はこれで2つ壊しました。テスター等で必ず確認してください。
ESP8266にプログラムを書き込む
Arduino IDEを使ってesp8266にプログラムを書き込みます。コードの全体像はこちらです。
/* * 吹き消すと火が消えるライト * 息はコンデンサマイクで検知。 */ #define A1 0.9999 // 定常値を求めるLPFの係数。長期的なセンサ値の平均を求める。[] // 検出判定のパラメータ。TIME_DURATION[s]間にCOUNT_MAX回しきい値を超えたら反応する。 #define TIME_DURATION 500 // [ms] #define COUNT_MAX 5 // [] #define V_DIFF 2.0 // 閾値 #define LED_PIN 14 float v = 0; // センサ値 float v_stable = 0; // センサ値の定常値 unsigned long last_detected_time = millis(); // 最後にセンサ値が閾値を超えた時間 int detect_count = 0; // 検出回数 void setup() { Serial.begin(115200); // 最初のanalogReadは値が信用できないので、何回か読む。 for(int i=0; i<50; i++){ v_stable = analogRead(A0); delay(10); } pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, HIGH); } void loop() { v = analogRead(A0); // センサ値 v_stable = A1*v_stable + (1-A1)*v; // センサ値にローパスフィルタをかけたもの。急には変化しない。 // 閾値を超えたらカウントを増やす if( fabs(v - v_stable) > V_DIFF ){ last_detected_time = millis(); detect_count++; } // タイムアウト if( millis() - last_detected_time > TIME_DURATION ) detect_count = 0; // カウントが溜まったらLEDを消灯 if( detect_count > COUNT_MAX ){ digitalWrite(LED_PIN, LOW); delay(1000); digitalWrite(LED_PIN, HIGH); detect_count = 0; } Serial.println(v); delay(10); }
息の検出アルゴリズム
loop()関数内で息の検出をしています。コンデンサマイクはかなり小さい信号しか出すことが出来ないので、通常はアンプ回路で信号を増幅しますが、息くらいの大きい振動ではアンプ無しでも検出できるくらいに信号が変化します。今回のプログラムではこの信号の変化をトリガーにしてアクションを起こします。
息以外の音でも反応してしまいそうですが、意外とそんなことはありませんでした。
書き込み
僕と同じモジュールを使っている方は以下の設定で大丈夫です。

完成!
コンデンサマイクに直接息を吹きかけてみましょう。LEDが消えます。

もし消えなかった方はプログラム中の閾値やMAX_COUNTを減らすなどして調整してみてください。
以上です。