ポークピカタ

強く生きる

一人暮らしでぶっ倒れるのが怖いから監視システムをつくった

⓪はじめに

お久しぶりですなるさわです!

今年の4月頭に、倒れました。

 

 

なんか、28時間ぐらいぶっ倒れていました。

詳細は省きますが、普通にストレス(あったっけ?)とか睡眠不足(このころ深夜4時までマイクラしてた)とかが原因らしいです。

 

一日一食が続いていることを医者に言ったら怒られました。

 

ぼく、一人暮らししてます。

 

以前にもnoteってところで書いたことがあるんですが、

一人暮らししてる人って病に倒れてしまった場合、全然気づかれない可能性があって怖くないですか!?

 

特に僕なんかはフルリモートなので、金曜日の日中なんかに倒れてそのまま逝ってしまったららもう大変。

月曜日まで誰も気づかないまま…なんてことも(なんなら会社も結構勤務確認がユルユルなのでそれ以上も)あり得るわけです。

 

倒れた日から、Twitter、インスタ、discordなどのインターネットに顔を出さない日があると

(少数の)インターネットフレンドたちが心配してくれるようになりました。うれしいですね。

うれしいんですが、単純に仕事が忙しすぎてスマホなんて触ってられない日まで心配させてしまっている。

これは申し訳ないですし、単純に連絡返すのがダルい日もあります

というかダルい日がほとんどです。すまん。

 

ま、そういうことで、

自動で周りに生存(or死亡)をお知らせできる仕組みを作りたいな~」

と思い立ったのが4~5月のことです。

 

①生存お知らせシステムver.β

そういうことで

まずお手軽にiPhoneのオートメーションを使って生存お知らせシステムが作れそうだったので作ってみました。

 

朝、始業5分前にアラームで起きてるので、

アラームを停止したときに↑の画像のやつが動くようにしました。

 

これで「あ~起きたんだな」とdiscordにいらっしゃるインターネットフレンドの方々に自動的にお知らせすることができます。

 

名前を「生存お知らせシステムver.β」とでもしましょう

お手軽な分問題点があります。

 

問題点(1)うるさい

なんせ毎朝動くので、こういうことになります。

  

うるせ~~~の。(15:00のやつはまた別で動いてるやつです)

 

discordに専用のチャンネルを作ってるのでミュートしていただければいいのですが、

それでも毎日1~2回通知が出るのは美しくないです。

 

問題点(2)アラーム止め忘れに対応できない

当たり前ですが、「アラームを止める」ことがトリガーなので、

止め忘れたら通知が出ません(会社にも遅刻です)。

 

「生存お知らせシステムver.β」運用開始から今まで止め忘れたことはないのですが、

アラーム止め忘れ自体は今までもあったので

安定してこのシステムが稼働できるとは言い切れません。

 

大きくこの二つが問題点として挙がると思います。

その他、「iPhoneの電源が落ちてたら終わり」「アラームの設定時間を変えたらオートメーションの設定もいじらないといけない」「土日の生存確認がガバガバ」等の細かい問題点もあります。が、

「まあ一番安上がりで済んでるし何かきっかけがあるまでこれでいいか…」と思っていました。

 

Raspberry Pi 4 との出会い

先日、用事があったので都内に出てついでに秋葉原に行きました。

「こっち引っ越してきてから行ったことなかったな」程度の気持ちで。

 

歩いてて、千石電商の前を通ったところ、

なんとRaspberry Pi 4が売ってるらしい

(「らずばりーぱい?」となった人は「ちーちゃいパソコン」と思っておいてください)

www.raspberrypi.com

 

正直Amazonでも買えるのですが、定価の倍ぐらいしますマジで。

半導体不足の影響かな?(「半導体って不足するんだね」と未だに思ってます)

 

そのRaspberry Pi 4(以下「ラズパイ」)が定価+2000円ぐらいで売ってるじゃないですか!!!(10000円ぐらい)

 

なんかその、その時は生存確認システムとかすっかり頭から抜け落ちていたんですけど、

「あ、買わなきゃ」と思って買ってました。

同行していた人も「なに?」って感じだったと思います。

 

買った直後、

「買ったはいいものの…何しよう」

ってなりました。おバカなので。そこでやっと思い出します。

「生存確認システムをどうにかしないといけない」ことを――。

 

ここから怒涛の買い出しが始まります。

 

まず何をするにも必要(だと思われる)なものを購入しました。

・電源コード

・SDカード(64GB)

・半田ごてその他一式(結論から言うと要りませんでした)

 

まず、以下の改善案の要件を満たしたいな~と思って買い出しを進めます。

 

生存確認システム改善案①

リードスイッチってのを使ってドアの開閉をしたときに記録する

