JavaScriptを実行(04)

前回は、HPのボタンを押してLEDをオンオフしました。今回は、Java Scriptを使用してLEDのオンオフに合わせてボタンの表示をON/OFFと切り替えてみます。

XMLHttpRequestを使用

現在の設定はサーバからクライアントにHTMLファイルを送ってホームページの画面を製作しています。この設定でボタンの表示文字を変更するには以下の2つが考えられます。

  1. HTMLファイルをボタンの状態に合わせて書き換えた後、クライアントに送る。
  2. HTMLファイルの内容を1行づつ送り、該当箇所に来たら書き換えたものを送る。

今回のHTMLファイルは小さいので上記2の方法でも良かったのですが、(ちなみに、2のケースは、ここでやった事が有ります) 今回はJavaScript関数、”XMLHttpRequest” を使う事にしました。XMLHttpRequestは、サーバ、クライアント間でデータの送受信を行う際に利用する関数です。状態に応じてどのタイミングでサーバーと通信するかを指定出来ます。

  • readyState=0 : 状況=UNSENT: 初期状態(インスタンス作成)
  • readyState=1 : 状況=OPENED: openメソッド実行
  • readyState=2 : 状況=HEADERS_RECEIVED: レスポンスヘッダー受信
  • readyState=3 : 状況=LOADING: データ受信中
  • readyState=4 : 状況=DONE: リクエスト完了

今回は、”4”のリクエスト完了を指定しています。スクリプトファイル、”hello_server_04.js”は、こんな感じです

hello_server_04.js

    var url = "http://raspberrypi.local:8080/?80=0";
    var xhr = new XMLHttpRequest();	
    var str;

    xhr.open('GET', url);
    xhr.send();
     
    xhr.onreadystatechange = function() 
    {
      if(xhr.readyState === 4 && xhr.status === 200) 
      {
        str = "OFF";
        if(Number(xhr.responseText[0])) str = "ON";

        document.getElementsByName('1')[0].innerHTML = str;
      }
    }
  • 1行目:サーバのURLとリクエストする内容の設定。今回は、サーバーに80=0を送信。
  • 2行目:XMLHttpRequest()を宣言
  • 5,6行: これでサーバーにGETリクエストを行う。
  • 8行目以降: ここからが通信の処理
    • 10行目: if文以下が実行される条件
      • readyStateが ”4” かつ、通信が正常に行えた(xhr.status === 200)時
      • データは、responseText[]に取り込まれる。
    • 13行目: ここで送られて来たデータを判断
    • 15行目: 状態に合わせて、ボタンの表示を変更する。

次は、これに対応するPython ファイル、”hello_04.py” の説明です。

hello_04.py

from http.server import HTTPServer, SimpleHTTPRequestHandler
from urllib.parse import urlparse,parse_qs
import RPi.GPIO as GPIO

btn_stat = 0

GPIO.setmode(GPIO.BCM)      
GPIO.setup(17, GPIO.OUT)   
GPIO.output(17,0)        

class MyHandler(SimpleHTTPRequestHandler):

    def do_GET(self):
        global btn_stat

        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")

        #-----  Data transfer  --------------
            elif a == "80":
                print("Data transfer\n")
                self.send_response(200)
                self.send_header('Content-type', "text/plain")
                self.end_headers()
                self.wfile.write(str(btn_stat).encode())

                fl = 0
        #-------------------------------------

        else :
            fl = 0

        if fl == 1:
            f = open("index_04.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_04.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 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
#---------------------------------------------------------

host = ''
port = 8080
httpd = HTTPServer((host, port), MyHandler)
print('serving at port', port)
httpd.serve_forever()
  • 38行目から:—– Data transfer ————–
    • 今回追加したクライアントからのリクエスト 80=0は、ここで処理されます。
    • 44行: self.wfile.write(str(btn_stat).encode())
      • btn_stat → ボタン状態を保持した変数。 0:オフ 1:オン
      • str(btn_stat) → 数字を文字列に変換
      • wfile.write(str(btn_stat).encode()) → 文字列をバイト単位に変換しクライアントに送信
      • 送られるデータは数字では無く文字で、ボタンオンの場合:”1”。オフの場合:”0”。となる。
  • 71,73行目: JavaScriptファイル(拡張子 ”.js”)に対応する為に追加。

最後にHTMLファイル、”index_04.html”です。

index_04.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_03.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'>
        	        <button type='submit' name='1' value='0' >On</button>
        	        <button type='submit' name='2' value='0' >Clear</button>
                </form>
            </div>
        </center>
      <script src="http_server_04.js"></script>
    </body>
</html>

19行目の、<script src=”http_server_04.js”></script> を追加しました。CSSファイル、”http_server_03.css”の変更有りません。

http_server_03.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;
}

button {
        display: block;
        margin: 10px;
        line-height: 50px;
        cursor: pointer;
        color: #fff;
        background: #228b22;
        border-radius: 10px;
        font-size: 24px;
	    width:120px;
}

今回からファイルは4つです。

  • hello_04.py:      python本体
  • index_04.html:    HTMLファイル
  • http_server_03.css  CSSファイル
  • hello_server_04.js:  Java Scriptファイル

これら4つを同じフォルダーに入れて、python を実行して下さい。

ブラウザに、”raspberrypi.local:8080”と入力すれば、HPが表示されます。

たった数行のHTTPサーバですが、Java Scriptもしっかり動きます。次回は、温度を測定して値を表示する機能を付けます。