にゃんCAM再構築

ことの経緯 以前、raspberry piにて猫カメラ(にゃんCAM)を構築しました。 普段はTapoを使っていたので全然出番が無かったのですが、久々に起動しようとしたら起動を繰り返す変な動作をしていました(デスクトップ表示→左上にアンダーバー点滅→再起動の繰り返し) 直そうと適当にやっていたらSDカードを割ってしまいました(物理的に)。 というわけで、再構築しようとしたのですが環境が変わってつまずいたので新たに記事にしました。 教訓 SDカードは色んな意味ですぐ死ぬ。クローンを作っておくこと ハード raspberry pi 3B(3B+ではない) 32GBのSDカード 5.1V出力のACアダプタ これはlow voltage errorの対策です 機能、ファイルの置き場など OpenCVでUSBカメラ(webカメラ等)2台を制御し、30分ごとにLINEで撮影画像を送信します 2台用でプログラムを書いてあるため、2台以外(1台or3台〜)ではプログラムがエラーになり、動作しません。(具体的にはプログラムエラー→再起動のループ)) 1台分にコメントアウトすれば1台でも動作します saikidou.serviceとsaikidou.timerによってラズパイを定期再起動させますが、ラズパイの安定性向上やUSBが死んでカメラが認識しなくなることが偶にあり、それを復帰させるために実施します USBが死ぬ(例えば、カメラが1台しか繋がってないような状態になります)と上記した通りnyancam.pyがエラーになり、nyancam.serviceによってプログラムの再起動を繰り返しますが、少なくとも定期再起動のタイミング(6時間ごと)で復帰するため長期にカメラが機能しなくなることはなくなります 死亡したときはプログラムの再起動が繰り返されるため、USBカメラのLEDが点滅します(LEDは撮影時に点灯) ラズパイのそばで対応できるなら再起動を待たずともUSBの抜き差しで再認識します(USBが復帰し、nyancam.serviceによる再起動でカメラが2台認識します) GPIO26に接続したスイッチを単押しするとテスト撮影、5秒以上長押しするとラズパイをシャットダウンします Filename Path Note nyancam.py /home/USERNAME/Desktop/ プログラム本体 nyancam.service /lib/systemd/system/ nyancam.pyを自動起動する エラー時にnyancam.pyを再起動する saikidou.service /lib/systemd/system/ ラズパイを定期再起動する saikidou.timer lib/systemd/system/ saikidou.serviceとセット githubにあげてあります プログラムの実行結果 nyancam.pyを実行した結果例を載せます カメラ検索でワーニングが出ますが、動作に問題ないためそのままにしています。 camera number 0 Find! [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video1): can't open camera by index camera number 1 None camera number 2 Find! [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video3): can't open camera by index camera number 3 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video4): can't open camera by index camera number 4 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video5): can't open camera by index camera number 5 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video6): can't open camera by index camera number 6 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video7): can't open camera by index camera number 7 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video8): can't open camera by index camera number 8 None [ WARN:0] global /tmp/pip-wheel-a8gfdc_n/opencv-python_13563f08137a4b20bc4dfee05bcbf854/opencv/modules/videoio/src/cap_v4l.cpp (893) open VIDEOIO(V4L2:/dev/video9): can't open camera by index camera number 9 None 接続されているカメラは 2 台です カメラ0 : 0 番 カメラ1 : 2 番 CamNum0 : 0 CamNum1 : 2 ### にゃんCAM 起動 ### rev:7 30.0分ごとに撮影しまーす! 接続カメラ : 2 台 2023-09-09 11:38:42.525746 カメラ0 うp残 : 46 / 50 カメラ1 3BはUSBブートがめんどいらしい SDカードを割ってしまったので、もっと(物理的に)丈夫なUSBメモリでbootさせることを考えました しかし、3B+はデフォルトでUSBブートができるのですが、3Bはできないようです 正確には色々変更すればできるようですが面倒なので見送りました ...

2023/09/07 · Last updated on 2023/09/23 · 3 min · 576 words

【にゃんCAM2】Tapo C200 を Homeassistant で動かす

