カメラのモーション検知時間を記録し、アレクサでお知らせする

2階にいるとチャイムの音が聴こえにくいので、宅配便が来た事に気が付かないことがあります。玄関に人が入ってきたらアレクサにお知らせしてもらおう、という試みです。

以前から玄関フード内にカメラをセットしたラズベリーパイZEROを設置していて、インターホンの横に貼り付けておりました。カメラ付きインターホンのように使えればよいかなと思ったのですが、視野が狭く殆ど使い物になっていません。敢えて言うと冬に外に出なくても雪庇の状態を確認できるという程度です。
今回このカメラを人感センサーとして使おうと思います。

motionは動きを検知したら動画を記録するのですが、そのタイミングをトリガーにしてプログラムを動かせないかを調べたところ、motionにオプションが用意されていました。
on_event_start
https://motion-project.github.io/motion_config.html#on_event_start

動きを検知したらホームディレクトリにあるシェルスクリプトを起動するように/etc/motion/motion.confの最後に以下の一文を追加します。
on_event_start /home/pi/detect.sh

detect.shの内容は以下のようになっています。

#!/usr/bin/bash
cd /home/pi
python ./camera_detect.py

pythonプログラムcamera_detect.pyは、起動されると現在時間をファイルに記録し、ダミーセンサーを検知状態にします。アレクサで定型アクションを作成し、ダミーセンサーの検知を実行条件にしてアレクサにお知らせしてもらうように設定します。
プログラムは以下のようになっております。

camera_detect.py

from datetime import datetime
import os
import subprocess

# 連続回避
SLEEP_TIME = 300
LATEST_FILE = "./latest.txt"
# LOGファイル名
LOGFILE = '/var/www/html/sensor_log.txt'
MAX_LINE = 100

# 連続回避
def isStart(now):
  if os.path.exists(LATEST_FILE):
    f_time = os.path.getmtime(LATEST_FILE)
    expiring_time = datetime.fromtimestamp(f_time + SLEEP_TIME)
    if now > expiring_time:
      f = open(LATEST_FILE, 'w')
      f.close()
      return 1
    else:
      return 0
  else:
    f = open(LATEST_FILE, 'w')
    f.close()
    return 1

# テキスト保存
def saveText(now):
  list = []
  if os.path.exists(LOGFILE):
    f = open( LOGFILE, 'r' )
    for line in f.readlines():
      list.append(line)
    else:
      f.close()
  start = 0
  num = len(list)
  if num >= MAX_LINE:
    start = num - MAX_LINE + 1
  f = open( LOGFILE, 'w' )
  for i in range(start, num):
    f.write( list[i] )
  f.write( now.strftime('%Y/%m/%d %H:%M:%S\n') )
  f.close()

# main関数
if __name__ == '__main__':
  now = datetime.today()
  if isStart(now):
    saveText(now)
    subprocess.run('./send24.sh')

プログラムの説明
最初のnow = datetime.today()では現在日時を取得しています。この時間をイベント発生日時とします。
次のisStart()関数では以降の処理をすべきかどうか判定します。前回の検出から5分(300秒:SLEEP_TIME)経たないイベントは処理しないことにしています。空のファイルlatest.txtを作成し、そのファイルの更新時間と現在日時を比較して判定します。
処理する場合はsaveText()関数で現在日時をファイルsensor_log.txtに書き込みます。古いものは削除し、直近100回(MAX_LINE)のイベント日時を書き込みます。出力ディレクトリを/var/www/html/にしたのはこれをホームページ上で表示させるためです。

次にシェルスクリプトsend24.shを起動します。
send24.sh

#!/usr/bin/bash
python ./alexa_trigger_func.py 24

pythonプログラムalexa_trigger_func.pyを実行し、ダミーセンサーNo.24を検出状態にします。
当初、シェルスクリプトを介さずに直接subprocess.run(‘python ./alexa_trigger_func.py 24’)としていました。Windows環境では上手く動作したのですが、ラズベリーパイではエラーになってしまったので、シェルスクリプトを介して起動することにしました。
そもそも、alexa_trigger_func.pyはメインのプログラムにimportし、関数コールで実行するつもりでした。しかし、それをするとラズベリーパイZEROではちょっと困ったことになったのです。当初のalexa_trigger_funcをimportしたプログラムでは現在日時が実際に発生した日時よりも7~8秒かそれ以上遅れて記録されていました。アレクサへの通知はもっと遅れて10数秒かかっています。プログラムの1行目に日時を取得しそれを書き込んでいるのに、です。ライブラリをimportすると、プログラム実行前にimportしたライブラリとそれに関連するライブラリすべてを読み込んでしまうので、最初のステップを実行する前に数秒かかっているようでした。これをmotionの動作検知イベントで行うと録画処理と並行して実行するのでさらに数秒遅くなるようなのです。ラズベリーパイZEROはCPUの処理能力が低く、それに動画処理をさせているための問題で、通常の環境では問題にならないかもしれません。
alexa_trigger_funcのimportを止めたことにより、発生日時を取得し、ファイルに書き込む処理はそれほど時間がかからずにできるようになりました。しかし、アレクサによる通知は依然として発生から10数秒かかるままです。

これを幾分解消しようと、動画のフレームレートを15から7に落としてみました。動画処理にかかる負担を減らすことでアレクサへの通知処理を速くしようという狙いです。しかしながら結果は改善したように見えませんでした。これ以上は現状のハードウエアでは諦めるしかないかもしれません。ZERO2ならもっと速くなると推測します。いつか試してみたいものです。

alexa_trigger_func.pyはメイン起動するプログラムを関数化したものですが、メイン起動も関数コールもできるように若干の改良を行いました。
alexa_trigger_func.py

import sys
import os
import json
import datetime
from trace import Trace
import requests

