環境監視システム自作 Webサーバー(RasPi+Python)

環境(温湿度)データをリアルタイムにWeb配信するためのサーバーを、Raspberry Pi3 とPython3 により構築しました。

1.システムの概要

・Raspberry Pi3 OS:Raspbian Jessie
・CGI(Web)サーバースクリプト言語:Python3.4.2
・環境データ受信(デーモン起動):Python3.4.2

・ディレクトリ構成
ディレクトリ構成

2.Python によるCGI(Web)サーバー立ち上げ

➀ CGIサーバー起動用スクリプトの作成
/home/pi に web フォルダを作成し、その中に次のファイルを作成しました。
cgiserver.py

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

from http.server import HTTPServer, CGIHTTPRequestHandler
import os
os.chdir('/home/pi/web')  # カレントディレクトリを/home/pi/webとする。デーモン起動時に必要

host = ''     # RasPiのIPアドレスは書かなくても良い
port = 8000
httpd = HTTPServer((host, port), CGIHTTPRequestHandler)
print('serving at port', port)
httpd.serve_forever()

➁ コマンドラインからの起動テスト

 $ python3 cgiserver.py

➂ デーモンとして起動
次のデーモン起動ファイルを/etc/systemd/systemのディレクトリに作成しました。
cgid.service

[Unit]
Description = CGI-Server

[Service]
ExecStart=/home/pi/web/cgiserver.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

パーミッションを次のように変更しました。

 $ sudo chmod 644 cgid.service

当初755としていたらエラーで実行権限を禁止するよう指示が出ました。変更したら問題なく動作するようになりました。

デーモンの登録およびスタートを行えばすぐにデーモンとして起動します。また、次回のRasPi起動時にも自動的に起動されるようになります。
デーモン登録関連のコマンドを次に示します。

 デーモンの登録		$ sudo systemctl enable cgid
 デーモンの削除		$ sudo systemctl disable cgid
 デーモンをスタート	$ sudo systemctl start cgid
 デーモンを停止		$ sudo systemctl stop cgid
 デーモンの再読み込み	$ sudo systemctl daemon-reload
 状態確認           	$ systemctl list-unit-files
 			$ systemctl status
 			$ systemctl status cgid

最初なかなかうまく動作しませんでしたが、ネット情報、コンソールのメッセージ、 ログ /var/log/syslog を見て何とか問題解決ができました。

ここまでに参考にさせて頂いた主要サイト
 http://blog.sarabande.jp/post/81479479934
 http://dreamerdream.hateblo.jp/entry/2016/01/08/000000

➃ 動作確認1 Hello World !
最初に簡単なHTMLファイルを作成しWebサーバーの動作を確認しました。次のファイルを作成し、/homo/pi/web のホルダに保存しました。
index.html

<html>
<body>
Hello World !
</body>
<html>

ブラウザで動作を確かめました。

URL:http://localhost:8000/
または http://192.168.xxx.xxx:8000/

➄ 動作確認2 PythonによるCGIスクリプト
現在の日時を表示するスクリプトを /home/pi/cgi-bin フォルダの中に作成し、CGIの動作を確認しました。
test.py

#!/usr/bin/env python3

import datetime

#フォーマット文字列の作成
html_body = """
<html><body>
{0.year:d}/{0.month:d}/{0.day:d} {0.hour:d}:{0.minute:d}:{0.second:d}</body></html>"""

now=datetime.datetime.now()

print("Content-type: text/html\n")
print(html_body.format(now))

ブラウザで動作を確かめました。

URL:http://localhost:8000/cgi-bin/test.py
または http://192.168.xxx.xxx:8000/cgi-bin/test.py

このスクリプトは次のサイトで紹介されていたものを使わせて頂きました。

http://coreblog.org/ats/stuff/minpy_web/03/02.html

3.親機2からの環境データ受け取りと一時保存

環境データは親機2から約10秒ごとに送られてくるのに対して、CGIスクリプトはブラウザからの要求のタイミングで動作することになります。ブラウザから要求されたときに最新のデータを送信するためには、データ保存場所を用意しマイコンからの送信タイミングでデータが常に更新されるようにしておかなければなりません。この一時的な保存場所としてファイルを使うことにしました。
親機2から送られてくるデータのシリアル受信とデータを一時保存するためのスクリプトをPythonにより作成し、デーモンとして常駐させるようにしました。このスクリプトを /home/pi/web/cgi-bin フォルダに作成しました。
dat_rec.py

