ラズパイによる訪問者通知システム

やっとRaspberry Piによる訪問者通知システムが完成しました

概要

1.玄関の赤外線センサーに反応があったら、電気を点灯
2.玄関に設置しているラズパイで写真を撮ってレンタルサーバのFTPにアップロード(サムネイル用の小さい画像 & 960×720の画像)(5枚の連続写真)
3.GMailを使って訪問通知メールを送信(内容には画像のアドレスを表示)
4.サムネイル表示のPHPにアクセスするとサムネイル表示

と言った感じ

玄関の赤外線センサー

玄関設置の赤外線センサーです
配線が見えてるのが少し不格好かな

フレームと雲台は3Dプリンターで自作してます

赤外線センサーはカメラ用のラズパイではなく、天井裏の照明管理ラズパイにつないでます
センサーに反応があると玄関の照明を点灯して、web経由で玄関カメラに撮影指示を出します

センサーとつなぐケーブルですが、個人的にはベル線がオススメ。
0.9mm単線なのでブレッドボードやジャンパーのメス線に直接刺さります(ギリギリだけど)
はんだ付けなどの加工が必要ないのが良いですね
ただ、センサーには線が3線必要なので、2本(4線)使って1線無駄にしてます
3線のベル線があれば良いのにな

天井裏のラズパイのPythonコード(一部)

# -*- coding: utf-8 -*-

import os
import RPi.GPIO as GPIO
#import datetime, time
from time import sleep
from datetime import datetime, date, time

import MySQLdb
import requests

#ラズパイのデータベースにログを残す(形式: ID, DATETIME, TEXT)
def cmdlog(cmd):
    c = MySQLdb.connect(host="localhost", db="******", user="****", passwd="******", charset="utf8")
    cursor = c.cursor()
    
    sql = u"insert into cmdlog values(null, cast(now() as datetime), '" + cmd + "')"
    cursor.execute(sql)
    c.commit()
    cursor.close()
    c.close()
    return

#GPIO23を出力に設定する(玄関の照明リレー操作)
GPIO.setup(23, GPIO.OUT)

#GPIO26を入力に設定する(赤外線センサーの出力)
GPIO.setup(26, GPIO.IN)

sflag=0
h=0
m=0
try:
   while True:
       m = datetime.now().minute
       h = datetime.now().hour
       sleep(1)
       if GPIO.input(26):
            #ログ出力は10秒以上あける
            if(sflag < 50):
                cmdlog("Genkan Sonsor")
            
            #カメラ撮影指示は5秒以上あける
            if(sflag < 55):
                # 玄関の照明を点灯
                GPIO.output(23, GPIO.HIGH)
                sflag = 60
                #玄関のラズパイにWebアクセス(撮影指示)
                requests.get('http://192.168.1.**:****/command.php?user=******&command=camera')
        sflag = sflag - 1
        if sflag == 0:
            if h>=21 or h<=16:
                # センサーに60秒反応が無かったら玄関の照明を消灯(17~21時を除く)
                GPIO.output(23, GPIO.LOW)
                sflag = 0

センサー(GPIO26)に反応があると、照明のリレー(GPIO23)をONにして
玄関ラズパイのwebページにアクセスします(requests.get)
(command=cameraで撮影を指示)

注意事項として、赤外線センサーの出力は5Vなので
ラズパイに入力する場合は同じ抵抗3つを使って3.3Vに落とす必要があります

玄関のカメラ撮影ラズパイ

こちらが玄関の撮影用ラズパイ
前回増設したコンセントにつないでます

造花にうまく紛れてるかな
カメラのケーブルがペラペラでうまく立たないんですよね
針金を使って立たせてます

玄関側ラズパイのPythonコード

import os
import time
from time import sleep

import picamera
from datetime import datetime
import MyLib

import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)

log_m = datetime.now().minute

import shutil
import os
import glob
from PIL import Image