client_ID = ''
client_secret = ''
refresh_token=''
token_file = './token.txt'

def alexa_trigger(no):
  sensor_name = 'dummy_sensor_' + format(no, '0>3')
  # アクセストークンが有効かどうかチェックする
  access_token = ''
  access_token_valid = False
  today = datetime.datetime.today()
  if os.path.exists(token_file):
    #ファイル有、データ読み込み
    f = open(token_file)
    data = f.read()
    f.close()
    jdata = json.loads(data)
    expires = jdata['expires_in']
    f_time = os.path.getmtime(token_file)
    file_time = datetime.datetime.fromtimestamp(f_time)
    expiring_date = datetime.datetime.fromtimestamp(f_time + expires)
    if today < expiring_date:
      # アクセストークン有効、取得
      access_token_valid = True
      access_token = jdata['access_token']

  # アクセストークンが無効の場合はoa2サーバーから取得
  if not access_token_valid:
    header_str = {
      'Content-Type': 'application/x-www-form-urlencoded;Accept-Charset=UTF-8'
    }
    body_str = {
      "grant_type": "refresh_token",
      "refresh_token": refresh_token,
      "client_id": client_ID,
      "client_secret": client_secret
    }
    response = requests.post('https://api.amazon.com/auth/o2/token', data = body_str, headers = header_str)
    if response.status_code != 200 :
      print(response.status_code)
      exit()
    jdata = json.loads(response.text)
    access_token = jdata['access_token']
    if len(access_token) > 0:
      access_token_valid = True
      # ファイルに書き込み
      f = open(token_file, 'w')
      f.write(response.text)
      f.close()

  if not access_token_valid:
    print('can not get access_token')
    exit()

  # イベントゲートウェイにイベントを送信する
  message_id = sensor_name + '_' + str(today)
  timeOfSample = today.strftime('%Y-%m-%dT%H:%M:%S.00Z')
  header_str = {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type': 'application/json'
  }
  json_str = {
    "event":{
      "header":{
        "namespace":"Alexa",
        "name":"ChangeReport",
        "messageId":message_id,
        "payloadVersion":"3"
      },
      "endpoint":{
        "scope":{
          "type":"BearerToken",
          "token":access_token
        },
        "endpointId":sensor_name
      },
      "payload":{
        "change":{
          "cause":{
            "type":"PHYSICAL_INTERACTION"
          },
          "properties":[
            {
              "namespace":"Alexa.MotionSensor",
              "name":"detectionState",
              "value":"DETECTED",
              "timeOfSample":timeOfSample,
              "uncertaintyInMilliseconds":0
            }
          ]
        }
      }
    },
    "context":{
      "properties": [
        {
          "namespace": "Alexa.EndpointHealth",
          "name": "connectivity",
          "value": {
            "value": "OK"
          },
          "timeOfSample": timeOfSample,
          "uncertaintyInMilliseconds": 0
        }
      ]
    }
  }

  response = requests.post('https://api.fe.amazonalexa.com/v3/events', json = json_str, headers = header_str)
  if response.status_code == 202:
    print('success')
  else:
    print(response)

if __name__ == '__main__':
  alexa_trigger(sys.argv[1])