・最後のドアの開閉から24時間開閉が無かったらあらゆる場所に通知をする通知をする

 

そういうことでドア開閉を感知するための

リードスイッチってのを探します。が。

 

ない。マジでどこにもない。

 

秋葉原中の電子工作屋さん(何ていうの?パーツ屋?)をめぐりましたが、

見事にドア開閉をどうこうしてくれるやつだけない。

 

こりゃ困りました。

 

僕は、こういう勢いで始めた作業がちょっとした要因で中断してしまうと

突然やる気がなくなってしまうんです。

 

別の案を考えよう。

 

そう思いながら歩いていたら超音波センサーが。

こりゃいいかも。

 

生存確認システム改善案②

超音波センサーで常時距離を測定する

・あからさまに人が通った感じがあったらいろんな場所に通知を飛ばす

 

といった感じでどうでしょう。

 

そういうことで、

秋葉原で以下のものを購入。

 

【買ったもの】


・ラズパイ
・SDカード64GB
・ブレッドボード
・ジャンパ線
・はんだ一式
・超音波センサーのなんかのキット

※このあとmicroHDMI買い忘れていることに気づきます

 

さ、いろいろやるか!

 

③赤外線センサー式へ

帰宅しました。夜もだいぶ遅いです。

 

さて、どうやってラズパイと超音波センサーをどうこうするんだろうな。

調べたところ、すげ~めんどくさそう(抵抗とかめちゃめちゃつけなきゃいけない)だし、

 

冷静に考えて距離が何cmになったら「生きてる」になるんだい?

倒れた時に窓を開けていて

物が動いたらすべて生きている判定になったらそれはそれでマズい。

(今考えるとなんとかできそうではある)

 

「めんどくさ~い」って思いながら

「ラズパイ 人感センサー」とかでググったらいいものがありました。

 

赤外線センサーです。

 

①ラズパイとの接続が簡単

②赤外線を感知しないといけないので、熱を発しない単なる物体には反応しない

③「動いた」「動いてない」の0か1かなので組むのが簡単そう

 

上記の理由で

「超音波やーめた」ってなりました。

 

赤外線センサーで人の有無を感知したいと思います。

ということで以下の通り。

 

生存確認システム改善案③

・赤外線センサーを使って廊下通過時に記録する

・最後の通過から24時間反応が無かったらあらゆる場所に通知をする

 

そうと決まれば赤外線センサーをAmazonで注文です。

(ついでにmicroHDMIも)

 

こうして1日目終了。

次の日からバリバリ構築していきます。

 

④はじめてのプログラミング

Amazonのインターホンで起きた日曜日

 

microHDMI赤外線センサー(HC-SR501)が到着です。

※ジャンパ線もオスメス違いを買いました

 

ラズパイの初期セットアップも終わりました。

 

さ。

プログラミングのお時間です。

 

えっとですね、一応IT系のベンチャーに勤めているのですが、

なんせ僕はネットワークエンジニアですので

プログラミングなんかさっぱりなわけです。

 

最近仕事でちょいちょいプログラミング言語に触ってはいるのですが、

コピペやテンプレのちょっとした改変ばかりで、

一から自分から組んだことなどございません。

 

勉強しながらやるか~~~~~~~!!!!!

 

まずこういうのは

「どういう動きをさせたいか」を

きちんと書いてから作業始めないといけないってみんな言ってた気がするな。

 

書きました。

どうですか。本業の方。

卒倒もんでしょ。フローチャート?知らんなあ


↑の画像の①と②を同時に処理したいな~と思って書き出しました。

※結果としてほとんどこの通りに仕上がってます

 

勉強とかいろいろ端折りますが、

なんかできました。(10時間かかりました)

以下コード。

import time, threading ,json ,requests ,tweepy
from gpiozero import MotionSensor

#生死ステータス管理、カウントダウンの全ての処理を1ファイルで行う

#ネットワーク接続待機(一応)
time.sleep(15)

#クラス変数の設定
class g:
    death = 0
    counter = 1440
    CONSUMER_KEY = 'xxxxxxxxxx'
    CONSUMER_SECRET = 'xxxxxxxxxx'
    ACCESS_TOKEN = 'xxxxx-xxxxxxxxxx'
    ACCESS_TOKEN_SECRET = 'xxxxxxxxx'