まえがき 以前、ラズパイで猫カメラを作りました(にゃんCAM1)。 今回は、2です。 homeassistantとTP-linkのipカメラ Tapo C200 を使って構築しました。 こっちのほうが全然簡単です笑。 にゃんCAM1 part1から8まで記事が分かれています。 全体は8を見ればわかります。 8のリンクのみ載せておきます。 にゃんCAM1からの性能アップ内容 にゃんCAM1は画像をLINEで定期送信(30分ごと)が機能でしたが、それに加えて以下を追加しました。 ほとんどカメラのデフォルトの機能ですが。 暗視 動体検知 動画配信 カメラ設置の自由度向上 画質向上 簡素化 以下に詳しく記載します。 暗視 カメラのデフォルトの機能です。 IRカメラが搭載されており、周りの明るさに応じて自動でカメラが切り替わります。 暗さのしきい値を越えればIRカメラに切り替わります。 にゃんCAM1では普通のwebカメラを使っていたので、暗くなると全く映りません。 そのため、夜でも薄暗く電気をつけていました。 2ではその必要がなくなったため、にゃんこの負担も減るでしょう。 一応、にゃんCAM1でもIRのwebカメラを買って動作確認はしましたが、面倒くさくて完全導入はしていませんでした笑。 動体検知 これもカメラのデフォルト機能です。 30分ごとの撮影では良いタイミングを逃してしまうことは多々あります。 カメラの設置目的はお留守番に問題ないかな?の確認用とは言っても、あっ今ウンコ💩した!ご飯食べてる!がリアルタイムにわかるのは嬉しいです。 動画配信 これもデフォルト機能です。 あまり見ませんが、リアルタイムで見れるという安心感は大きいです。 homeassistantのオーバービューに表示していますが、外出先からはtailscale(VPN)でアクセスして確認します。 カメラ設置の自由度向上 カメラと電源コードのみなので置き場所の自由度があがります。 にゃんCAM1ではラズパイ本体とwebカメラ2台をダイソーのワイヤーネットに固定していたので大きいし、設置位置も限られました。 それが解消しました。 また、ipカメラなので、カメラが容易に増やせるのもメリットです。 簡素化 homeassistant本体(ラズパイ)はルーターボックスに常設していたWOLサーバ用ラズパイに入れたので、カメラ使用時はカメラだけ設置すれば良くなりました。 ついでにWOL機能もhomeassistantに構築しています。 後ほど紹介します。 そもそも ここまで読んでいただいて言うのもあれですが、わざわざhomeassistantを使わなくてもtp-linkのアプリ(tapoアプリ)を使って普通に使うだけで十分高性能です。 上記した動体検知や暗視は使えますし、モバイル回線からも動画が見れます。 じゃあ何でわざわざ?なのですが、以下が理由です。 tp-linkのサーバに動画をあげたくない LINEで画像を確認したい homeassistantで構築できる機能を使いたい 1については、モバイル回線から動画が見れている時点でtp-linkのどっかのサーバに動画が上がっているわけです。 動画のようなプライベート性の高いものを知らないサーバに上げるのは気持ちが悪いです。(それを言ったら他のクラウドサービスも一緒ですけどね) Amazonのレビューを見ると、カメラが勝手に動いたとかtapoアプリのプライバシーポリシーには第3者が動画を見ますよ?(←よく読んでない)的なことが書いてあって同意しないとアプリを使えないとか何とか。 など諸々を考えてLANのみの運用にしました。 後ほど詳しく記載しますが、ルータの設定でtapoのインターネットアクセスを遮断しています。 WANからはtailscale(VPN)を使ってアクセスします。 2についてはにゃんCAM1の機能を維持するためです。 tapo純正ではその機能は無い(調べてない、が正しい)ので、作ることにしました。 3については、WOLやtailscaleのことです。 日々の運用方法 homeassistantは常に起動しており、WOLやtailscaleのサーバとして動作しています。 そして必要なときにカメラの電源を入れればhomeassistantから見えるようになり、監視が開始します。 カメラが不要になったら、カメラの電源を抜けばそれで終わりです。 環境 raspi 3B+ 32GB SDカード 失敗メモ 以下に記載したのは構築時の自分メモです。 実際は失敗したので使用していません。 ...

2022/09/11 · Last updated on 2023/09/22 · 4 min · 743 words

ラズパイのCPU温度をブラウザに表示

ラズパイの温度が気になります。夏だし🚀 こちらの記事のWOLサーバにCPU温度とCPU周波数を表示していますが、その詳細です 環境 ラズパイ 3B+ ubuntu 20.04.4 LTS 温度取得(sensors) ubuntuなので、こちら↓に習って sensors このままだとcpu温度しか出ないのでこちらを参考↓ sudo sensors-detect これでたくさん出る 温度取得(vcgencmd) 「ラズパイ 温度」とかでググると、vcgencmdの記事がたくさんヒットするのではないでしょうか ubuntuなので、vcgencmdがありません ので、まず入れます sudo apt install libraspberrypi-bin 以下を実行すると温度が出ます vcgencmd measure_temp temp=56.9'C sudo じゃないとだめ(でもなかった) sudoをしないと、以下のエラーが出ました vcgencmd measure_temp VCHI initialization failed でも、最初だけ必要なのかもしれません 2回目以降はsudoなしでもいけました 温度取得(ファイルから取得) ← 軽い!これを採用💡 これまでは前置きです(長い) WOLサーバに合わせて記述するので、Flaskのpythonプログラム中でvcgencmdを実行しようと思っていましたが、どうやってもブラウザに表示できませんでした Linuxコマンドはsubprocessで実行しようと試していましたが、どうやらこれが上手くいかない模様 なのでこちら↓を参考にして温度が収められているファイルから値を取ってくることにしました 四つめは動作電圧を確認するメリットをあまり見いだせなかったのと、動作電圧を記録したファイルが見当たらなかったためです。 とのことなので、電圧はいいや(分かれば、何かしらの原因切り分けには使えるが) 温度はここ↓ /sys/class/thermal/thermal_zone0 cat temp 59610 1000で割って、59.61℃ 温度じゃないけど周波数はここ↓ /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq kHz表示なので、1000で割ってMHzになる 表示仕様 温度と周波数を表示 WEBブラウザをリロードすれば値は更新 プログラム修正時 .pyファイルを書き換える時はsystemdのサービスを以下のようにする stop(止める) → status(止まったこと確認) → .py書き換える → start → status(動いたことを確認) ちゃんとしたコマンドは以下↓(stopの例) sudo systemctl stop wakeonlan-server.service nginxは触らなくても、上記だけでWEBページを更新すれば挙動がわかる ...