最後の2行を追加しただけです。
内容の詳細については仕組みを説明した「スイッチでAlexa定型アクションを起動~導入編~(https://km2-system.com/2022/06/27/alexa_switch_1/)」、最初のバージョンについて記載した「スイッチでAlexa定型アクションを起動~スキルテスト編~(https://km2-system.com/2022/06/29/alexa_switch_4/)」、関数化させた「明日の天気によってAlexa定型アクションを起動する(https://km2-system.com/2022/08/22/alexa_switch_8/)」を参照してください。

以下は記録した日時を表示するHTMLファイルです。
sensorlog.html

<!DOCTYPE html>
<html lang='ja'>
<head>
  <meta charset='UTF-8'>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <meta http-equiv="Cache-Control" content="no-cache">
  <title>Motion sensor log</title>
  <link rel="stylesheet" href="css/style.css">
  <script type='text/javascript'>
    var req = new XMLHttpRequest();
    req.open('get', 'sensor_log.txt', true);
    req.send(null);
    var result = '';
    req.onload = function() {
      tmp = req.responseText.split('\n');
      for( var i = tmp.length - 1; i >= 0; i-- ) {
        if( tmp[i] == '' ) continue;
        result += tmp[i];
        result += '\n';
      }
      document.getElementById('text').value = result;
    }
  </script>
</head>

<body>
  <p>人感センサー</p>
  <textarea class='list-motion-sensor' id='text' title='sensorlog' cols='25' rows='20' readonly></textarea>
</body>
</html>

/var/www/html/css/style.cssには以下の5行を追加します。ファイルやフォルダがない場合は作ります。

.list-motion-sensor{
  resize: none;
  font-size:14pt;
  background: #C0C0C0;
}

sensorlog.htmlは/var/www/html/に配置します。以下が表示イメージになります。

使った感想としては、今のところ『とても良い』です。
カメラとして視野が狭く、明るさに鈍感で夜若干明かるくなっても反応しない、というのが逆にいいです。
夜、ライトをつけた車が通っても、玄関横のトイレの明かりがついても反応しないのです。
玄関フードに入ると人感センサー付きの電池式ライトが光るので反応します。
視野が狭く、郵便や新聞を配達してくれた際に手が映るか映らないか際どい感じなので反応しないときがありますが、目的としては問題ないです。
むしろもうちょっとだけ上に設置しても良いかもしれません。

ラズベリーパイ防犯カメラ ~再生秒数の長い録画だけを抽出する機能を追加~

実際に玄関まで来た録画のみをリストアップ

防犯カメラ機能を実際に使っていると家の前を数秒で通り過ぎる録画が沢山出来てしまい、玄関に訪問した録画だけを抽出する機能が欲しくなりました。

調べてみると郵便受けに投函するだけの配達は20秒以上、チャイムを鳴らす配達や訪問は1分以上かかることがわかりました。時間で絞り込めばよいのではないかと考え、機能を追加しました。

時間で録画を絞り込む機能を追加

mp4ファイルの再生時間を抽出するのに、当初はffmpegを使用することを検討していましたが、このコマンドは動画ファイルの全てのプロパティをキャラクタで出力し、そこから再生時間だけを抜き出すという処理が必要になるので、1ファイルにつき0.5秒程かかります。現行の仕組みに組み込むと画面表示にかなりの時間がかかるため、再生時間だけを出力するプログラムを自作することにしました。

解析ツールの作成に当たり、以下のURLを参考にしました。
mp4のファイル構造を解説
https://qiita.com/satken2/items/d14b4113fe3fb5f5597b
NovieファイルのAtom情報を解析
https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html
mp4 mvhd 解析
http://wikil.lwwhome.cn:28080/?p=607
mp4の情報を表示するツール Header Reader MP4
https://dev.onionsoft.net/seed/info.ax?id=1303
mp4の情報を表示するツール Mp4Reader
https://github.com/suzutsuki0220/Mp4Reader

作成したプログラムmp4lenのソースは以下のようになります。
コマンド成功で戻り値に0を返し、標準出力に再生時間をmsで返します。
コメントが少なすぎますがご容赦を
main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int cnv4b2int( unsigned char* buf );

int main(int argc, char** argv){
  int ret = 1;

  if( argc < 2 ){
    return 1;
  }
  FILE* fp  = fopen( argv[1], "rb" );
  if( fp == NULL ){
    return 1;
  }

  for( int i = 0; i < 100; i++ ){
    unsigned char typeBuff[8] = { 0 };  // size(4)type(4)のバッファ
    if( fread( typeBuff, 1, 8, fp ) < 8 ){
      break;
    }
    int chklen = cnv4b2int( &typeBuff[0] );
    if( memcmp( "moov", &typeBuff[4], 4 ) == 0 ){
      for( int j = 0; j < 100; j++){
        if( fread( typeBuff, 1, 8, fp) < 8 ){
          break;
        }
        int chklen2 = cnv4b2int( &typeBuff[0] );
        if( memcmp( "mvhd", &typeBuff[4], 4 ) == 0 ){
          if( fseek( fp, 16, SEEK_CUR ) != 0 ){
            break;
          }
          if( fread( typeBuff, 1, 4, fp ) < 4 ){
            break;
          }
          printf( "%u\n", cnv4b2int( typeBuff ) );
          ret = 0;
          break;
        }
        if( fseek( fp, chklen2 - 8, SEEK_CUR ) != 0 ){
          break;
        }
      }
      break;
    } else {
      if( fseek( fp, chklen - 8, SEEK_CUR ) != 0 ){
        break;
      }
    }
  }
  fclose( fp );
  return ret;
}

int cnv4b2int( unsigned char* buf ){
  return (buf[0]*0x1000000 + buf[1]*0x10000 + buf[2]*0x100 + buf[3]);
}

mp4lenプログラムはビルドして以下の圧縮ファイルに同梱していますが、自分でビルドする場合は以下のコマンドを実行します。
$gcc main.cpp -o mp4len
実行時間は15msほどになりました。60ファイルで1秒ほどかかりちょっと時間がかかりますが、大分改善されました。

ftpアプリを使い、更新した新しいhtmlファイルの一式を/var/www/htmlに上書き転送します。
※これまでのファイルは削除しても構いません。
※「カメラ用ラズパイ・サーバーセットアップ」を参照

アップロードしたmp4lenプログラムに実行権限を付与します。
$chmod a+x /var/www/html/mp4len

これで時間で絞り込む機能が追加されます。
※ボタンがうまく表示されない場合は、chromiumブラウザのキャッシュをクリアします。

絞り込み時間を変更する場合は/func/config.iniの項目を変更します。
middle=20
long=60

ラズベリーパイ防犯カメラ ~CLIモードセットアップメモ~

忘備録として

カメラ用ラズパイのセットアップはSSHを経由してセットアップする前提で記載しましたが、モニタやキーボードを直接ラズベリーパイに接続して操作するためのセットアップを忘備録として記載します。

モニタの解像度変更

$sudo raspi-config⏎

2 Display Options – D1 Resolutionを選択します。

解像度を選択します。※CEAはテレビ出力、DMTはPC用モニタとなります。

設定後、<Finish>を選択しコンフィグを終了させ、リブート確認画面で<Yes>を選択すると再起動し、解像度が変更されます。

この設定は/boot/config.txtを直接変更することでも変更できます。

#framebuffer_width=xxxxx
#framebuffer_height=xxxxx
の行のコメント#を削除し以下のように編集します。
framebuffer_width=640
framebuffer_height=480

もしくはは
#hdmi_group=x
#hdmi_mode=xx
を以下のように編集します
hdmi_group=2
hdmi_mode=4

※hdmi_groupの説明
0 自動検出
1 CEA
2 DMT

hdmi_modeは下記URLを参照して設定します。hdmi_grouppで変わるので注意。
https://www.raspberrypi.com/documentation/computers/config_txt.html#hdmi-mode

日本語キーボードの設定

デフォルトの設定ではShuft+;で”+”などの文字が打てなくなるので日本語キーボードを設定します。

$sudo raspi-config

5 Location Options – L3 Keybordを選択します。

Generic 105-key PC (intl.)を選択<OK>
Otherを選択<OK>
Japaneseを選択<OK>
Japanese -Japanese (OADG 109A)を選択<OK>
The Default for the keyboard layoutを選択<OK>
No compose keyを選択<OK>

日本語の文字化けが気になる場合

$sudo raspi-config

5 Location Options – L1 Localeを選択します。

ja_JP.UTF-8 UTF-8を選択後、ja_JP.UTF-8ではなく、en_GB.UTF8を選択する。
これでメニューが英語になり、日本語表示しなくなるので文字化けが無くなります。

■日本語表示と日本語入力の設定

日本語表示させたい場合は以下の設定を行います。

日本語フォントのインストール
notoフォント
$sudo apt install fonts-noto

日本語入力メソッドをインストール
$sudo apt install uim uim-anthy

日本語対応ターミナルをインストール
$sudo apt install jfbterm

ローカルコンソールでの”jfbterm”の起動を自動化
$sudo nano .bashrc

 If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
linux)					@@行追加@@
    if [ -c /dev/fb0 ]; then		@@行追加@@
        jfbterm -q -e uim-fep -u anthy	@@行追加@@
        exit				@@行追加@@
    fi					@@行追加@@
    ;;					@@行追加@@