#!/usr/bin/env python3
# PICからシリアルで送られてくる全センサーのデータを一時保存

import serial

str=[""] * 10                # センサー10個分のバッファ

con=serial.Serial('/dev/ttyUSB0', 9600, timeout=15) # USB-Serialを使用
print (con.portstr)          # comポート コネクション表示

t15 = 0                      # データ受信 失敗判断用カウンタ
while 1:
    s=con.readline()         # デリミタまで1行受信
    if s == b'':             # timeout なら
        if t15 < 40:
            t15 += 1
            print("データ受信失敗 %3d[s]" % (t15 * 15))
        else:                # 受信失敗が10分経過したら
            t15 = 0
            f = open("/home/pi/web/cgi-bin/env_dat.txt", "w", encoding="utf-8")
            f.write("")      # 一時保存ファイルを空に
            f.close()        
        continue

    # データ受信時の処理
#   print (s)
    cs = s.decode('utf-8')   # 1行のデータを整形(デリミタ削除)
    try:
        n = int(cs)          # 有効データ数(センサ数)を数値に変換
    except:
#       print("数値変換エラー")
        continue             # 変換に失敗なら処理ループの先頭に戻る

    # 有効データ数キャッチ後の処理(データブロックの先頭)
    i = 0
    while i < n:             # 受信データの判定ループ
        s=con.readline()     # 1行(1センサー分のデータ)読み込み
#       print (s)
        cs = s.decode('utf-8') # データを整形(デリミタ削除)
        str[i] = cs          # バッファに格納
        i += 1
    f = open("/home/pi/web/cgi-bin/env_dat.txt", "w", encoding="utf-8") # 一時保存ファイルをオープン
    i = 0
    while i < n:             # 全センサデータをファイルに保存
#       print (str[i])
        f.write(str[i])
        i += 1
    f.close()

このスクリプトを動かすためには、シリアルパッケージをあらかじめインストールしておく必要があります。

シリアルパッケージのインストール
$ sudo apt-get install python-pip
$ pip install pyserial

この dat_rec.py の処理でデータ保存用の一時ファイルが /home/pi/web/cgi-bin フォルダに env_data.txt として作成されます。そのファイルの内容の例を次に示します。
env_data.txt(例)

00B0xxxA, 13.7, 88, 63, 2
00B0xxx5, 13.7, 85, 75, 5
00B0xxx1, 14.2, 85,123, 1
00B0xxx6, 33.0, 74, 75, 7
00B0xxx4, 13.5, 88, 57,28

左から TWE-Strong ID、温度、湿度、電波強度、インターバル(1:10[s])。IDのxxxは非公開とさせて頂きます。

このスクリプトをデーモンとして動作させるために、前と同様に /etc/systemd/system フォルダに次の設定ファイルを作成しました。
env-monitor_data_rec.service

[Unit]
Description = Env-Monitor Data_Record

[Service]
ExecStart=/home/pi/web/cgi-bin/dat_rec.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

前と同様にパーミッションを 644 に変更し、デーモンの登録とスタートを行えばデーモンとして常駐します。

パーミッション変更 $ sudo chmod 644 env-monitor_data_rec.service
デーモンの登録   $ sudo systemctl enable env-monitor_data_rec
デーモンをスタート $ sudo systemctl start env-monitor_data_rec

4.Python によるCGIスクリプト作成

いよいよ最後の、環境データをブラウザにリアルタイムに表示させる部分です。/home/pi/web/cgi-bin フォルダにスクリプト envmon.py を作成しました。ファイルが少し大きいので別リンクとしました。ご覧になりたい方はダウンロードをして下さい。
envmon.py_.txt
テキストファイルとなっています。末尾の_.txtを削除して envmon.py として下さい。なお 文字コードはUTF-8、TABサイズは 4 です。

ブラウザでアクセスすれば環境データがリアルタイム表示されます。