#カウントダウンのクラス
class counting(threading.Thread):
    def __init__(self):
        super(counting, self).__init__()
    
    def run(self):
        #webhookURLと送信文面の指定
        webhook_url_death  = 'https://discord.com/api/webhooks/xxxxxxxxxx'
        main_content_death = {'content': '24時間家にいないか、死んでるかもしれません'}
        headers_death      = {'Content-Type': 'application/json'}
        #counterループ
        while True:
            #counterが0以下の場合
            if g.counter <= 0:
               #counterが0以下かつ生存ステータスだった場合
                if g.death == 0:
                    #webhookの投稿、死亡ステータスへ、60秒待機後、ループ
                    response = requests.post(webhook_url_death, json.dumps(main_content_death), headers=headers_death)
                    g.death = 1
                    print('Status updated Alive -> Death')
                    #死んでる旨をツイート
                    auth = tweepy.OAuthHandler(g.CONSUMER_KEY, g.CONSUMER_SECRET)
                    auth.set_access_token(g.ACCESS_TOKEN, g.ACCESS_TOKEN_SECRET)
                    api = tweepy.API(auth)
                    api.update_status("[自動]24時間家にいないか、死んでるかもしれません")
                    time.sleep(60)
                    continue
                #counterが0以下だがすでに死亡ステータスの場合
                else:
                    #60秒待機後、ループ
                    time.sleep(60)
                    continue
            #counterが0より大きい場合
            else:
                #カウンター減算、60秒待機後ループ
                g.counter = g.counter - 1
                #カウントダウンを表示
                print(g.counter)
                time.sleep(60)
                continue
        else:
            #通常表示されないはずのメッセージ 
            print('Counter loop stopped!')

#赤外線感知クラス
class sensing(threading.Thread):
    def __init__(self):
        super(sensing, self).__init__()

    def run(self):
        def detectMotion():
            pir = MotionSensor(18)
            pir.wait_for_motion()
            #感知時カウンターを戻す
            g.counter = 1440
            #感知したことを表示
            print('motion detect!')
            #webhookURLと送信文面の指定
            webhook_url_alive  = 'https://discord.com/api/webhooks/xxxxxxxxxx'
            main_content_alive = {'content': '生存が確認されました'}
            headers_alive      = {'Content-Type': 'application/json'}

            #生存ステータスの場合
            if g.death == 0:
                pass
            #死亡ステータスから生存ステータスへ更新された場合
            else:
                #webhookに投稿、生存ステータスへ
                response = requests.post(webhook_url_alive, json.dumps(main_content_alive), headers=headers_alive)
                g.death = 0
                print('Status updated Death -> Alive')
            time.sleep(0.2)
    
        while True:
            detectMotion()

#counterをリセット
g.counter = 1440

#webhookURLと送信文面の指定
webhook_url_reboot  = 'https://discord.com/api/webhooks/xxxxxxxxxx'
main_content_reboot = {'content': '再起動しました'}
headers_reboot      = {'Content-Type': 'application/json'}

#カウントダウン表示
print(g.counter)

#webhookに投稿
response = requests.post(webhook_url_reboot, json.dumps(main_content_reboot), headers=headers_reboot)

#起動処理待機のため5秒待つ
time.sleep(5)

#threadingにより2つのクラスを並列処理
if __name__ == '__main__':
    t1 = counting();
    t2 = sensing();

    t1.start()
    time.sleep(5)
    t2.start()

【補足】
・GPIOは18使ってます

・tweepyのインストールが必要です(ラズパイ以外だったらrequestsも)

 

やってること(別に見なくていい)

①起動

②ネットワーク接続待機(15秒。後々マジで大事になる)

③下記の操作を2ついっぺんに処理するので

 それぞれで共有する変数(生死ステータス、カウントダウン)の設定

④(1)カウントダウン

 ☆

 └・カウントダウンが0以下のとき

   └・生存ステータスのとき

     └死亡ステータスにして、discordとtwitterに投稿、1分待機して☆に戻る

   └・死亡ステータスだったとき

     └何もせず1分待機して☆に戻る

 └・カウントダウンが0より大きいとき

  └カウントダウンを1減らして、1分待機して☆に戻る

 (2)赤外線センサー反応時の動作設定

 カウントダウンをリセット、感知したことを表示して、

 ・生存ステータスのとき

  └何もしないでカウントダウンだけリセット

 ・死亡ステータスのとき

  └生存ステータスにして各所に投稿

⑤(再)起動したことを通知

⑥④の処理二つを並列処理する

 

といった具合です。

 

●参考サイト様

Python 標準ライブラリ — Python 3.10.4 ドキュメント

Python クラス変数 と インスタンス変数 の違い | AI Academy Media

ラズパイ(Raspberry Pi)を使って赤外線センサの仕組みを学ぼう | Device Plus - デバプラ

Python3でマルチスレッド処理を試してみる - Corgi Lab. ~備忘録のための技術ブログ~

discordのwebhookをpythonのrequestsのpostで使う | jibundex