*)
    ;;
esac

「CTRLキー」+「スペースキー」で日本語入力モードに切り替える設定
$sudo nano /usr/share/uim/generic-key-custom.scm

(define-custom 'generic-on-key '("zenkaku-hankaku" "<Control> ")	@@ShiftをControlに変更@@
               '(global-keys1)
               '(key)
               (N_ "[Global] on")
               (N_ "long description will be here"))

(define-custom 'generic-off-key '("zenkaku-hankaku" "<Control> ")	@@ShiftをControlに変更@@
               '(global-keys1)
               '(key)
               (N_ "[Global] off")
               (N_ "long description will be here"))

ラズベリーパイ防犯カメラ ~ブラウザ用ラズパイが止まる?~

chromiumの不具合?

ブラウザ用ラズパイをセットアップしなおしたところ、起動して暫くすると現在の画面が更新されずに停止してしまいます。

chromiumの設定をいろいろ変更しても変わりませんでした。

ソフトウエアのアップデートをスキップしたところ、これまで通り止まらずに動作するようになりました。

最新のchromiumブラウザに何か問題があるのかもしれません。

(2022/6/11追記)
セットアップしなおしたところ、現行では解決しているようです。
また、Raspbery Pi Imagerは最新のOSイメージがあればダウンロードしてそれを使うと思っていたのですが、最新のOSイメージを使う場合は最新のRaspberry Pi Imagerをインストールしなおさなければならないようです。

ラズベリーパイ防犯カメラ ~防犯カメラの設置工事~

防犯カメラの設置

家が古く、カメラ付きのインターホンシステムがないので、作成した防犯カメラシステムを実際に設置してみました。雑な仕事を晒してみます。

カメラです。結束バンドで強引に縛っています。ラズベリーパイは中にあり、USB延長ケーブで繋がっています。新聞受けの上側にドリルで穴をあけて線を通しました。

裏側です。最初上側にドリルで穴をあけていましたが、ガラスにぶち当たってしましました。下側にあったキャップを外すと穴があったのでこちらを利用して線を通しました。USB延長ケーブルを途中で切って穴を通し、4本の線を繋ぎなおしています。

USBケーブルを2本通しましたが、1本はインターホン横にカメラ付きラズベリーパイゼロを付けました。ゼロはCPUパワーに乏しいので映像のサイズを320*240にしました。しかし、カメラの画角が狭く実用に耐えないので動作はしていますが、ほとんど使っていません。

ラズベリーパイ本体は高いところに設置しました。

電源は玄関横のトイレにコンセントが空いていたのでそこを借りました。壁に穴を開けました。トイレ側と玄関側、両側の位置をちゃんと合わせなければなりません。キャップのようなものを買いました。径が記載されていましたが内径で、間違ってドリルを2本買う羽目になりました。これを通す穴は13.5mmでした。延長コードを抜き差ししてラズベリーパイをリセットします。

コードは配線カバーを使い隠しましたがちょっと雑です。が、あまり見るところではないので気にならなくなりました。

ラズベリーパイ防犯カメラ ~ブラウザ用ラズパイ・セットアップ

ブラウザラズパイOSのインストールと設定

ブラウザのラズベリーパイはGUIのOSをインストールし、起動時Webブラウザを全画面で開くようにします。

カメララズパイで使用したイメージャを起動します。

Operating Systemで推奨(Recommanded)となっているRaspberry Pi OS(32bit)を選択します。


SD Cardを選択します。

WRITEを押して書き込みを開始します。

GUI環境のためにダウンロード量や書き込み量が大きいためか45分ほどかかりました。

書き終わったSDカードをブラウザラズベリーパイに挿して起動し、初期設定を行います。

電源を入れるとデスクトップ画面が立ち上がります。 ようこそダイアログが起動しています。Nextを押してセットアップ画面を表示します。

Set Country画面ではJapanを選択して’Next’を押下します。


パスワードを変更し、’Next’を押下します。

ディスプレイの端が黒い余白がある場合はチェックを入れます。’Next’を押下します。

接続するWifiルータを選択します。’Next’を押下します。

パスワードを入力し、’Next’を押下します。

ソフトをアップデートしますかと聞かれます。’Next’を押下します。

アップデートが始まります。

50分ほどかかりました。’OK’を押下します。

セットアップ完了画面で’Restart’を押下し、再起動します。

SSHを有効にします。
左上のラズベリーパイマークから[設定]-[Raspberry Piの設定]を選択します。
Raspberry Piの設定画面で[インターフェース]タブを開きます。
SSHを有効にして[OK]を押します。

画面の解像度を800*600に設定します。
左上のラズベリーパイマークから[設定]-[Screen Configration]を選択します。
Screen Layout Editor画面でメインメニューから[Configure]-[Screens]-[HDMI-1]-[解像度]-[800×600]を選択します。
メインメニューから[Configure]-[適用]を選択します 。