#起動する度に画像フォルダをリセット
shutil.rmtree("/home/pi/Desktop/img/")
os.mkdir("/home/pi/Desktop/img")

mail_flag = 0
path = "/var/www/html/command.txt"

with picamera.PiCamera() as camera:
    while True:
        sleep(2)
        m = datetime.now().minute
        h = datetime.now().hour
        
        # メール送信は1分以上空ける
        if log_m != m:
            mail_flag = 1
            log_m = m
        
        #command.txtが見つかったら処理
        if os.path.exists(path):
            cmd = open(path).read()
            if cmd == "camera":
                camera.resolution = (960, 720)
                camera.brightness = 55
                camera.start_preview()
                # 5枚の連続写真を撮る
                for num in range(5):
                    timestr = datetime.now().strftime('%Y%m%d%H%M%S')
                    filePath = 'img/'+timestr+'.jpg'
                    filePath2 = 'img/'+timestr+'s.jpg'
                    camera.capture(filePath)
                    MyLib.xserver_upload(filePath, 'STOR /******.***/public_html/****/' + filePath)
                    print("upload image" + filePath)
                    
                    # サムネイル用の画像作成しアップロード
                    img = Image.open(filePath)
                    img_resize = img.resize((128, 96), Image.LANCZOS)
                    img_resize.save(filePath2)
                    MyLib.xserver_upload(filePath2, 'STOR /******.***/public_html/****/' + filePath2)
                    print("upload image" + filePath2)
                
                if mail_flag == 1:
                    MyLib.sendGmail("******@gmail.com", "Camera", 'https://******.****/******/'+filePath)
                    mail_flag = 0                
            elif cmd == "delete":
                MyLib.xserver_delete()
            elif cmd == "":
                print("Error")
            #command.txtを削除
            os.remove(path)
            
     

command.txtの存在を監視して、cameraと書いてあったら撮影&アップロードします
メールは1分置きとか、5枚連続で写真を撮るとか細かく調整してます

サムネイルを表示させるために画像のアップロードアドレスはpublic_html以下を指定してます
(アドレスが分かると誰でも見れてしまうので問題ですが)

こちらがライブラリMyLibのコード
GMail送信とXServerのFTPにアップロードする関数
メイン部はネットで探したものをコピペしてます

#!/usr/bin python3
# -*- coding: utf-8 -*-

import smtplib
import ftplib

from email.utils import formatdate
from email.mime.text import MIMEText

# GMailを使ってメールを送る関数
def sendGmail(to_addr, subject, body): 
	# 送信用のGMailアドレスとパスワード
    gmail_addr = "***.******@gmail.com"
    gmail_pass = "********"
    SMTP = "smtp.gmail.com"
    PORT = 587
    
    from_addr = gmail_addr

    msg = MIMEText(body, "plain", "utf-8")
    
    msg["From"] = from_addr
    msg["To"] = to_addr
    msg["Date"] = formatdate()
    msg["Subject"] = subject
    
    try:
        send = smtplib.SMTP(SMTP, PORT)
        send.ehlo()
        send.starttls()
        send.ehlo()
        send.login(gmail_addr, gmail_pass)
        send.send_message(msg)
        send.close()
    except Exception as e:
        print("except: " + str(e))
    else:
        print("Complite Send Mail")

# FTPにアップロード
def ftp_upload(hostname, username, password, upload_src_path, upload_dst_path):
    ftp = ftplib.FTP(hostname)
    ftp.set_pasv("true")
    ftp.login(username, password)
    fp = open(upload_src_path, 'rb')
    ftp.storbinary(upload_dst_path, fp)
    
    ftp.close()
    fp.close()

# XServerのサーバーパネルで設定したFTPのアドレスとUser,Passwordを設定
xserver_addr = "*****.xserver.jp"
xserver_user = "***********"
xserver_pass = "***********"

# XserverのFTPにアップロード
def xserver_upload(src_path, upload_dst_path):
    ftp_upload(xserver_addr, xserver_user, xserver_pass, src_path, upload_dst_path)