URL:http://localhost:8000/cgi-bin/envmon.py
または http://192.168.xxx.xxx:8000/cgi-bin/envmon.py
環境データ表示(例)

環境データ表示(例)

センサーIDは上位の ”00” を削除して表示しています。Sは信号強度(0~255)を20で除した値です。

5.まとめ・課題・雑感

1.とりあえずの目標としていた RasPi による環境監視データのWeb配信を行うことができるようになりました。まだLAN環境内での表示ですがWi-Fiルータがあれば電波の届く限りデータを表示させることが出来ます。敷地内の作業場ではハウス1が少し遠いのでTWE-Strongの信号が屋外アンテナでかろうじて受信できています。しかし、住宅内に置いているWi-Fiの電波はハウス1までは厳しい状況です。

2.今後の課題
➀ RasPi による Web(CGI)サーバー ですが、現在の所まだ安定動作に至っていません。2~3日経過するとエラーで停止することがあります。2018年4月現在、稼働を始めてから1年半経過しますが勉強不足で対策が出来ていません。Webサーバーによっては高負荷に耐えられないものもあるらしいので、Python Webサーバーはちょっと厳しいのかも知れません。早めにapacheサーバーに移行しようと考えています。
➁ TWE-Lite との通信をPICに任せましたが、出来れば RasPi でまとめて行う方がハード的には簡単になります。この検討をしてみたいと思っています。
➂ ダイナミックDNSに登録してインターネットから表示できるようにしたいと思います。ただし、セキュリティー面で心配があるのでもう少ししっかり検討していく必要があります。
➃ 温湿度データをデーターベース化して日時とともにグラフ表示できるようにしたいと思います。データーベース化はそれ程問題ではなさそうですがクエリーやグラフ表示が大きな課題です。
➄ 温湿度だけでなく露点温度や気圧等も表示させるようにしたいと思います。露点温度は計算のみ、気圧はセンサー設置、プラスちょっとしたプログラム変更です。
➅ 近年農場施設の防犯対策も必要となってきました。防犯センサーも含めたデーター収集と、メールで警報を伝えるなどもやってみたいと思っています。

3.RaspberryPi について
友人から「RasPi でやると良いよ」と紹介されてからほぼ1年が経過しました。丁度 Raspberry Pi3 が発売されたのを機に購入し、農作業や他の課題の合間に少しずついじってきました。RasPi3 になってから消費電力が増えたのがちょっと難点とは思えますが、IoTなどのシステム作り用マシンとしては最高です。Linux系のコマンドも少し思い出して慣れてきました。益々楽しみです。

4.Python について
私のプログラム歴は「N88BASIC」にはじまり「Quick BASIC」「Quick C」「マイコンのアセンブラ」「マイコンのC言語」と渡ってきました。「構造化プログラミング」が叫ばれた頃からになります。その後の「オブジェクト指向言語」は少しずつかじって頓挫・挫折状態でした。このシステム作りに当たり言語を決めて勉強を始めようと思いつつも、何にするかで長くさまよっていました。PHP、JavaScript、perl、C#等々選択肢があり過ぎです。
今回、RasPi や Python への理解が少しずつ進むに従って今までの迷いも無くなってきました。現在のIoTにマッチし、高機能でありながらわかりやすい標記。オブジェクト指向言語でありながら、私にとっては Quick BASIC からの連続のように思えてきます。何と素晴らしい。マイコンはC言語で良いのですが、RasPi や PC は Python で行きたいと思っています。
★★★ Python最高 ★★★ ・・・ちょっと褒めすぎでしょうか?

以上、「マイコン系のものづくり」もこれからまだまだ楽しめそうです。

関連ページ

環境監視システム自作 概要
環境監視システム自作 子機 温湿度センサーユニット
環境監視システム自作 親機1 特定子機監視器
環境監視システム自作 親機2 データ収集 (TWE_Lite+PIC+Serial出力)
環境監視システム自作 Webサーバー(RasPi + Python)
環境監視システム自作 sqlite3によるデータ蓄積とグラフ表示
環境監視システム自作 matplotlibによるグラフ表示
環境監視システム自作 windows+Apacheサーバー
湿度を正確に測る