‘OK’を押して解像度を確定します。

ブラウザをOS起動時に全画面モードに開くようにします。
ターミナルを開き、/home/pi/.config/lxsession/LXDE-pi/autostartファイルを開きます。
無い場合は作成します。ディレクトリから作成が必要かもしれません。
$mkdir /home/pi/.config/lxsession
$mkdir /home/pi/.config/lxsession/LXDE-pi
$sudo nano /home/pi/.config/lxsession/LXDE-pi/autostart
autostartファイルを以下のように編集します。

@lxpanel --profile LXDE-pi
@pcmanfm --desktop --profile LXDE-pi
@xset s off
@xset -dpms
@xset s noblank
@chromium-browser --kiosk http://192.168.XXX.XXX

アドレスのXXXのところはカメラ用ラズパイのアドレスにしてください。
chromium-browser のところはchromiumかもしれません。
[左上ラズベリーパイマーク]-[インターネット]-[Chromium]で右クリックしてpropetyを開き、[デスクトップエントリ]タブのコマンドから確認してください。

ファイルを新しく作成してタイピングするのが面倒な場合は、windowsで以下のテキストを貼り付けてファイルを作成し、ftpで転送すると良いかもしれません。

Ctrl + O⏎で保存し、Ctrl + Xでnanoを終了します。
再起動すれば起動時に全画面で防犯カメラアプリが起動します。

ラズベリーパイ防犯カメラ ~カメラ用ラズパイ・サーバーセットアップ~

ブラウザでアドレスhttp://xxx.xxx.xxx.xxx:8081を使うだけでも防犯カメラとしての用は足りますが、過去映像を見るためにftpを使用しなければならないため、面倒です。

これを解消するためにHTTPサーバーを使ってブラウザ上でファイルを選べるようにします。

apacheとPHPをインストールします。

$sudo apt install apache2⏎
続行しますか?[Y/n]と聞いてきたらY⏎
$sudo apt install libapache2-mod-php⏎
続行しますか?[Y/n]と聞いてきたらY⏎
$sudo apt install php⏎
続行しますか?[Y/n]と聞いてきたらY⏎
$sudo apt install php-mbstring⏎
続行しますか?[Y/n]と聞いてきたらY⏎
インストールは5分ほどで完了しました。

この時点でブラウザのラズベリーパイのアドレスを開くと以下のようなページを表示します。

ftpでファイルを転送するためにフォルダの権限を変更します。
$sudo chmod 777 /var/www/html

HTMLファイルをダウンロードし、解凍します。

ftpアプリを使い、ファイル一式を/var/www/html/に転送します。

アパッチのコンフィグファイルを変更し、エイリアスを設定します。
$sudo nano /etc/apache2/apache2.conf

<Directory>セクションが続く箇所に以下の行を追加

Alias /mp4/ /var/lib/motion/
<Directory /var/lib/motion/>
Require all granted
</Directory>

保存はCtrl + O⏎
終了はCtrl+ X⏎

/var/lib/motionディレクトリにアクセス権限追加
$sudo chmod 777 /var/lib/motion

アパッチ再起動
$sudo service apache2 restart⏎

再度ラズベリーパイのアドレスを開くと以下のようなページを表示します。
TOPページは現在のカメラ映像を表示しています。
左側の『今日』、『昨日』、『2日前』ボタンを押すと録画した映像ファイルを新しい順に1ページ5ファイルずつ表示します。
『新』、『前』ボタンでページを移動できます。
その下にはページの一番新しい映像の時刻を表示しており、クリックしてそのページに移動できます。

画像の▶ボタンを押下して映像を再生させると、怪しい男がゴミ出しから帰ってくるところが再生されました。
※一番最近の映像はmotionプログラムが記録中で再生できない場合があります。1~2分経てば再生できます。
このページはhtmlとphpプログラムから構成されています。
気に入らない部分は変更することによって自分の好みに変えることができます。

このページの設定ファイルを説明します。

/func/confiog.ini

width=640                              映像の解像度 幅

height=480                             映像の解像度 高さ

numperrow=13                      映像一覧の1行の個数

vnumperpage=5                    映像の1ページの個数

ラズベリーパイ防犯カメラ ~カメラ用ラズパイ・motionセットアップ~

ラスベリーパイのカメラアプリケーションmotionのインストールとセットアップを行います。
motionはリアル映像を配信しつつ、動きに変化があれば動画を保存してくれる、優れたアプリケーションです。

motionのインストールと設定

パッケージリストを新しくします
$sudo apt update⏎
パッケージを新しくします
$sudo apt upgrade⏎
続行しますか?[Y/n]と聞いてきたらY⏎
updateは1~2分、upgradeは10分弱でした。

カメラアプリmotionをインストールします。
$sudo apt install motion⏎
続行しますか?[Y/n]と聞いてきたらY⏎
5分ほどでインストールは完了しました。

エディタnanoでコンフィグファイルを編集し、motionの設定をします。デフォルトから変更するものと主なものについて記載します。
$sudo nano /etc/motion/motion.conf⏎

——————————————————————————————————————————

daemon on デーモン(バックグラウンド処理)として起動します。
※最終的にはonにしますが動作確認時はoffにした方がやりやすいかもです。
width 640 映像の解像度 幅
height 480 映像度の解像度 高さ
framerate 15 映像のフレームレートの最大値
threshold 1500 変化があったピクセル数の閾値。これを超えると変化があったとみなす
pre_capture 30 動体検知をする前の映像をどれだけ何フレームバッファリングするか?
post_capture 15 動体検知をする後の映像をどれだけ何フレームバッファリングするか?
event_gap 60 動体検知が終わってからevent_gap秒間以内に動体検知したら同じ映像ファイルにまとめる
movie_max_time 120 1つの映像ファイルの最大秒数
movie_output on 映像ファイルを保存するかどうか
movie_codec mp4 映像ファイルの種類
text_scale 2 映像に出力される文字を大きくする
movie_filename %Y%m%d-%H%M%S 映像ファイルは日付と時刻の名前になります
stream_motion on 映像の配信を行うかどうか?
stream_maxrate 15 映像配信の最大フレームレート
stream_localhost off 映像配信はローカルのみに限定するかどうか

