サボるとキレる機械作った


勉強してるとついサボっちゃうよね

家で作業や勉強してると誘惑が沢山ありますね。
一人暮らしの私の場合はついついギター弾きたくなったりベッドに潜りたくなったり・・・
昔勉強サボるとオカンに叱られたっけ。
全く、自分一人じゃこんなもんかよ・・・

というわけでサボり対策の為にサボると警告してくれるガジェットを作ってみました。

実現したいこと

デスクから離れている状態をサボりとします。
常時私がデスクに居るかどうか監視し、1分間デスクから居なくなったらアラートが作動するシステムを作ります。
サボり抑制システムなので、なるべく鬱陶しいアラートを実装したいですね。

早速作ってみよう

配線図がへたくそ過ぎて泣きそうですが・・・
こんな感じのハードにします。
今回も制御用マイコンはArduinoで行きます。
Arduinoであればnanoでなくても何でもOKです。

使ったもの

デスクから離れるとダ〇ボーがブンブン回ります。

今回は人感センサで人が居るのか居ないのかをチェックし、人が居ない状態が1分続いたらダ〇ボーを載せたサーボモータをぶんまわします。
みゅいんみゅいん動作音もうるさいし、なんか動いてるし鬱陶しいったらありゃしません。すぐに止めに行かねば。
言葉でこの鬱陶しさを伝えきれないので、gifをご覧ください。

1分間デスクから離れるとデスクからみゅいんみゅいん聞こえる・・・
結構うるさい。
机に戻るとピタッと止まる。
結構賢い。

gifでは分かりにくいので手をかざしていますが、デスクに戻るとちゃんと反応して止まってくれます。

というわけでどうやって動かしたのか、肝心のプログラムを紹介します。

では行こう。

どうやって動かしてんだよ

まずはソースコードの全容をご覧ください。

okoru.ino
#include <Servo.h>
#define SENSOR 5
#define IRU 1
#define INAI 0
#define LED 2

Servo waiservo;

unsigned char human = IRU;
unsigned char prev_state;

void anger(void);

void setup()
{
    pinMode(LED, OUTPUT);
    pinMode(SENSOR, INPUT);
    Serial.begin(9600);
    waiservo.attach(3);
}

void loop()
{
    human = digitalRead(SENSOR);
    if ((human == INAI && prev_state == IRU))
    {
        for (int i = 0; i <= 60; i++)
        {
            delay(1000);
            Serial.println(i);
            if (digitalRead(SENSOR) == IRU)
            {
                break;
            }
            if (i == 60)
            {
                anger();
                i = 0;
                break;
            }
        }
    }
    delay(500);
    prev_state = human;
}

void anger(void)
{
    for (;;)
    {
        human = digitalRead(SENSOR);
        waiservo.write(0);
        digitalWrite(LED, HIGH);
        delay(300);
        waiservo.write(90);
        digitalWrite(LED, LOW);
        delay(300);
        waiservo.write(180);
        digitalWrite(LED, HIGH);
        delay(300);
        waiservo.write(90);
        digitalWrite(LED, LOW);
        delay(300);
        if (human == IRU)
        {
            digitalWrite(LED, HIGH);
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
            digitalWrite(LED, HIGH);
            delay(100);
            digitalWrite(LED, LOW);
            break;
        }
    }
}

人感センサについて

人感センサで人が居る、居ないを判断するわけですが、ざっくりどんなものかと言いますと、赤外線で熱量の変化をみてるんですね。
なので動物や人間がセンサの検知範囲内に入った時だけ反応するという点が特徴です。
プログラムでの取り扱いは非常に簡単で、人を検知したら3Vを出力するだけなので、プログラム側では入力があったか無かったか見てあげればOKです。
今回はこいつをスイッチとして利用します。

但し、人を検知した場合、検知した状態(出力状態)を一定時間保持するという仕様となっているので、
「人が居なくなってから1分経ったらサーボモータを動かす」という動作を実装する場合、人が居なくなった瞬間がどこなのか、はっきりさせなければいけません。

edge

void loop()
{
    human = digitalRead(SENSOR);
    if ((human == INAI && prev_state == IRU))
    {
        for (int i = 0; i <= 60; i++)
        {
            delay(1000);
            Serial.println(i);
            if (digitalRead(SENSOR) == IRU)
            {
                break;
            }
            if (i == 60)
            {
                anger();
                i = 0;
                break;
            }
        }
    }
    delay(500);
    prev_state = human;
}

loop関数の中の処理は
人感センサの値がINAIに変わったら、
for文で1分待機モードに遷移し、1分経ったらサーボモータを起動
待機中にセンサが反応したら何もせず監視モードに戻るという処理です。
ここでポイントになるのはif ((human == INAI && prev_state == IRU))
の条件文です。

human変数にはセンサの現在の状態が入っています。
人が居る場合常にIRUが入ってきます。
loop関数の最後、prev_state変数にはhumanの値を代入しています。

これにより、loop関数の頭に戻った時にhuman変数の値がINAIになった(人が居なくなった)場合、次のif ((human == INAI && prev_state == IRU))で比較しているそれぞれの変数の中身はhuman = INAI prev_state = IRUとなり、人が居なくなった瞬間を判断したことになります。

このように信号の立ち上がりや立下りを検出するテクニックをエッジを取るなんて呼んだりします。(なんかカックイイです)
普通の物理的なスイッチなんかの処理にもよく使います。(スイッチ離したらそれをフックになんか処理するとか。)

というわけで今回は信号の立ち下りを取ることで人が居なくなったタイミングを判断できたわけですね。

サーボモータについて

Servo
void anger(void)
{
    for (;;)
    {
        human = digitalRead(SENSOR);
        waiservo.write(0);
        digitalWrite(LED, HIGH);
        delay(300);
        waiservo.write(90);
        digitalWrite(LED, LOW);
        delay(300);
        waiservo.write(180);
        digitalWrite(LED, HIGH);
        delay(300);
        waiservo.write(90);
        digitalWrite(LED, LOW);
        delay(300);
        if (human == IRU)
        {
            digitalWrite(LED, HIGH);
            delay(100);
            digitalWrite(LED, LOW);
            delay(100);
            digitalWrite(LED, HIGH);
            delay(100);
            digitalWrite(LED, LOW);
            break;
        }
    }
}

こちらがサーボモータを動かす関数です。
そうです、ライブラリ頼みです。
なんならついでにLEDピカピカさせてアラート感出してみています。

プログラムの冒頭で
Servo.hをインクルードし、
Servo waiservoと名前をつけて
waiservo.write(90)引数に角度を整数で指定すれば、その角度にサーボモータが動いてくれるという簡単な仕組みです。
今回は0~180度までビュンビュン回しています。
間にdelay()を挟んでモータが次の角度に動く迄待ってあげるという流れです。

終わりに

まだ複雑なものはなかなか作れませんが、
こういった単純な工作ならパパっと設計して作り切れるようになってきました。
楽しい。
今回のネタを応用して、サボった回数をスプレッドシートかなんかでログを取って、一定回数サボったら罰を与える鬼畜システムでも作ってみたいですね。

とりあえず一度もダ〇ボー暴れさせることなく本記事を最後まで書けたので効果はテキメンということにしておきます。