Twitter APIで遊んでみた ~2. ツイートの投稿と取得~ | SIOS Tech. Lab

 

※重要※現役エンジニアの皆様へ

いじめないでください。わかってます。

どうして意味の分からないところでsleep入れてるんだとか。

その他もろもろね。ええ。

わかりますけど。

こっちはプログラミング1日目ですよ!?そんな言わなくたってもねえ!ねえ…

(よりよいアルゴリズムというか記法があるならぜひともご教授お願いいたします)

 

そういうことで、コードも書いて

赤外線センサーも挿して

正常に動作することを確認!

 

ここでもう日付変わるぐらいになってます。寝よう。

 

⑤VS.赤外線センサー

次の日、

長めのジャンパ線(ラズパイとセンサー繋いでるやつ)が届いたので

本格運用に向けて線を挿して動作を確認。

 

するとなんと

 

誰も通ってないのに

1分おき(ぐらい)に反応する。

 

線を替えても、センサーを替えてもダメ。

 

こりゃ憑いてるのか…?

と思いながら日本語でググってもマジで出てこない。

 

英語で頑張ってググった結果

PIR false positives - Raspberry Pi Forums

上記のサイトなどがヒット。

 

要は

Wi-Fiが干渉して反応することがある

らしい。うそでしょ?なに?

 

Wi-Fiって赤外線なの?

フリーW-Fiってフリー赤外線でもあるの?

なに?ガラケーの赤外線通信ってWi-Fiですらあったってこと?

 

意味わかんないですが、

実際にジャンパ線を

延長しまくって本体から遠ざけて

動作確認したらちゃんと動いた。

 

何?昨日ちゃんと動いたのも意味わかんないし。

 

この(Wi-Fi干渉)対処で1日かかりました。

クソー

 

⑥設置

ここまで来たらあともう少し。

 

延長コードとなんかいろいろ買って、

まず本体をここ(長押)にひっかけて、

電源をいい感じに這わせました

 

賃貸なのでマステの上にいろいろしてます

 

赤外線センサーまでのジャンパ線を整えて、

センサーを置く予定の場所まで固定しました。

 

この時点で一回プログラムを動かしっぱなしにして

異常停止することがないか

異常に反応することがないか

をチェックします

 

その間に

センサーのケースを作ります

基盤むき出しだし、固定できないし。

 

ホムセンで薄いアクリル板を買って、

切って瞬間接着剤で貼って、

できた!!きったね!しらね!

 

ちなみにこれ、

右が寝室、左が仕事部屋なのですが

完全にこのセンサーの前を通過しないと

反応しないっぽかったので

こんな感じで高いところから角度をつけたところ

部屋から出た瞬間に反応するようになりました。

やったね!

 

あと運用までにやることは一つ。

 

⑦起動時にプログラム自動実行させる

ちゃちゃっと設定できると思ってました。

大間違いでした。

 

センサー設置日の24時から

「ラズパイが起動したときに一緒にプログラムも実行させる」

(systemdというもので)設定をしようとしたのですが、

AM5時まであれこれした結果

設定できませんでした。

 

結果から申し上げると、
以下の点で躓きました。(技術的な説明です)
・rootにtweepyをインストールしてなかった
・ネットワーク確立前にwebhookにPOSTしようとするので失敗する
 └"systemd-networkd"と"systemd-networkd-wait-online"をenableにする
 └serviceファイルの[Unit]に"After=network-online.target" と "Wants=network-online.target"を追加する
 └コードの最初に15秒待機を入れる

・参考サイト様
systemdを使ってスクリプト自動起動
systemdで確実にネットワークの起動後にサービスを起動させたい場合のメモ - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ
NetworkManager-wait-online.serviceはどのような場合にenableにすれば良いか、を理詰めで考える - Qiita

 

 

 結果、うまいこと設定できたのが

次の日のお昼過ぎ。(もちろん寝た後です)

 

本当にこんなところで苦労するとは思わなかった。

 

そんなこんなで本格稼働です!

 

⑧課題

残された課題としては、

・通知先にLINEが設定されていないので設定する
・プログラムがぶっ壊れた時「自分から見に行く」以外に知る術がない

といったところです。

 

⑨まとめ

なんかごちゃごちゃ書きましたが、

 

・寝室―仕事部屋間に赤外線センサーを設置した

・24時間通過がないとツイートなどされるようにした

 

って感じです!

 

僕がぶっ倒れてるかもしれないときは

「24時間家にいないか、死んでるかもしれません」

ツイートがされるはずなので、

 

心配でしたらご連絡いただけますと幸いです。

 

そして、上記以降のツイートが全然なかったら

そういうことだと思ってください。

 

あと、このシステム普通に売れない?

買ってください。40万から。よろしくお願いします。