——————————————————————————————————————————

(2022/7/29追記)
motionのバージョンアップにより項目名や設定内容などが変更されている場合があります
以下のドキュメントを参照
https://motion-project.github.io/motion_config.html

設定を終えたらCtrl+O⏎で書き込み、Ctrl+Xで終了です。

USBカメラをラズベリーパイに挿し、motionを起動します。
$sudo motion⏎

PCのブラウザでmotionが正常に動作していることを確認します。(xxx.xxx.xxx.xxxはカメラ・ラズベリーパイのIPアドレス)
http://xxx.xxx.xxx.xxx:8081

deamon onにしている場合のmotion停止方法
$sudo service motion stop⏎

deamon offにしている場合のmotion停止方法
Ctrl + C

調整が終わったらmotion.confでdeamon onになっていることを確認します。
ラズベリーパイが起動時に自動的にmotionが起動するようにします。
$sudo nano /etc/rc.local⏎

exit 0 より上に以下の1行を追加
motion &

Ctrl+O⏎で書き込み、Ctrl+Xで終了です。

再起動します。
$sudo reboot⏎

FTPソフトのインストールと動作確認

FTPソフトを使用してmotionが保存した映像ファイルをPCにダウンロードし、映像を確認します。

FTPソフトを新しくインストールする場合はWinSCPをお勧めします。
インストール方法や操作方法は下記公式サイトが詳しいです
https://winscp.net/eng/docs/lang:jp

motionの映像ファイルは以下のフォルダに格納されています。
/var/lib/motion/
正しく設定されていればmotion.configに従って作成された映像ファイル(yyyymmdd-HHMMSS.mp4)が作成されているはずです

10~15分で動作確認できると思います

crontabの設定

motionで映像ファイルを保存する設定を行いましたがこのまま運用していくと映像ファイルがたまる一方で最終的にはSDカードに空きが無くなります。
そのような事態を避けるために定期的にファイルを削除するようにします。
スケジューラーにcrontabを使用します。
【基本的書式】
分 時 日 月 曜日 実行コマンドorスクリプト

これを使い、毎晩3時に10日以上経ったファイルを削除するようにします。
$crontab -e⏎
エディタを選択するように促されます。1のnanoを選択します。
Choose 1-3 [1]: 1⏎

↓キーで最終行まで移動し、以下の1行を追加します。
00 3 * * * sudo find /var/lib/motion -name ‘*.mp4′ -mtime +10 -delete⏎

スペースを開けることに注意してください。 00△3△*△*△*△sudo△find…
保存し、終了します
Ctrl + O⏎
Ctrl + X⏎

設定を確認します。
$crontab -l⏎

5分もかからないでしょう

ラズベリーパイ防犯カメラ ~カメラ用ラズパイ・OSセットアップ~

ラズベリーパイのセットアップ方法について記載します。

セットアップ手順

5部構成でセットアップ方法を記載します。
ラズパイOSのインストールは沢山のページで説明されていますし、motionについても同様ですが、あちこち見るのが面倒の方のために記載します。
ネットワーク環境等により大きく左右されると思いますが、何かの参考になるかと思い、セットアップにかかつた時間も記載したいと思います。
1.カメララズパイOSのインストール・・・約30分
2.SSHでの接続確認・・・約15分
3.ラズベリーパイのアドレス固定化・・・約15分
4.motionのインストールと設定・・・約30分
5.FTPソフトのインストールと動作確認・・・約10分、+こころゆくまで調整
6.crontabの設定・・・約5分
7.apache、PHPのインストールと設定・・・約10分、+こころゆくまで調整
8.ブラウザラズパイOSのインストールと設定・・・約2時間、+こころゆくまで調整

カメラ・ラズパイOSのインストール

  • ラズベリーパイのOSのインストールはとても簡単です。
  • 1.ラズベリー財団のHPを開きます。
https://www.raspberrypi.org/
  • 2.Softwareを選択します。
  • 3.ラズベリーパイイメージ書き込みツールを使ってラズベリーパイOSをインストールします。

Install Raspberry Pi OS using Raspberry Pi Imager項目の[Download for Windows]ボタンを押し、ラズベリーパイイメージ書き込みツールをダウンロードします。
数秒でダウンロードが終わりました。

ダウンロードしたファイル(imager_1.4.exe)を起動するとインストーラが起動します。
Installボタンを押下してインストールを行います。
インストールはすぐに完了します。

インストールが完了するとイメージ書き込みツールがPCにインストールされ、ツールが起動します。
このツールはフォーマットもOSのインストールも全部やってくれます。

GUIは不要なのでCUIだけのLite版をインストールします。推奨版やFullを選択しても構いませんが容量が大きいので時間がかかり、OSが占める容量も増えます。
Operating System[CHOOSE OS]を押下し、Raspbery Pi OS(other)からRaspberry Pi OS Liteを選択します。

SD Card[CHOOSE SD CARD]押下し、インストールするSDカードを選択します。

WRITEボタンが有効になるので押下します。イメージ書き込みを開始します。

私の環境では7~8分でインストールが完了しました。

書き終わったSDカードをラズベリーパイに挿入して起動し、初期設定を行います。
login idはpi
passwordはraspberryでログインします。
ログインしたらraspi-configを起動します。

$sudo raspi-config⏎

