ตัวอย่างการควบคุมอุปกรณ์ผ่าน Python HTTPServer กับ Raspberry Pi

Raspberry Pi กับ HTTPServer อยู่ด้วยกันมาตลอด เพราะทั้ง Python2 และ Python3 ต่างก็มี standard library ที่ช่วยให้ Raspberry Pi กลายเป็น Web Server ได้ทั้งคู่ ในบทความนี้จะใช้ HTTPServer ซึ่งอยู่ใน Python 2 มาใช้ทำเป็นตัวอย่าง และเพืี่อให้เห็นว่าเราสามารถทำให้มันเป็นมากกว่า HTTP Server ก็จะมีการนำเอาไปควบคุมการหมุน Servo เป็นตัวอย่างครับ

การสร้าง HTTP Server (Python 2)


ส่วนทีี่ 1 : Libraries 


from gadgets.motors.servos import Servo5V
from BaseHTTPServer import BaseHTTPRequestHandler
import cgi
import time
import atexit
from os import curdir, sep
from urlparse import urlparse


บรรทัดทีี่ 1 : นำเข้า library จาก RPI.GPIO.TH เพื่อใช้ในการควบคุม Servo ชนิดใช้แรงดันไฟฟ้า 5 V
บรรทัดที่ 2 : นำ BaseHTTPRequestHandler  เข้ามาเพื่อใช้ในการจัดการความต้องการที่ส่งมาจาก client มายัง Raspberry Pi
บรรทัดที่ 3 : cgi เป็น library ที่ช่วยให้เราแยกข้อมูลที่ส่งมาจาก client ด้วยวิธี POST ออกเป็นส่วน ๆ
บรรทัดที่ 5 :  atexit  นำเข้ามาเพื่อช่วยในการ clean up การใช้งาน GPIO
บรรทัดที่ 6 :  curdir, sep ช่วยในการจัดทำ path ของ static file ให้ถูกต้อง
บรรทัดที่ 7 : urlparse  เช่นเดียวกับ cgi แต่ใช้ในกรณีที่ทาง client ส่งข้อมูลมาด้วยวิธี GET



ส่วนที่ 2 : Request Handler Class


สร้าง Request Handler Class ซึ่งจะเป็นตัวทำหน้าที่หลักในการจัดการกับข้อมูล (HTTP requests) ที่ส่งมายัง HTTP Server  โดยเราจะเริ่มต้นด้วยการ overwrite  2 methods คือ do_GET และ do_POST  โดยยังมี code ภายใน

class RequestHandler(BaseHTTPRequestHandler):
   def do_GET(self):
      return

   def do_POST(self):
      return
โดยทั่วไปเราเข้าใจกันแล้วว่าหน้าแรกที่เราได้เห็นสำหรับเว็บไซต์ทั่วไปมักมีชื่อว่า index.html ซึ่งเราจะสร้างไว้เป็น static file แยกไว้ต่างหาก เมื่อ client ส่งความต้องการมาด้วยรูปแบบของ URL ที่ปิดท้ายด้วยสัญญลักษณ์ "/" จะถูกตีความว่าให้ส่งเนื้อหาใน index.html ไปยัง browser

def do_GET(self):
    query_str = urlparse(self.path)
    if query_str.path == '/':
        self.path = './index.html'
        with open(curdir + sep + self.path) as f:
     self.send_response(200)
     self.send_header('Content-type',"text/html")
            self.end_headers()
     self.wfile.write(f.read()) 
   
    return

Code ที่เพิ่มเข้าไปใน do_GET  เริ่มต้นด้วยการแยก request ออกด้วยคำสั่ง urlparse(self.path) ซึ่งทำให้เราทราบว่ามี query string เป็นอย่างไร ในกรณีที่ query string เป็น "/" ก็จะทำการ อ่านข้อมูลจาก index.html ขึ้นมาไว้ใน self.wfile (ที่สำหรับเขียนข้อมูลของ BaseHTTPRequestHandler ก่อนส่งออกไปยัง client) สำหรับเนื้อหาใน index.html เป็นดังนี้



บนหน้าจอของ browser จะแสดงให้เป็น slider ที่แสดงค่าตั้งแต่ 0 - 180 ซึ่งตรงกับค่าช่วงกว้างของ Servo นั่นเอง ค่าของ slider จะถูกส่งไปยัง function write_servo(angle) เพื่อทำการส่งค่ามุมกลับไปยัง HTTP Server ด้วยวิธีการ POST (หรือการ submit form ในกรณีที่ใช้ form บนเว็บเพจ)