2022/06/30 · Last updated on 2023/09/22 · 1 min · 109 words

【ラズパイで猫ちゃんカメラ】 その8 テスト撮影用のスイッチ動作(GPIO)と全体

前回 前回はプログラムの自動起動について書きました 今回 GPIOを使って、スイッチの状態を読み取りテスト撮影などの機能を盛り込みます また、これまでの内容を総括した全体プログラムを紹介します 現在、猫カメラとして実働しているものになります お出かけには必須になりました 作ってよかった😁 間が結構飛んでいるかもしれませんが、大きな心でご容赦ください笑 スイッチの機能 カメラの設置場所を決めるために撮影用のスイッチをつけます 以下のような機能をつけます 短押し(5秒以下)でテスト撮影 長押し(5秒以上)でラズパイシャットダウン 撮ったらLINEします スイッチの状態を読み取ってみる スイッチは秋月電子でこれを買いました↓ プッシュスイッチは電源(3.3V)ースイッチーラズパイ のように接続します GPIO26に接続し、プルダウンに設定しています ですので何もしなければ0Vで、スイッチを押せばhigh(3.3V)となります whileループ内でスイッチの状態を読み取ります プルダウンとして設定しているので、何もしなければGPIO26の電圧はGNDレベル=0Vでありaの値をprintすると0となります スイッチを押すとGPIO26の電位がHigh=3.3Vとなるのでa=1となります ctrl + Cでwhileループから抜けて終了です import RPi.GPIO as GPIO button = 26 GPIO.setmode(GPIO.BCM) GPIO.setup(button,GPIO.IN,pull_up_down = GPIO.PUD_DOWN) # プルダウン try: while True: a = GPIO.input(button) print(a) except KeyboardInterrupt: # ctrl + C print('\nbreak') GPIO.cleanup(button) 短押し、長押し判定 割り込みは使っていません whileループ中に50msec毎にスイッチを見に行っています 50msecはチャタリングを考慮しての時間です ボタンを最初に押してから離すまでの時間を測り、5秒以下なら短押しと判定、5秒以上なら長押しと判定しています 長押しでシャットダウン import osをして sudo shutdown -h now を実行するだけです 長押し時の処理にこれを実行します 実働プログラム レビジョン7まで上げてやっと安定しました 今のところ問題なく動いているプログラムです 繰り返しの記述があるので、簡素化したいところです が、まあ動いているのでやる気が出たら手を入れます笑 """ にゃんCAM 更新履歴 2021/09/26 : rev0 new 2021/09/29 : rev1 撮影時のみカメラ起動、日本語化 2021/10/08 : rev2 リトライ処理実装(おもにwifiが無くなったことを想定) 2021/10/09 : rev3 カメラ2台対応(1台用とは別にする 1台用はrev2),カメラ自動認識(1台でも認識はするがエラーになる。あくまで2台用) 2021/10/11 : rev4 カメラ2台用 リトライ処理削除(リトライはsystemdのRestart=alwaysが行うため) 2021/10/13 : rev6 カメラ2台用(ベース:rev4) 起動時にまず撮影 2021/10/13 : rev7 いじり respをそとに resp間にsleep(resp_time) 送る間隔が早すぎてエラーになってたかも(ネットが低速で画像が送れない) 自動起動 sudo nano /lib/systemd/system/nyancam.service sudo systemctl start nyancam.service sudo systemctl stop nyancam.service sudo systemctl enable nyancam.service sudo systemctl disable nyancam.service /etc/systemd/system.conf #DefaultStartLimitInterval=10s #DefaultStartLimitBurst=5 systemdは10秒の間に5回まで再起動が行われ、それを超えると再起動をやめてしまう ので、そうならないように起動時にtime.sleep(initwait)する initwait = 3 10秒では3秒*3回=9秒より、3回しか再起動できないため、再起動しつづける動きとなる 再起動 1日4回 0,6,12,18時 systemdで実装 saikidou.service saikidou.timer ← enable 機能 ・ラズパイ起動時に自動起動、撮影開始 ・プログラムが失敗(wifi消失などによる)したときはsystemdにより自動再起動する 再試行中はカメラの青LEDが点灯(接続カメラチェックによる)するためわかる 再試行周期はinitwait+5秒くらい ・短押し : テスト撮影(カメラ設置場所決定のため) ・長押し(5秒以上) : shutdown エラー時(wifi消失) wifi消失によりエラーとなるのはAPIで通信する resp = requests.post(api,headers = headers,data = data) がある以下3箇所 1.### にゃんCAM 起動 ### 2.ボタン短押し(テスト撮影) 3.定期撮影(30分ごととか) エラーパターン1 : 最初からwifiがない 1.でエラーになる エラーパターン2 : 途中でwifiがなくなる 1.はきっと通過している 2.や3.でエラーとなる wifiが復帰したら、最初からやり直しなのでLINEで### にゃんCAM 起動 ###がまた来る """ import cv2,time,requests,os import RPi.GPIO as GPIO import datetime import sys initwait = 3 time.sleep(initwait) ######## Variables ######## rev = 7 # プログラムレビジョン token = 'あなたのトークン' # LINE Notifyトークン init = True # 初回のみフラグ Snapshot_period = 1800 # 撮影周期 sec 30分=60秒*30=1800秒 Longpush_period = 5 # 長押し時間 sec 5秒 shutter_time = 5 # 露光?時間 sec elecomピンクが性能悪いので5秒 resp_time = 1 # カメラ2台 resp間ウエイト(画像が重い) # リトライ関連 # デバッグ時は短く設定 エラー行数が出ないのが惜しい MAXTRY = 0 # トライ回数 20回 Try_period = 1 # トライ周期 sec 1分 Trycnt = 0 # トライカウント ######## 接続カメラチェック ######## true_cam = [] # 配列用意 for camera_num in range(10): # カメラ番号を0~9まで変えて、COM_PORTに認識されているカメラを探す cap = cv2.VideoCapture(camera_num) ret, frame = cap.read() if ret == True: # capture.read()に画像が格納されていたら=画像が取得できたら=カメラが接続されていたら true_cam.append(camera_num) #print('camera number {} Find!'.format(camera_num)) else: # ここでワーニング #print('camera number {} None'.format(camera_num)) pass print('接続されているカメラは {} 台です'.format(len(true_cam))) for i in range(len(true_cam)): # カメラ番号調べ print('カメラ{} : {} 番'.format(i,true_cam[i])) CamNum0 = true_cam[0] # 接続されたカメラの番号 CamNum1 = true_cam[1] print('CamNum0 : {}'.format(CamNum0)) print('CamNum1 : {}'.format(CamNum1)) ################################# # Setup working directory os.chdir('/home/pi/Desktop') # Setup GPIO button = 26 # VDD(3.3V) - button - GPIO26 GPIO.setmode(GPIO.BCM) GPIO.setup(button,GPIO.IN,pull_up_down = GPIO.PUD_DOWN) # pull down button_old = time.time() trig = False val = False long = False flag = False # Setup LINE Notify api = 'https://notify-api.line.me/api/notify' headers = {'Authorization': 'Bearer' + ' ' + token} filename0 = 'cat0.jpeg' images0 = '/home/pi/Desktop/' + filename0 filename1 = 'cat1.jpeg' images1 = '/home/pi/Desktop/' + filename1 # Setup OpenCV windowsize = (1024,768) # 4:3 LINE Notifyの送信可能な最大解像度 ImageNum = 0 old = time.time() # 初回時間 try: while(True): # にゃんCAM main loop if init == True: # 初回のみ message = '\n\n### にゃんCAM 起動 ###\nrev:{}\n\n{}分ごとに撮影しまーす!\n接続カメラ : {} 台'.format(rev,Snapshot_period/60,len(true_cam)) data = {'message': message} resp = requests.post(api,headers = headers,data = data) print(message) print(datetime.datetime.now()) # 初回にまず撮影する shutter_t0 = time.time() cap0 = cv2.VideoCapture(CamNum0) cap1 = cv2.VideoCapture(CamNum1) while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining # for LINE Notify # カメラ0 message0 = '\nカメラ0\nうp残 : {} / {}'.format(ratelimit_image_remaining,ratelimit_image) data0 = {'message': message0} files0 = {'imageFile': open(images0,'rb')} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) # カメラ1 message1 = '\nカメラ1' data1 = {'message': message1} files1 = {'imageFile': open(images1,'rb')} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) init = False # 初回のみより、initのTrueへの復帰は不要 debug0 = time.time() # for measure time of main loop # button detect button_t0 = time.time() # now # snapshot t0 = time.time() # now debug1 = time.time() # for debug # button detect if button_t0 - button_old > 50/1000: # 50msecごと(以上)にボタンの状態を監視 button_state = GPIO.input(button) # check button state if button_state == 1: # button pushed if trig == False: # first pushed firstpush = time.time() # for debug trig = True val = True else: # button released if val == True and long == False: # short pushed( < 5sec ) : Test shot shortpush = time.time() # for debug shutter_t0 = time.time() cap0 = cv2.VideoCapture(CamNum0) cap1 = cv2.VideoCapture(CamNum1) while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining # for LINE Notify # カメラ0 # message0 = '\nカメラ0\nテスト撮影\n短押し : {} sec\nうp残 : {} / {}\nエラー時リトライ回数 : {} / {}\nリトライ周期 : {} sec'.format(round((shortpush - firstpush),2),ratelimit_image_remaining,ratelimit_image,Trycnt,MAXTRY,Try_period) message0 = '\nカメラ0\nテスト撮影\n短押し : {} sec\nうp残 : {} / {}'.format(round((shortpush - firstpush),2),ratelimit_image_remaining,ratelimit_image) data0 = {'message': message0} files0 = {'imageFile': open(images0,'rb')} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) # カメラ1 message1 = '\nカメラ1\nテスト撮影' data1 = {'message': message1} files1 = {'imageFile': open(images1,'rb')} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) print('main loop : {} sec'.format(debug1 - debug0)) val = False trig = False flag = False long = False if val == True: # button pushed longpush = time.time() # for debug if (longpush - firstpush) > Longpush_period: # long pushed ( > 5sec ) : shutdown if flag == False: # once count print('\n長押し : {} sec'.format(round((longpush - firstpush),2))) # for debug message0 = '\n\n### にゃんCAM シャットダウン ###\n\nばーい!' data = {'message': message0} requests.post(api,headers = headers,data = data) print(message0) # for debug time.sleep(3) # シャットダウンするまで少し待ち # os.system('sudo reboot') os.system('sudo shutdown -h now') flag = True long = True trig = False button_old = button_t0 # 定期撮影 if t0 - old > Snapshot_period: shutter_t0 = time.time() # initial time cap0 = cv2.VideoCapture(CamNum0) # VideoCapture cap1 = cv2.VideoCapture(CamNum1) # VideoCapture while True: ret, frame0 = cap0.read() # capture(too slow!!) ret, frame1 = cap1.read() # capture(too slow!!) shutter_t1 = time.time() if(shutter_t1 - shutter_t0) > shutter_time: # shutter time frame0 = cv2.resize(frame0, windowsize) # resize the window frame1 = cv2.resize(frame1, windowsize) # resize the window cv2.imwrite(filename0,frame0) # save cv2.imwrite(filename1,frame1) # save cap0.release() cap1.release() break # break while loop ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining #カメラ0 files0 = {'imageFile': open(images0,'rb')} # message0 = '\nカメラ0\nImage_{}\nうp残 : {} / {}\n撮影周期 : {} 分\nエラー時リトライ回数 : {} / {}\nリトライ周期 : {} sec'.format(ImageNum,ratelimit_image_remaining,ratelimit_image,round((t0-old)/60,2),Trycnt,MAXTRY,Try_period) message0 = '\nカメラ0\nImage_{}\nうp残 : {} / {}\n撮影周期 : {} 分'.format(ImageNum,ratelimit_image_remaining,ratelimit_image,round((t0-old)/60,2)) data0 = {'message': message0} requests.post(api,headers = headers,data = data0,files = files0) time.sleep(resp_time) #カメラ1 files1 = {'imageFile': open(images1,'rb')} message1 = '\nカメラ1\nImage_{}'.format(ImageNum) data1 = {'message': message1} requests.post(api,headers = headers,data = data1,files = files1) # for debug print(message0) print(message1) print(datetime.datetime.now()) print('main loop : {} sec'.format(debug1 - debug0)) # メインループ実行時間 old = t0 ImageNum = ImageNum + 1 Trycnt = 0 # プログラムに復帰したらトライカウントをリセット except KeyboardInterrupt: print('\nプログラム終了!!') sys.exit() 実働で調整したこと(関係ないことも含む。つまり何でもあり) 実際の現場で動かさないと見えてこないことが沢山ありました ...