1 System Options – S3 Passwordを選びパスワードを変更します。
デフォルトのraspberryのままで良い場合は設定する必要はありません。
1 System Options – S1 Wireless LANを選択し、Wifiを設定します。
国を選択 JP Japan ↑↓キーで選択、TabキーでOKに合わせ⏎
SSIDをキー入力⏎
暗証番号を入力⏎
5 Localization Optionsを選択
L1 Locale でja_JP.UTF-8 UTF-8を選択(↑↓キーで位置合わせ、スペースキーで*を付け選択、TabキーでOKに合わせ⏎)デフォルトロケールにja_JP.UTF-8を選択(↑↓キーで選択、TabキーでOKに合わせ⏎)
L2 TimezoneでAsia(↑↓キーで選択、TabキーでOKに合わせ⏎)-Tokyo(↑↓キーで選択、TabキーでOKに合わせ⏎)を選択
L4 WLAN CountryでJP Japanを選択(↑↓キーで選択、TabキーでOKに合わせ⏎)
3 Inerface Optionsを選択
P1 CameraとP2 SSHでを選択し、enable(有効)にします。
6 Advanced Optionsを選択
A1 Expand FileSystemを選択(パーテンションを拡張)
をします。Tabキーで<Finish>を選択し ⏎ キーでコンフィグを終了させようとすると、リブートするかどうか確認されます。<Yes>を選択し、 ⏎
ラズベリーパイが再起動しますので、idとパスワードを入力し、ログインします。

ネットワークアドレスをメモします。
$ifconfig⏎
IPアドレスとMACアドレスをメモします。
以降はSSHで接続できるので、ラズベリーパイにつなげたモニタとキーボードは不要になります。
1~6までネットワークに一発で繋がれば、10~15分くらいでしょうか。

TeraTermでSSH接続確認

SSHで接続するソフトを使えば、ラズベリーパイにモニタとキーボードを接続しなくても、パソコンでラズベリーパイの操作ができるようになります。
ターミナルソフトがPCにインストールしていない場合は、定番ソフトのTera Termのインストールをお勧めします。
“TeraTerm”と検索すれば一番上に窓の杜のページが現れますので、そこからダウンロードし、インストールします。
TeraTermが起動したらTCP/IPを選択し、ホスト(T)にIPアドレスを入力します。
サービスにSSHを選択し、[OK]ボタンを押下します。
画面が変わり、ユーザ名(pi)とパスフレーズ(設定パスワード)がを入力し、[OK]ボタンを押すとターミナルが使用できるようになります。
最初はセキュリティ警告が出ますが、アドレスが間違いなければ問題ありません、[続行]を押してください。
[設定]-[端末]で文字コードがUTF-8になっていることを確認してください。
インストール込みで10分~15分。

ラズベリーパイのアドレスの固定化

ラズベリーパイのアドレスを固定化し、サーバーとして使いやすくします。
ラズベリーパイのアドレスの固定化は、ルーターのDHCPの割り当て設定を変更することで固定アドレス化できます。
ルーターやゲートウエイの機種によってやり方は異なると思いますが、バッファローのルーターWSR-1166DHPL2の例で説明します。
ルーターにログインし、[詳細設定]-[LAN]-[DHCPリース]と選択肢、接続しているIPアドレスの一覧を表示します。
ラズベリーパイに該当するアドレスに対して「手動割り当てに変更」ボタンを押すと、固定化されます。
覚えやすいアドレスに変更する場合は、手動割り当てとなったアドレスに対して、「修正」ボタンを押下します。修正したいアドレスを入力後、「修正保存」ボタンを押して修正完了です。

ターミナルソフトからラズベリーパイを再起動します。
$sudo shutdown -r now⏎
※shutdown 再起動しない場合のオプションは-hになります。
シャットダウンするとターミナルソフトは勝手に閉じられます。
30秒くらい経って再起動する頃合いにターミナルソフトを起動し、手動で割り当てた固定アドレスに対して再度SSHで接続します。
接続出来たら固定アドレス化が完了です。
上手くいけば15分ほど。

次はカメラアプリケーションmotionのセットアップからです。

ラズベリーパイ防犯カメラ ~システム構成と用意するもの~

ラズベリーパイで防犯カメラシステムを作成する手順を紹介します。

システム構成

ラズベリーパイ2台構成になっています。
1台はカメラに接続し、映像を配信します。もう1台はWebブラウザで映像を表示します。
ブラウザ用ラズパイはPCやスマホ、タブレットでも表示できるので、必ずしも必要ではありませんが、専用機としてセットアップしています。
カメラ用ラズパイは3つのアプリ構成になっています。
[motion]カメラ制御、ストリーム配信、ビデオファイル録画保存、これらは全てmotionがやってくれています。
[crontab]motionで録画したファイルはたまる一方なので定期的に古いファイルを削除します。
[apache2]WebサーバーapacheとPHPを使って現在と過去の映像を提供します。
開発に使うPCはWindowsを前提としています。
ネットワークはルータに接続することを前提にしています。

用意するもの

カメラ用ラズベリーパイ

  • ラズベリーパイ本体

カメラ用のラズベリーパイはZERO~最新の4までどれでも使用可能です。
ただ、3以上でないと640*480以上の解像度で滑らかな映像をストリーミング&録画するのは難しいようです。

  • ラズベリーパイ用ACアダプタ

スイッチサイエンスから専用のものが販売されており、スイッチサイエンスのサイトやアマゾンからでも買うことが可能です。

スマホのUSB充電器を利用することができるので以下の条件を満たしているものがあればわざわざ買う必要はありません。充電器からラズベリーパイ迄のケーブルも100円ショップで簡単に購入できるでしょう。

機種名電源供給コネクタ最大消費電流推奨電源
Raspberry Pi 3 Model B+micro USB1.13A5V 2.5A
Raspberry Pi 4 Model BUSB Type- C?5V 3A
Raspberry Pi Zero W(WH) micro USBmicro USB150mA5V 1
  • ラズベリーパイ用ケース

