定期的に温度を測る(6)

前回の温度を測定する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)&nbsp;&nbsp;
			            <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 : イベント検知を解除します。
  • DS3231の設定
    • 測定の開始と同時に時間を、00:00:00にセット
      • 今回は、時と分のみアラームに使用している為、他のパラメータはセットしていません
    • タイマーとしてアラーム2を使用しています。
      • アラーム2は、分、時、日が対象となります。今回は、分と時が対象
      • 日を無効にする為に、レジスタ0x0Dの7ビットを、”1”にセット
      • アラームを有効にする為に、レジスタ0x0Eの2ビット目を、”1”にセット
      • 同時にレジスタ0x0Fの2ビット目を、”0”にセット
  • 温度測定データ
    • 測定データは、Raspberry PIのSDカードに保存されます。
    • ファイル名は、”data.txt”。保存先は、サーバーのルートディレクトリです。
    • 保存形式は、測定回数、測定時間、温度データ。
    • データの取り出しは、サーバールートにある、”data.txt”を直接取り込んでも良いですが、HPからクライアントにダウンロード出来る様になっています。

プログラムの実行

いつも通りに、JS Python CSS HTMLの各ファイルを同じフォルダーに保管して、”python3 hello_06.py”とターミナルで実行して下さい。

ブラウザー側で、”raspberrypi.local:8080″と入力するとサーバのHP画面が表示されます。

  1. この入力欄で測定間隔の時と分を設定します
  2. ”Start”ボタンを押すと測定開始。
  3. 測定中は、”Start”ボタンがインアクティブになります。
  4. 測定中でも測定値のダウンロードは出来ます。”Download00″をクリックすると
  5. ウインドウが開きます。ここで、プログラムで開くを選べば
  6. データをダウンロードせずに見る事が出来ます。(この機能はブラウザによっては無い。今回はFire FOXを使用)

”Stop”ボタンを押せば測定が終了します。終了後、”Download00″をクリックしてデータをダウンロードしたものが以下です。データ自体はRaspberry PIのSDカードに保存されているのですが、Raspberry PIに触ることなくデータをPCにダウンロード出来ます。

今回は、ここまで。次回はHTTPサーバからメールを送ってみたいと思います。