2022/06/28 · Last updated on 2023/09/23 · 7 min · 1371 words

【ラズパイで猫ちゃんカメラ】 その7 ラズパイ起動時にプログラムを自動起動する

前回 だいぶ日が空いてしまいましたが・・・ 前回はLINE NotifyのAPI制限を確認しました 今回 systemd で自動起動 ラズパイを起動してからプログラムを手動で実行するのは実用的じゃないですね ですので、ラズパイ起動時にプログラムを自動起動します 自動起動には systemd を使います こちらの記事を参照ください↓ systemd の良いところ ちょっと本旨とずれますが、プログラムがエラーで停止しても、systemdがサービスを再起動してくれます ラズパイ自体の再起動はcronでも良いですが、自作のプログラムはバグ取りが不十分なのでエラー停止でサービスを再起動してくれるのは助かります(エラー対策の話につながります) エラー対策 今回はLINEに送信するため、ネットワークを利用します 実際動かしてみて、wi-fi関連処理がうまく行かずエラー停止が結構ありました 自分ではどうしようもない問題でした また、USBが死ぬこともよくあり、これは再起動で復活しますので、定期再起動もサービスにしました 手動でプログラムを再実行できれば対策不要ですが、猫カメラは不在時に使用します なので、手動で再実行はできず、エラー対策は必須です 私も猫カメラ開発でエラー対策が必要だと痛感しました (カメラ止まった!猫の様子がわからない!どうしよう!は飼い主には耐えられないです) 実装の候補は以下2つ Python中にリトライ処理をいれる systemdが勝手に再起動してくれる ← こっちにした リトライ処理も実装してみましたが、よりお手軽な systemd にしました .service の作成 猫カメラなので、名前はnyancam.serviceにしました(何でもOKです) ここでは/lib以下に作っています(上の記事でもあるように、/etc以下に作る方法もあります) sudo nano /lib/systemd/system/nyancam.service nyancam.serviceの中身↓ [Unit] Description = test [Service] ExecStart=/usr/bin/python3 /home/pi/Desktop/nyancam.py Restart=always Type=simple [Install] WantedBy=multi-user.target ExecStart = XXXはプログラムの起動についての記述です /home/pi/Desktop/nyancam.pyをpython3で起動します Restart=alwaysでnayncam.pyがエラー時にサービスを再起動してくれます systemd コマンド sudo systemctl start nyancam.service <--- サービスを起動するコマンド sudo systemctl stop nyancam.service <--- サービスを停止するコマンド sudo systemctl enable nyancam.service <--- サービスを自動起動するコマンド sudo systemctl disable nyancam.service <--- サービスを自動停止するコマンド start は起動(ラズパイを再起動したら停止してしまう) ...

