前回の温度を測定するHPから機能を追加して下記の様に変更しました。
- Interval表示の下にある入力欄で測定間隔を入力(分、時単位)
- Startボタンで測定開始。Stopボタンで測定終了。
- その間の測定値はメモリーに保存。
- 一番下の、”Download00″で測定値をPCにダウンロード出来る。
タイマー割り込みが見つからない
タイマー割り込みを使用してRaspberry Piである間隔で温度を測定しようとしたら、Pythonにタイマーで割り込みをかける関数が見あたりません。外部からの信号で割り込みをかける関数は有りました。そこで専用のRTCモジュールDS3231を使ってRaspberry PIに割り込みを発生させる事にしました。使い方は”DS3231 (RTC)を使う”にDS3231の説明が有るので参考にして下さい。
I2Cの機器が2つになったので
温度測定用のADT7410とRTCのDS3231は共にインターフェースが、”I2C”です。同じバスに複数の機器を接続出来るのが、I2Cのメリットです。
ソフトの説明
先ずは、HPの、”index_06.html”です。
index_06.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" type="text/css" href="./http_server_06.css" >
<title>http-server 1.0</title>
</head>
<body>
<center>
<div class="b_frame">
<div class="t_font"><u>HTTP-Server 1.0</u></div><br>
<form method='get'>
<div style="display:flex; justify-content:space-evenly;">
<button type='submit' name='1' value='0' >On</button>
<button type='submit' name='2' value='0' >Clear</button>
</div>
<p style="font-size:25px;" id='1'>Tenp:</p>
<button type='submit' name='3' value='0' >Measure</button>
<br>
<div class="disp-group">
<p style="font-size:25px;">Interval</p>
<div>
<input type='number' max='23' min='0' id='2' class='inp_box' />(H)
<input type='number' max='59' min='0' id='3' class='inp_box' />(M)
</div>
</div>
<div style="display:flex; justify-content:space-evenly;">
<button type='submit' name='4' value='0' onclick='onBtn_Set_Inter()' >Start</button>
<button type='submit' name='5' value='0' >Stop</button>
</div>
</form>
<br>
<a style="font-size:25px;color:#00ffff;cursor: pointer;" href='./data.txt' download='data.txt'>Download00</a>
<br>
</div>
</center>
<script src="http_server_06.js"></script>
</body>
</html>
- 6行目: CSSファイルを、”http_server_06.css”と指定
- 24行目: 測定間隔の分を指定する入力欄。IDを’2’に指定しています。
- 25行目: 測定間隔の時を指定する入力欄。IDを’3’に指定しています。
- 30行目: 測定を開始するボタン。Nameを’4’と指定。ボタンがクリックされると、
’onBtn_Set_Inter()’が起動 - 31行目: 測定停止ボタン。Nameを’5’と指定。
- 34行目: 測定データのダウンロード
- 37行目: jsファイルの指定。”http_server_06.js”
次は、JSの、”http_server_06.js”です。
http_server_06.js
var url = "http://raspberrypi.local:8080/?80=0";
var xhr = new XMLHttpRequest();
var str;
var para=['0','0','0','0','0','0'];
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function()
{
if(xhr.readyState === 4 && xhr.status === 200)
{
console.log( xhr.responseText );
b=0;
for(a = 0; a < 5; a++)
{
para[a]='';
while( xhr.responseText[b] != ',')
{
para[a] += xhr.responseText[b];
b ++;
}
b ++;
}
str = "OFF";
if(Number(para[0])) str = "ON";
document.getElementsByName('1')[0].innerHTML = str;
document.getElementById('1').innerHTML = para[1] + "℃";
document.getElementById('2').value = Number(para[2]);
document.getElementById('3').value = Number(para[3]);
str = true;
if(para[4] == "0") str = false;
else document.getElementsByName('4')[0].style.backgroundColor = "gray";
document.getElementsByName('4')[0].disabled = str;
}
}
function onBtn_Set_Inter() {
var a;
var str="";
str += (document.getElementById('2').value + ",");
str += document.getElementById('3').value
document.getElementsByName('4')[0].value=str;
}
- 4行目: Python側から送られくるパラメータを保存する配列。保管するデータは、”ボタンの状態”、
”温度”、”測定間隔の時”、”測定間隔の分”、”状態”の5つ - 15-25行: ここでパラメータを配列に保管
- 27-32行: HPの状態書き換え。
- 34-37行: 測定中は、”Start”ボタンをインアクティブにする。
- 42-48行: ”Start”ボタンを押すとこの関数が実行される。時と分のデータをサーバに送る。
続いて、Pythonプログラム、”hello_06.py”です。
hello_06.py
from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse,parse_qs
import RPi.GPIO as GPIO
import datetime
import smbus
btn_stat = 0
run_stat = 0
time_no = 0
hr = 0
mn = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.output(17,0)
GPIO.setup(4, GPIO.IN, GPIO.PUD_UP)
bus = smbus.SMBus(1)
f = open('data.txt', 'w')
f.close()
class MyHandler(SimpleHTTPRequestHandler):
def do_GET(self):
global btn_stat, run_stat, hr, mn, f
fl = 1
if self.chk_file() == 0:
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
a=next(iter(params))
#----- LED ON/OFF --------------
if a == "1":
if btn_stat == 0:
GPIO.output(17,1)
print("LED_On\n")
btn_stat = 1
else:
GPIO.output(17,0)
print("LED_Off\n")
btn_stat = 0
#----- Port Clear -----------------
elif a == "2":
GPIO.cleanup()
print("IO_Clear\n")
#----- Measure temp -----------------
elif a == "3":
print("Measurement\n")
#----- Start -----------------
elif a == "4":
run_stat = 1
b = params['4'][0]
b = b.split(",")
hr = int(b[0])
mn = int(b[1])
time_no = 0
self.DS3231_init()
f = open('data.txt', 'w')
f.close()
self.measure_next(123)
a = bus.read_byte_data(0x68, 0x0e)
a |= 2
bus.write_byte_data(0x68, 0x0e, a)
GPIO.add_event_detect(4, GPIO.FALLING, callback=self.measure_next)
#----- Stop -----------------
elif a == "5":
run_stat = 0
a = bus.read_byte_data(0x68, 0x0e)
a &= 0xfd
bus.write_byte_data(0x68, 0x0e, a)
GPIO.remove_event_detect(4)
print("Stop\n")
#----- Data transfer --------------
elif a == "80":
print("Data transfer\n")
self.send_response(200)
self.send_header('Content-type', "text/plain")
self.end_headers()
a = self.get_temp()
buf = str(btn_stat) + ',' + str(f'{a:.1f}') + ',' + str(hr) + ',' + str(mn) + ',' + str(run_stat) + ','
self.wfile.write(buf.encode())
fl = 0
#-------------------------------------
else :
fl = 0
if fl == 1:
f = open("index_06.html",'rb')
self.send_response(200)
self.send_header('Content-type', "text/html")
self.end_headers()
self.wfile.write(f.read())
f.close()
#-------------------------------------------------
def chk_file(self):
a=0
if self.path == "/":
self.path = "/index_06.html"
dataType = "text/html"
a=1
if self.path.endswith(".css"):
dataType = "text/css"
a=1
if self.path.endswith(".js"):
dataType = "application/javascript"
a=1
if self.path.endswith(".txt"):
dataType = "txt/plain"
a=1
if a == 1:
self.path = "." + self.path
f = open(self.path,'rb')
self.send_response(200)
self.send_header('Content-type', dataType)
self.end_headers()
self.wfile.write(f.read())
f.close()
return a
#-------------------------------------------------
def get_temp(self):
global bus
# 13bit mode
bus.write_byte_data(0x48, 0x03, 0x00)
word_data = bus.read_word_data(0x48, 0x00)
data = (word_data & 0xff00) >> 8 | (word_data & 0xff) << 8
data /= 128
return(data)
#-------------------------------------------------
def DS3231_init(self):
for a in range(3):
bus.write_byte_data(0x68, a, 0)
a = bus.read_byte_data(0x68, 0x0d)
a |= 0x80
bus.write_byte_data(0x68, 0x0d, a)
#-------------------------------------------------
def measure_next(self,dummy):
global hr, mn, bus, f, time_no
d_min = bus.read_byte_data(0x68, 0x01)
d_min = self.chg_dcb(d_min)
d_hour = bus.read_byte_data(0x68, 0x02)
d_hour = self.chg_dcb(d_hour)
d_min += mn
if d_min > 59:
d_min -= 60
d_hour += 1
d_hour += hr
if d_hour > 23: d_hour -= 24
d_min = self.chg_bcd(d_min)
d_hour = self.chg_bcd(d_hour)
bus.write_byte_data(0x68, 0x0b, d_min)
bus.write_byte_data(0x68, 0x0c, d_hour)
dt_now = datetime.datetime.now()
time_no += 1
a = self.get_temp()
f = open('data.txt', 'a')
f.write(str(time_no) + "," + dt_now.strftime('%Y年%m月%d日 %H:%M:%S') + ',' + str(f'{a:.1f}') +'\n')
f.close()
d_min = bus.read_byte_data(0x68, 0x0f)
d_min &= 0xfd
bus.write_byte_data(0x68, 0x0f, d_min)
print("innterrupt\n")
#-------------------------------------------------
def chg_bcd(self,data):
return(((data // 10) << 4) + ( data % 10))
#-------------------------------------------------
def chg_dcb(self,data):
return((data >> 4) * 10 + ( data & 0xf))
#---------------------------------------------------------
host = ''
port = 8080
httpd = HTTPServer((host, port), MyHandler)
print('serving at port', port)
httpd.serve_forever()
- 7行目: btn_stat:ボタンの状態。0:オフ 1:0オン
- 8行目: run_stat:測定器の状態。 0:停止 1:測定中
- 9行目: time_no:測定して回数。
- 10行目: hr:測定間隔の時の値。
- 11行目: mn:測定間隔の分の値。
- 54から72行: 測定開始。”Start”ボタンが押されると実行される。
- 56行: run_statを1に設定
- 57から60行: クライアントから送られてきた時と分のデータの読み込み
- 62行: 測定回数を0に設定。
- 63行: DS3231の時間の初期設定。
- 65,66行: データ保存用のファイルを作成
- 68行: データの測定と次の測定の準備
- 69から71行: DS3231のタイマー開始
- 72行目: Raspberry PIの割り込み開始
- 74から81行: 測定停止。”Stop”ボタンが押されると実行される。
- 76行: run_statを0に設定
- 77がら79行: DS3231のタイマー停止
- 80行目: Raspberry PIの割り込み停止
- 83から94行: データの送信。
- ”ボタンの状態”、”温度”、”測定間隔の時”、”測定間隔の分”、”状態”の5つを文字列にして送信
- 101,113行: ”Index_06.html”の指定
- 124から126行: 測定データダウンロード用に拡張子、”.txt”を許可。
- 152から159行: DS3231の設定
- 154から159行: 時間と分と秒を00:00:00に設定
- 157から159行: タイマー2の時と分を使用
- 162から203行: 温度の測定。測定データの保存。次の測定時間の設定
- 165から181行: 現行の時間を読み込み、測定間隔時間を足して次の測定時間を設定。
- 183から188行: 温度を測定して現在の時間と共に保存。
- 190から192行: フラクをクリアーして次のタイマーのカウント開始
最後は、CSS。”http_server_06.css”です。
http_server_06.css
@charset "UTF-8";
.t_font {
font-size: 40px;
font-weight:bold;
font-style: italic;
color: #0ff;
}
.b_frame {
width: 400px;
background: #363636;
padding: 15px;
border-radius: 10px;
margin-top: -30px;
margin-right: 10px;
color: #EFEFEF;
}
button {
display: block;
margin: 10px;
line-height: 30px;
cursor: pointer;
color: #fff;
background: #228b22;
border-radius: 10px;
font-size: 24px;
width:120px;
}
.inp_box {
width:40px;
font-size: 16px;
text-align: right;
}
- 32から37行: 時と分、入力用欄のCSSを追加。
その他追加詳細
- Raspberry PIの割り込み
- GPIOエッジ検出コールバック関数として以下を使用
- add_event_detect : イベント検知。検知したらcallbackで指定された関数を実行。
- GPIO.RISING: 立ち上がりエッジ
- GPIO.FALLING: 立ち下がりエッジ
- GPIO.BOTH: 両方
- remove_event_detect : イベント検知を解除します。
- add_event_detect : イベント検知。検知したらcallbackで指定された関数を実行。
- GPIOエッジ検出コールバック関数として以下を使用
- DS3231の設定
- 測定の開始と同時に時間を、00:00:00にセット
- 今回は、時と分のみアラームに使用している為、他のパラメータはセットしていません
- タイマーとしてアラーム2を使用しています。
- アラーム2は、分、時、日が対象となります。今回は、分と時が対象
- 日を無効にする為に、レジスタ0x0Dの7ビットを、”1”にセット
- アラームを有効にする為に、レジスタ0x0Eの2ビット目を、”1”にセット
- 同時にレジスタ0x0Fの2ビット目を、”0”にセット
- 測定の開始と同時に時間を、00:00:00にセット
- 温度測定データ
- 測定データは、Raspberry PIのSDカードに保存されます。
- ファイル名は、”data.txt”。保存先は、サーバーのルートディレクトリです。
- 保存形式は、測定回数、測定時間、温度データ。
- データの取り出しは、サーバールートにある、”data.txt”を直接取り込んでも良いですが、HPからクライアントにダウンロード出来る様になっています。
プログラムの実行
いつも通りに、JS Python CSS HTMLの各ファイルを同じフォルダーに保管して、”python3 hello_06.py”とターミナルで実行して下さい。
ブラウザー側で、”raspberrypi.local:8080″と入力するとサーバのHP画面が表示されます。
- この入力欄で測定間隔の時と分を設定します
- ”Start”ボタンを押すと測定開始。
- 測定中は、”Start”ボタンがインアクティブになります。
- 測定中でも測定値のダウンロードは出来ます。”Download00″をクリックすると
- ウインドウが開きます。ここで、プログラムで開くを選べば
- データをダウンロードせずに見る事が出来ます。(この機能はブラウザによっては無い。今回はFire FOXを使用)
”Stop”ボタンを押せば測定が終了します。終了後、”Download00″をクリックしてデータをダウンロードしたものが以下です。データ自体はRaspberry PIのSDカードに保存されているのですが、Raspberry PIに触ることなくデータをPCにダウンロード出来ます。
今回は、ここまで。次回はHTTPサーバからメールを送ってみたいと思います。