# Xserverのカメラの画像を全削除
def xserver_delete():
    ftp = ftplib.FTP(xserver_addr)
    ftp.set_pasv("true")
    ftp.login(xserver_user, xserver_pass)
    try:
    	# カメラのフォルダを指定(起点からの相対パスhttp不要)
        lst = ftp.nlst("**********/public_html/*****/*.jpg")
        for item in lst:
            print(item)
            ftp.delete(item)
    except Exception as e:
        print("except: " + str(e))
    else:
        print("Compete Delete!")
        
      
    ftp.close()
    

こちらが玄関カメラのwebページ(var/www/html/command.php)
受け取ったコマンドをそのままcommand.txtに吐き出します

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<title>Command</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<meta name="viewport" content="width=device-width">
</head>

<form name="form1" id="form1" action="command.php" method="post">

<?php
if ($_SERVER["REQUEST_METHOD"] == "GET") {
	if($_GET["user"] == "*******"){
		$filename = "./command.txt";
		$f = fopen($filename, "w") or die("失敗!");
		fwrite($f, $_GET["command"]);
		fclose($f);		
		echo $_GET["command"]."要求を受け付けました。";		
	}else{
		echo "Dont Access!";
	}
}

?>
<HR>
</form>
</body>
</html>

このPHPが吐き出したcommand.txtを元に、Pythonプログラムが特定の処理を行います
cameraだった場合はカメラ撮影を行う
deleteだったFTP上の画像を全削除する
といった感じ

これはスマホに届いたメール(アドレスは消してます)

送信用の別アカウントから送られてきます
タップすると画像が表示されるようになってます

サムネイル表示

XServerのサムネイル表示PHPにアクセスするとサムネイルを表示します
連続5枚撮影なので5枚1列で表示するようにしてます

サーバーにサムネイル表示のPHPを置きます(public_html/*****.php)
(public_html以下に撮影画像フォルダを置かないとPHPからアクセスできません)

以下がサムネイル表示PHPのコード

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<title>カメラ・サムネイル</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<meta name="viewport" content="width=device-width">
</head>

<form name="form1" id="form1">

<?php
// 当日分のサムネイルだけ表示
$text = date('Ymd');

// サムネイル画像ファイルを列挙(***s.jpg) フォルダ名は隠してます
$files = glob('./******/***/'.$text.'*s.jpg');

// 新しい画像を上に持ってくるため逆順にする
$result = array_reverse($files);

$cnt = 0;
printf('<table id="tbl1" border=1>');
foreach($result as $img){
	if($cnt % 5 == 0){
		printf('<TR>');
	}
	
	// 画像へのリンクテキスト(sを外す)
	$img2 = str_replace('s.jpg', '.jpg', $img);
	
	// 画像ファイル名をそのまま日付に変換(手抜き)
	$str = str_replace('./camera/img/','',$img2);
	$str = str_replace('.jpg','',$str);
	$str = substr_replace($str, ':', 12,0);
	$str = substr_replace($str, ':', 10,0);
	$str = substr_replace($str, ' ', 8,0);
	$str = substr_replace($str, '/', 6,0);
	$str = substr_replace($str, '/', 4,0);
	
	printf('<TD>');
	printf('<BR>%s<BR>', $str);
	printf('<a href="%s"><img src="%s"></a>', $img2, $img);	
	printf('</TD>');

	// 1行に5枚ずつ表示
	if($cnt % 5 == 4){
		printf('</TR>');
	}
	$cnt = $cnt+1;
}
printf('</table>');

?>

</form>
</body>
</html>

サムネイル画像をglobで列挙して並べ替えて表示って感じです
画像をクリックするとメイン画像(960,720)が表示されます

1つ完成

正月休みを使って、作りたかったものを1つ完成させました。
やはり達成感がありますね

アイデアを思いつくままに創作する
苦労なんか全然感じないし、やってて本当に楽しいです

さーて、次は何を作ろうかな

おすすめ

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です