2022/06/27 · Last updated on 2023/09/23 · 1 min · 164 words

ラズパイ(ubuntu 20.04.4 LTS)に Tailscale を構築

ラズパイにtailscaleを構築した。 tailscaleはWireGuard(VPN)を扱いやすくしたものである(ざっくり) 素のWireGuardも検討したが、マンション住まいのため、上位のルーターの構成が不明で、かつポートを開けたり(知識不足)を下手にやってセキュリティガバガバになるのは嫌だったのでtailscaleにした 環境 ラズパイ 3B+ ubuntu 20.04.4 LTS 32GBのSDカード Tailscale のアカウント作成 何はともあれ、まずはアカウントを作る 参考: https://blog.tsukumijima.net/article/tailscale-vpn/#toc3 githubアカウントでログインした その端末の認証鍵の有効期限を無効にすることができます。 → 無効化した ラズパイに構築する前に、SynologyのNAS(DS118)がTailscaleに対応していたので、先に入れてみた すぐにアクセスできた 簡単すぎてビビった ラズパイのセットアップ まず、OSをSDカードに書き込み 書き込みには Raspberry Pi Imager を使用 https://www.raspberrypi.com/software/ 空だけど、何となく erase してから書き込む こちらを参考(↓)にしてwolサーバとしたかったのでubuntuにした https://macoshita.me/posts/wi-fi-router-and-tailscale/ ubuntu はラズパイ 3B+ なので、32bit armhf lts安定版 とした 初回セットアップはこちら(↓)を参考 https://takaken.tokyo/dev/raspberrypi/raspi_ubuntu_setup2004/ 習って、rootパスワード変更まで実施 有線だけで使うため、無線LANは無し(コメントアウトした) Tailscale のインストール 参考: https://tailscale.com/download/linux/ubuntu-2004 tailscale ip -4 か ip addr show tailscale0 で、ipがわかる でもipアドレスはtailscaleのページ(https://login.tailscale.com/admin/machines)でわかるから、コマンドを打たなくてもいいか ラズパイを再起動して、tailscaleが自動起動することを確認した 確認は、上記のtailscaleのページでactive(緑色の丸)になっていることからわかる exitnode の設定 ずっと電源入れっぱなしにしても大丈夫なラズパイをexitnodeに設定する VPNを野良Wi-Fiをセキュアにするために使いたいなら exitnode の設定が必要 exitnode経由にしてなかった時は、接続先がいなくてもVPN接続ができていた おやっ、VPNって2拠点間を繋ぐものなのになんで接続ができて(VPNがactiveになる)るんだ?って思ってグローバルipを確認したら、VPN接続前後でグローバルipが変わってなかった global ipの確認: https://www.cman.jp/network/support/go_access.cgi つまり、インターネットには保護なしで出ていた ...

