HC-SR501を使用したWindows用の簡易版人感センサーを3回に分けて制作します。
私の場合は、このセンサーを以前制作したRICOH Thetaを使用した監視カメラの起動シャッターに応用しようと思います。
赤外線よる動体検知なので、人感だけでなく、できれば機器類の動作にも反応してくれないかと期待して作ってみることにしました。
■ 動作原理
仕組みとしては単純で、下記のような流れでWindowsへ通知します
ポイントは、検知した情報をキーボードのキーコードとして通知する点です。
このような仕組みにすると、PC上でデバドラを作成する必要もない上でに、人感センサーの検知情報をOS上のイベント(キーコード)に変換してくれます。
Arduino Pro Microは、HIDキーボード、マウス機器として振る舞うことが可能ですので、この機能を利用させてもらいました。
モーション検知キーボードとにしておけば、ほかにも応用が利きそうでしたので、このような形態にしました。
① HC-SR501で人の動きを感知
↓ 通知
② Arduino Pro Micro(MCU:Atmega 32U4)
↓ ショートカットキー変換
③ Windowsがキーを検知(※補足)
↓ 撮影アプリ起動、撮影指示
④ 撮影アプリが周囲を撮影
↓ しばらく監視
⑤ 撮影後、しばらく異常がなければ撮影を停止、撮影アプリを終了します
また、キーボードとして動作させる以外にも、単に検知通知をシリアルポート経由で通知することも可能にしきました。単なるセンサーとしてアプリへの組み込みも可能にしておくことで、キーボード動作で対処できない環境でも使用できるようにするためです。
※ 補足 Windows 10のショートカットキー登録手順
Windows10では、デスクトップ上に置いたショートカットに対して、ショートカットキーが登録可能です。人感センサーは、この機能を使って撮影アプリの起動、撮影指示を行います。
ショートカットキーの設定手順手順を以下に示します。
a. デスクトップにショートカットを作成
b. a.のショートカットの上でマウス右ボタンを押し、プロパティを選択します
c. 下記のプロパティ画面が表示されるので、赤丸部を選択し、ショートカットキーに指定するキーを押して設定します
なお、ショートカットを削除するとショートカットキーも同時に解除されるようです。
■ センサー、MCU間の配線図
登場する部品は、HC-SR501とArduino Pro Microの2つだけで、以下のように接続します。
あと、HC-SR501の設定、調整は、こちらを参考にして行いました。
今回は、Jumper Set=Single Triggerに設定して使用します。
配線色 | Arduino Pro Micro | HC-SR501 |
赤 | VCC | 右側 |
黄 | 2 | 中央 |
黒 | GND | 左側 |
■ ケース設計
上記で配線はできたので、モデリングを行ってケースを設計しました。
ブレッドボードでの運用は、故障リスクが高いので避けたいですが、基板設計する程でもありません。
今回は、けちって、基板なしで組み立て可能なケースを考えました。
外観は、下記のような感じにしました。
面白い形状ではないですが、四角くシンプルなので縦でも横でも運用できるので、これに決定しました。
■ ソフト制御(抜粋)
ここでは、制御の主要な部分だけ抜粋して説明します。
ソース等は公開するつもりなので、理解しなくても使用可能だと思いますが、改造等する際の手がかりになればと思い記載しました。
まずは、人感センサーの検知部分。
下記のモジュールは、ArduinoのIF:setup()、loop()から呼び出すことを前提に設計しています。
仕組みとしては、キーボードとして使用することもあり、頻繁キー入力は避けたいので500ms単位で監視するようにしています。
また、検知した瞬間(HIGHになったタイミング)でキーの押し下げ、検知できなくなった瞬間(LOWになったタイミング)でキーの解放と同等のことが行えるように状態を作っています。
関数名 | 処理内容 |
motion_initialize() | ・人感センサー読み取り用PIN(2)を入力方向にセットします ・センサーの検知状況:s_status_sensorをLOWに設定 ・読み取りタイミング:s_tm_senseに現在時間を設定 |
motion_sense() | ・人感センサーが何かを検知した瞬間に、1を返す ・時間センサーが検知できなくなった瞬間に、-1を返す ・検知したまま、未検知のままは、0を返す ・500ms毎に検知を行う。 ・長期間の動作もあり得るので、タイマーが0msに戻った場合の制御もいれてます |
#define CNF_INTERVAL 500 /* is the interval time to sense. */
/*
* here's the configuration of this file.
*
* s_tm_sense ... is the time to sense.
* s_status_sensor ... is the status of sensor(HIGH or LOW).
*/
static unsigned long s_tm_sense;
static int s_status_sensor;
/*
* purpose : entry to initialize motion.
*/
void
motion_initialize(void)
{
pinMode(PIN_MOTION, INPUT);
s_status_sensor = LOW;
s_tm_sense = millis();
}
/*
* purpose : entry to sense motion.
* returns : 1 ... the sensor detects motion.
* : -1 ... the sensor lost motion.
* : 0 ... status isn't changed.
*/
int
motion_sense(void)
{
unsigned long tm;
tm = millis();
if (tm >= s_tm_sense) {
if (tm - s_tm_sense < 0x10000L) { /* 0に戻った時は差分が大きいので、その間は無視 */
s_tm_sense = tm + CNF_INTERVAL; /* 次の起動時間をセットする */
if (digitalRead(PIN_MOTION) == HIGH) {
if (s_status_sensor == LOW) { /* LOW->HIGHに変化した瞬間に1を返す +/
s_status_sensor = HIGH; /* 現在のセンサー状態をHIGHにセット */
return(1); /* this detects something. */
}
} else {
if (s_status_sensor == HIGH) { /* HIGH->LOWに変換した瞬間に-1を返す */
s_status_sensor = LOW; /* 現在のセンサー状態をLOWにセット */
return(-1);
}
}
}
}
return(0);
}
次に、キーボード処理を示します。
こちらも、ArduinoのIF:setup()、loop()から呼び出すことを前提に設計しています。
設定されたキー動作に従って、キーストロークを生成します。
今回は、ショートカットキーでしか使用しませんが、アプリケーション制御用にテキスト出力を行ったり、キーのシミュレーションも行えるように設計しています。
関数名 | 処理内容 |
key_initialize() | HIDキーボードを開始します。(PCはここでHIDキーボードと認識します) |
key_press() | ・人感センサーが何かを検知した際に呼び出され、キーの押し下げ処理を行います ・検知時に出力するキーの種類毎にキー押し下げ処理を行います ・ショートカット、テキスト(2文字)、キーの押し下げの3種類の動作を行います ・ショートカット:CTRL+ALT+キーを出力 ・テキスト:文字、ファンクションキーを出力 ・キー押し下げ:キーのストロークを再現し、key_release()が呼ばれるまでキーが下げられた状態にします |
key_release() | ・人感センサーが検知できなくなった瞬間に呼び出され、キーの上げ処理を行います |
/* * purpose : entry to initialize key. */ void key_initialize(void) { /* * this initialize keyboard HID. */ Keyboard.begin(); } /* * purpose : entry to press key. */ void key_press(keycnv_t *key) { switch (key->type) { case eXT_SHORTCUT: /* CTL+ALT+xxxのような ショートカット動作時の処理 */ if (key->code[1] != 0) { if ((key->code[0] & eSCK_CTRL) != 0) { Keyboard.press(KEY_LEFT_CTRL); /* コントロールキーの押し下げ処理 */ } if ((key->code[0] & eSUK_SHIFT) != 0) { Keyboard.press(KEY_LEFT_SHIFT); /* シフトキーの押し下げ処理 */ } if ((key->code[0] & eSUK_ALT) != 0) { Keyboard.press(KEY_LEFT_ALT); /* ALTキーの押し下げ処理 */ } if ((key->code[0] & eSUK_GUI) != 0) { Keyboard.press(KEY_LEFT_GUI); /* GUIキーの押し下げ処理 */ } Keyboard.press((int)key->code[1]); /* ショートカットキーのキー部分の押し下げ処理*/ Keyboard.releaseAll(); /* すべてのキーをリリース(離す) */ } break; case eXT_TEXT: /* アプリケーションのショートカット動作時の処理 */ if (key->code[0] != 0) { Keyboard.press((int)key->code[0]); /* 2ストロークの最初のキーを押す */ Keyboard.release((int)key->code[0]); /* キーを離す */ if (key->code[1] != 0) { Keyboard.press((int)key->code[1]); /* 2ストローク目を押す */ Keyboard.release((int)key->code[1]); /* 2ストローク目を離す */ } } break; case eXT_KEY: /* 特定キーのシミュレーション */ if (key->code[0] != 0) { Keyboard.press((int)key->code[0]); /* キーシミュレーションなので、押しっぱなし */ } /* キーを離すタイミングは、key_release() */ break; case eXT_SERIAL: /* シリアル接続センサーとしての動作 */ /* * this reports the key down to catch key event via serial port. */ sprintf(no, "0%02d", keyno); /* 感知したことをPCに通知する / Serial.println(no); Serial.flush(); break; default: break; } } /* * purpose : entry to release key. */ void key_release(keycnv_t *key) { switch (key->type) { case eXT_SHORTCUT: /* CTL+ALT+xxxのような ショートカット動作 */ case eXT_TEXT: /* アプリケーションのショートカット動作 */ break; case eXT_KEY: /* 特定キーのシミュレーション */ if (key->code[0] != 0) { Keyboard.release((int)key->code[0]); /* キーシミュレーションなので、キーを離す*/ } break; case eXT_SERIAL: /* シリアル接続センサーとしての動作 */ /* * this reports the key up to catch key event via serial port. */ sprintf(no, "1%02d", keyno); /* 検知できなくなったことをPCに通知する */ Serial.println(no); Serial.flush(); break; default: break; } }
■ 次回
次回は、簡易人感センサーの組付けを行います。
またよかったらのぞいていただけると嬉しいです。