ケースは公式のものをはじめ沢山のメーカーから出ています。公式のものは安くて良いかもしれません。スイッチサイエンスから購入できます。
ケース選択のポイントが幾つかあると思います。
1.GPIOは使用しないので穴は不要。
2.何処にセットするか?壁掛けにする場合、その穴はあるか?公式ケースには壁掛け用の穴はついていないように見えます。
3.ヒートシンク、冷却ファンは必要か?
ラズベリーパイ3や4になるとケースにヒートシンクやファンが付いているものが多いです。
しかしラズベリーパイ公式のケースには付いていません。
防犯カメラは常時フルパワーで動作しないので必要ではないかもしれません。
私はカメラ用にはラズベリーパイ3B+を使用し、ケースはアルミのものを使用しています。
アルミのケース自体がヒートシンクになっているタイプで、ラズパイとは0.5mmの熱伝導シートを挟んでおります。
YouTube等の動画をフルスクリーンで再生すると確かにケースが温かくなりヒートシンクの効果を実感しましたが、カメラ用途ではそれほど負荷が高くないため、不要かもしれないと感じています。
別の懸念としてアルミのケースはwi-fiの電波を阻害するのではという心配があります。
私の環境では問題なく通信できていますが、電波状態が悪いところではアルミのケースが通信を難しくしているケースもあるかもしれません。
ラズベリーパイZEROは、公式ケースで使用してもそれほど温かくならないのでヒートシンクは不要と思います。
4.本体とケースは一致しているか?
普通はまず間違えないと思いますが、アマゾンのサイトで検索すると違うものが検索結果に表れることがありますし、関連する商品が一致しないものである可能性も高いです。
購入する際に注意する必要があります。私は間違えて余計なものを買いました。

  • USBカメラ

USBカメラは手持ちの安いもので問題ないと考えていましたが、実際に使ってみると視野が狭いことに気が付きました。
画角60°では写したいところをカバーできないことが多いです。
アマゾンで2000円以下のものはスペック表記がどうあれ、画角60°超のものはないと考えてよいと思います。
ラズパイ用のカメラも悪くないと思いますが、本体とカメラは近くなります。
玄関を写す場合、ラズパイ本体を外に出すのは盗まれる可能性もあるため、ラズパイ本体は内、カメラだけ外という構成にしたいのです。
このため、USBカメラという選択になりました。

  • micro SDHC

16GB以上class10以上のSDカードなら問題ないと思います。音楽用のclass6のカードだと読み込みスピードが足りなくてOSが起動できませんので注意が必要です。筆者はアマゾンで700円前後の32GB class10のものを使用しています。このカードに動画ファイルも保存していますが、容量は10日間で2GB強程度になっています。動きに変化があったときだけ記録されますが、玄関前を通る車や歩行者にも反応していますので、ほんの数秒のものが殆どで1日200ファイル程です。解像度は640*480、15fpsという設定です。切れ目なく往来がある表通りでなければ32GB以上の容量は不要かと思います。

  • キーボード

1000円程度の有線のものを使用していますが、USBに接続する無線のものでも使えるかもしれません。

  • マウス

カメラ用はGUI環境を使う必要はないのでマウスは必要ではありません。ブラウザ用はタッチパネルでなければ必要です。USBの有線のものを新たに用意しましたが、家にあった安価な無線のものでも問題なく使えました。

  • HDMI用モニタ、ケーブル

最初のセットアップの時に必ず必要になります。カメラ用はセットアップが終わればターミナルソフトを使い無線で操作できるので、最悪HDMI入力のあるテレビがあれば新たに用意する必要はないと思います。
ラズベリーパイの種類によってHDMI端子の形状が異なるのでケーブル購入の際は注意が必要です。

機種名HDMI形状
Raspberry Pi 3 Model B+タイプA(一般的なHDMI)
Raspberry Pi 4 Model BタイプD(HDMI-micro)
Raspberry Pi Zero W(WH)タイプC(HDMI-mini)

HDMIケーブルはUSBケーブルに比べ高価ですが、安いものだと堅いものが多く、軽いラズパイに接続すると納まりが悪い場合があります。変換コネクタを使う選択肢もありますがラズベリーパイ4の場合、電源供給用のUSB Type-Cコネクタと差込口が非常に近く、変換コネクタの形状によっては挿しにくくなるので注意が必要です。

  • 購入費用

アマゾンで購入した金額。参考程度に。(2020/11頃)

ラズベリーパイ3 Model B+¥4,990
ラズベリーパイケース¥1,600
熱伝導シート¥820
micro SDHC¥720
ACアダプタ¥1,430
広角USBカメラ(BAFFALO)¥2,980
¥12,540

ブラウザ用ラズベリーパイ

  • ラズベリーパイ本体

ラズベリーパイZEROでX-windowはさすがにちょっと重いようです。3か4を使用したほうが無難です。

  • ラズベリーパイ用ケース

私は4を使用し、ヒートシンク、ファン付きのケースを使用しました。ファンが回っていると全く熱さを感じません。今回の用途にファンが必要なのかの検証はしていません。

  • 10インチモニタ

見やすさのために10インチモニタを選びました。
タッチパネルにするという選択肢があると思いますが、いずれにせよ10インチモニタは意外と選択肢がないです。
映像が640*480であるため、モニタ解像度を800*600にしてHTMLはそれに最適化させています。

購入費用

アマゾンで購入した金額。参考程度に。

ラズベリーパイ4 Model B¥6,875
ラズベリーパイケース(ACアダプタ付き)¥2,099
10インチモニタ¥9,860
micro SDHC¥720
Micro HDMI to HDMIケーブル1m¥699
¥20,253

次の章ではセットアップ手順について記載します。