2022/06/26 · Last updated on 2023/09/22 · 1 min · 123 words

ラズパイを WOLサーバ にする

こちらの記事で紹介したようにtailscaleを使ってラズパイをVPNサーバとして使っていますが、VPNできたら当然自宅NASにもアクセスしたくなりますよね でも、以前書いたようにNAS(DS118)は常時電源を入れていないので、使うときには起こす必要があります ですので、必要な時にNASを起こせるようにラズパイをwolサーバに仕立てます NAS(DS118)のwolを有効にする 使っているNAS↓ まず、NASがwol信号を受け付けられるように、wolを有効にします https://support.ask-corp.jp/hc/ja/articles/360051678474-Wake-on-LAN-を設定したい ラズパイに WOLサーバ を建てる 参考(大感謝!) 構成は Flask + uWSGI + Nginx となっています ラズパイのCPU温度とクロック周波数を追加していますが、すべてそのまま使わせていただきました 便利に使えており、大変感謝しております! 構築に関して、自分のつまずいた点などを書いています 出来上がり見た目↓ pip install -r requirements.txt のインストール方法 https://note.nkmk.me/python-pip-install-requirements/ 文字化けしたがインストールできたっぽい たぶんteratermの設定のせいで文字化けした 起動など ファイルは大体をPCで作って、FTPソフト(filezillaを使用)でラズパイに転送し、手直しはSSH接続して直接ファイルを弄った ubuntu@ubuntu:~$ pwd /home/ubuntu /home/ubuntu の下にファイルを作って配置する サービス起動 sudo systemctl start wakeonlan-server サービス自動起動(起動時) sudo systemctl enable wakeonlan-server 実行したのでbootで自動実行される サービスのステータス確認 sudo systemctl status wakeonlan-server active(running)になっているのでおけ プログラム(wol.py)のエラー箇所もわかる ValueError(“Incorrect MAC address format”) が出ていることがわかった たぶんwol.htmlから変数(MACアドレス)をwol.pyに渡す時におかしくなってるのだが、検証できなかった なので、MACアドレスを直書きすることにした(当面は同じNASしか使わないし) MACアドレスは:で区切る # wol.py 一部抜粋 def wol(): # addr = request.args.get('addr', '') addr = "XX:XX:XX:XX:XX:XX" # NASのMACアドレス直書き message = "" if request.method == 'POST': send_magic_packet(addr) message = 'Sent magic packet' return render_template('wol.html', message=message, addr=addr) WOLの方法 ubuntuのアドレス/wol ...