ขั้นตอนต่อไปเป็นการเพิ่ม code เข้าไปใน do_POST ที่วางไว้ในตอนแรก ดังนี้

def do_POST(self):
    form = cgi.FieldStorage(
                 fp=self.rfile, 
   headers=self.headers,
   environ={'REQUEST_METHOD':'POST',
   'CONTENT_TYPE':self.headers['Content-Type'],
  })
    servo.write(int(form["write_servo"].value))
    self.send_response(200)
    self.send_header('Content-type','text/plain')
    self.end_headers()
    self.wfile.write(form["write_servo"].value)
    return
ข้อมูลที่ผ่านการ submit form หรือการส่งด้วยวิธี POST เราจะทำการ dump ออกมาเก็บไว้ในตัวแปร form ซึ่งเป็น instance ของ cgi.FieldStorage class ก่อนแล้วจึงจะสามารถนำค่าที่เก็บไว้มาใช้งานได้ โดยค่าทีีส่งมานั้นเก็บไว้ใน key ชืี่อ 'write_servo' (ดูใน index.html)  ซึ่งจะถูกส่งต่อไปให้ servo ใช้งานต่อไป

ส่วนที่ 3


def cleanup():
 servo.cleanup()
  
if __name__ == '__main__':
 from BaseHTTPServer import HTTPServer
 global servo
 servo = Servo5V(pin_number=12,freq=100)
 atexit.register(cleanup)
 server = HTTPServer(('', 8080), RequestHandler)
 print ('Starting server, use  to stop')
 server.serve_forever()

งานที่เหลือได้แก่ การ cleanup GPIO หลังการใช้งาน และการประกาศตัวแปรที่จำเป็น ได้แก่
servo = Servo5V()
เพื่อใช้ควบคุม Server ผ่าน GPIO

server = HTTPServer(('', 8080), RequestHandler)
เพื่อใช้เป็น HTTP Server ที่ให้บริการผ่าน port 8080

ฉบับสมบูรณ์

RaspiHTTPServer.py :

from gadgets.motors.servos import Servo5V
from BaseHTTPServer import BaseHTTPRequestHandler
import cgi
import time
import atexit
from os import curdir, sep
import os

from urlparse import urlparse

class RequestHandler(BaseHTTPRequestHandler):
 def get_mimetype(self):
  if self.path.endswith(".html"):
   mimetype='text/html'
  if self.path.endswith(".jpg"):
   mimetype='image/jpg'
  if self.path.endswith(".gif"):
   mimetype='image/gif'
  if self.path.endswith(".js"):
   mimetype='application/javascript'
  if self.path.endswith(".css"):
   mimetype='text/css'
  return mimetype

 def response_static_file(self):
  with open(curdir + sep + self.path) as f:
   mimetype = self.get_mimetype()
   self.send_response(200)
   self.send_header('Content-type',mimetype)
   self.end_headers()
   self.wfile.write(f.read()) 
  
 def do_GET(self):
  query_str = urlparse(self.path)
  if query_str.path == '/':
   self.path = './index.html'
   with open(curdir + sep + self.path) as f:
    mimetype = self.get_mimetype()
    self.send_response(200)
    self.send_header('Content-type',mimetype)
    self.end_headers()
    self.wfile.write(f.read()) 
   
  return
  
 def do_POST(self):
  #dump POST request onto variable
  form = cgi.FieldStorage(
   fp=self.rfile, 
   headers=self.headers,
   environ={'REQUEST_METHOD':'POST',
     'CONTENT_TYPE':self.headers['Content-Type'],
     })
  servo.write(int(form["write_servo"].value))
  self.send_response(200)
  self.send_header('Content-type','text/plain')
  self.end_headers()
  self.wfile.write(form["write_servo"].value)
 
  return

def cleanup():
 servo.cleanup()
  
if __name__ == '__main__':
 from BaseHTTPServer import HTTPServer
 global servo
 servo = Servo5V(pin_number=12,freq=100)
 atexit.register(cleanup)
 server = HTTPServer(('', 8080), RequestHandler)
 print ('Starting server, use  to stop')
 server.serve_forever()

ผลการทำงาน


$ python RaspiHTTPServer.py

แล้วเปิดเว็บบราวเซอร์ด้วย URL : http://[your Raspberry Pi address]:8080/


Previous
Next Post »