2022/06/26 · Last updated on 2023/09/22 · 1 min · 113 words

【ラズパイで猫ちゃんカメラ】その6 LINE NotifyのAPI制限を確認する

前回 前回はpythonで定期的な動作を実装しました。 これで例えば10分毎に撮影し、画像をLINEすることができます。 今回 LINE NotifyのAPI制限を確認します。 画像のアップロード枚数制限やAPIのコール回数制限があります。 テストで画像を送りまくっていると制限にかかり、送れなくなります。 突然送れなくなるので、注意が必要です。 APIコール回数は1000回ですのでそうそう消費しないと思います。 ですが、画像のアップロード枚数制限は50枚(1時間あたり)です。 案外すぐ達してしまうのでモニターすることをオススメします。 ドキュメントのAPI Rate Limitに記載があります。 各サービスごとに1時間にAPIをcallできる回数の上限が設定されています。 デフォルトは1000回に設定されています。 上限はaccess tokenごとに設定されています。 API Rate Limitのstatusは、APIの以下のresponse headerで確認することができます。 API Rate Limitの確認方法 こちらを参考にさせていただきました。ありがとうございます(^^) # line-notify-limit.py import requests token = 'あなたの取得したトークン' api = 'https://notify-api.line.me/api/notify' message = '\n\nLIMIT CHECK' headers = {'Authorization': 'Bearer'+' '+token} data = {'message': message} resp = requests.post(api,headers=headers,data=data) print(message) # APIコール残 ratelimit = resp.headers.get("X-RateLimit-Limit") # max API call ratelimit_remaining = resp.headers.get("X-RateLimit-Remaining") # API call remaining print('API call remaining : {} / {}'.format(ratelimit_remaining,ratelimit)) # 画像うp残 ratelimit_image = resp.headers.get("X-RateLimit-ImageLimit") # max image upload at 1hour ratelimit_image_remaining = resp.headers.get("X-RateLimit-ImageRemaining") # image upload remaining print('Image upload remaining : {} / {} by an hour'.format(ratelimit_image_remaining,ratelimit_image)) # リセット時間 ratelimit_reset = resp.headers.get("X-RateLimit-Reset") # reset time UTC print('Reset time UTC : {}'.format(ratelimit_reset)) # ステータスコード print('HTTP status code : {}'.format(resp.status_code)) # HTTP status code ...

2021/10/19 · Last updated on 2023/09/23 · 1 min · 131 words

【ラズパイで猫ちゃんカメラ】 その5 10分毎撮影しLINEに送信する【python】

前回、LINE Notifyを使って簡単に画像を送ることができました。次は、定期的にLINEに画像を送る下準備として、定期的に動作させる方法を考えてみたいと思います。 pythonでプログラムを書いていきます(^o^) 構想だけ(というか実装できる気がしない) こちらの記事を参考にして、10分ごとに写真を撮り、前回写真と差分があればLINEする というのも考えました 送る画像を減らせてAPIの節約になることと、猫の動きがわかるメリットがありますが、日暮れで暗くなった場合にしきい値が変わるので誤って撮ってしまいそうでした ですので、実装やめました(てかできない) なので、変化ありなしに関わらず、10分ごとに写真を撮ってLINEすることにします time.sleep()はイマイチ イマイチというのは、今回の用途には合わないということです 真っ先に思いつくsleepは例えば10分止める場合は以下のように書けます import time time.sleep(600) # 600秒(=10分)スリープ イマイチなところは、処理を止めてしまうのでその間に他のことができなくなってしまうことです お安いWEBカメラ 10分スリープして、直後に撮影をすると画像が真っ暗になりました お安いWEBカメラにはシャッター時間を長くとって明るさを確保しないと画像が暗いということがよくあるようですが、私の数年前のWEBカメラも漏れずそんな感じみたいです 撮影する少し前に起動して、明るくなったら撮影する必要があります というわけでsleepは却下〜 起動処理は以下記事内にあるこの文↓ ret, frame = capture.read() タイマー割り込み? → 無い😇 別の方法としてタイマー割り込みを考えてみます 割り込みであればメインループでカメラを起動しておき、時間が来たら割り込みの処理を実行する(撮影する)ため明るさの問題は回避できそうです 10分毎に割り込み処理が入るようにすれば、10分毎の動作が可能です し・か・し どうやらラズパイにはタイマー割り込みがない模様・・・(pythonで書く場合) こちら↓の方が質問をされています 残念〜 python以外(Cとか?)で制御するならできなくは無さそうですが、敷居はとっても高そうです 割り込みはありますが、外部入力でGPIOに変化があったことをトリガーにする動作なのでタイマー割り込みではありません メインループ内で時間を測る 割り込みが使えないので、正確な時間は測れ無さそうです ですが、10分毎に動作のように分オーダーの時間であれば数秒程度の誤差なら全く問題にならなそうです というわけで、メインループ内で時間を測ることにしました import time old = time.time() # 現在時刻取得(sec) while(True): # main loop time0 = time.time() # 現在時刻取得 delta = time0 - old # 差分 if delta > 600: # 差が600sec(=10分)以上だったら print(delta) # 確認 old = time0 # 古い値を保持 はじめにoldに現在時刻を取得して格納します その後、メインループに入り都度時間を取得し、oldとの差分をdeltaに格納します そしてその差分deltaが10分以上となった時に処理を実行します 問題点 問題点はメインループの速度に依存してしまうことです メインループに時間がかかる処理があれば、その分遅れも大きくなるため、誤差が大きくなります 上のプログラムなら、600秒”以上”という条件でifとしているので、遅い処理があれば600.1秒にも600.2秒にもなる可能性があります ただもう一度になりますが、今回のように分オーダー毎の処理なら気にならない誤差だと思いますので許容しています おわり(^o^)

2021/10/05 · Last updated on 2023/09/23 · 1 min · 90 words

【ラズパイで猫ちゃんカメラ】 その4 ラズパイからPythonでLINEする(メッセージ、画像)LINE Notify

前回、Twitterのデベロッパーは却下されてしまったので別の方法を模索する中、LINE Notifyを使ってみることにしました。 こちらはAPI取得が楽ちんです。 自分のLINEや、LINEグループに画像を送るだけの今回のような用途には、こちらの方が適しているかもしれませんね! トークンの取得 こちらでトークンを取得します iPhoneからだとアクセストーン発行ボタンが見えませんでしたが、デスクトップモードにしたら見えました ご注意ください(^^) ドキュメントを見ると、トークンは一人当たり100個までのようです まずはメッセージを送ってみる【Python】 まずはメッセージだけを送ってみたいと思います プログラムはこれだけ↓ import requests token = 'あなたの取得したトークン' api = 'https://notify-api.line.me/api/notify' message = 'Hello!! message check!!' # 辞書型で記載 headers = {'Authorization': 'Bearer'+' '+token} data = {'message': message} requests.post(api,headers=headers,data=data) たったこれだけでメッセージが送信できてしまいます 「あなたの取得したトークン」と書かれているところに、取得したトークンを記載します 本当は、トークンはOSの環境変数において読み込んだほうが良いらしいですが、ここではしません(以下参照) 次に画像を送ってみる【Python】 カメラ画像を送りたいので、これが本来の目的です jpeg画像を用意して、以下のプログラムを実行すればLINEに画像を送ることができます ここでは、ラズパイのデスクトップにtest.jpegという画像データをおいて、それを送信します 画像だけを送ることはできないようです(メッセージとセットで送る) import requests token = 'あなたの取得したトークン' api = 'https://notify-api.line.me/api/notify' images = '/home/pi/Desktop/test.jpeg' # 画像のフルパスを記載 形式はjpgかjpegかpng message = 'Send image!!' # 辞書型で記載 headers = {'Authorization': 'Bearer'+' '+token} data = {'message': message} files = {'imageFile': open(images,'rb')} # バイナリ形式(テキスト以外) requests.post(api,headers=headers,data=data,files=files) 注意点 : 画像形式 画像の形式で詰まってました tifは送れません tif画像が送れず、ずっと悩んでました〜 対応しているのは jpeg,jpg,png のみ 最大サイズは1024×1024 LINE Notifyで送れる最大サイズは1024×1024です カメラ画像を送る際は、OpenCVでリサイズしてからにしましょう! これを超えるサイズの検証はしていないので、超えた場合にどんな挙動(画像が送れないetc)をするか不明です 以下を参考にさせていただきました(^^) おわり(^o^) ...

2021/09/15 · Last updated on 2023/09/23 · 1 min · 97 words