โครงงานวันหยุด : สร้างระบบจัดเก็บภาพพร้อมระบบจับการเคลื่อนไหวด้วย Raspberry Pi ภาคสอง

[ภาคแรก][ภาคสาม]

ต่อจากภาคแรกเราได้สร้างระบบจับภาพนิ่งพร้อมระบบตรวจจับการเคลื่อนไหวมาแล้ว  ทีนี้เรามาเพิ่มประเด็นให้คิดต่อกันอีก 1 เรื่อง คือ

ปัญหา : ในกรณีที่ไม่อยู่บ้านเราก็ยังสามารถดูภาพจากที่อื่นได้ผ่านอินเตอร์เน็ต


ทางเลือก

1. การทำ remote access
2. การแชร์พื้นที่ผ่าน cloud service


เปรียบเทียบทางเลือก

 1. Remote Access  เนื่องจากระบบปฏิบัติการของ Raspberry Pi เป็น Linux ดังนั้น utlities ที่เกี่ยวกับเรื่องนี้ก็ต้องมาจากคู่มือ Linux ปัจจุบันที่ใช้กันอยู่ก็จะมี

  • VNC [1] เป็น Graphic User Interface นิยมใช้งานกัน
  • SSH [2] เป็น Command line ใช้กันเยอะเช่นกัน
  • FTP [3] ต้องติดตั้ง Pi ให้ทำหน้าที่เป็น FTP server
  • SCP [4] (Secured Copy)
  • RSync [5]
  • WebServer
ที่กล่าวมานี้เป็น Utilities ที่ใช้งานกันทั่วไปและมีประสิทธิภาพดี แต่ต้องใช้ IP Address ของ Raspberry Pi  ถ้าเราต้องการติดต่อกับ Raspberry Pi จากอินเตอร์เน็ตเราต้องใช้ Public IP Address ซึ่งก็คือ IP Address ของ router ที่เราใช้งานนั่นเอง หากเราใช้วิธีนี้ก็ต้องอาศัยบริการ dynamic dns เช่น noip.comhttp://www.noip.com/ และการทำ port forwarding [6]




2. การใช้พื้นที่ผ่าน Cloud Service

หากเราค้นหาบริการนี้ในอินเตอร์เน็ตจะพบว่ามีให้เลือกใช้เยอะเลย เช่น dropbox[7,9], google drive [8], skydrive, Box, Copy [11], etc.

ส่วนตัวแล้วทางเลือกที่สองนี้น่าสนใจเพราะ สามารถเข้าถึงข้อมูลได้จากอินเตอร์เน็ต นับว่าตรงกับที่ต้องการมากกว่าทางเลือกแรก การเลือกบริการที่ต้องการ ในกรณีนี้ผมเลือกใช้ Google Drive เพราะมีบัญชีอยู่แล้ว ปัจจุบันได้โควต้าเนื้อที่ถึง 15 GB  นับว่าพอเพียง ผมเลือกที่จะเปิดบัญชีใหม่เพื่อจะได้ไม่กระทบต่อข้อมูลที่มีอยู่ในบัญชีอื่น







ติดตั้ง Google Data Python Library

ต้องการ gdata python library เพราะเราต้องมี software จัดการนำเอาภาพที่บันทึกไปวางไว้บน Google Drive มีสองทางเลือก
  1. การติดตั้งด้วย source code [12]
    1. ดาวน์โหลด source code จาก  https://code.google.com/p/gdata-python-client/downloads/list
    2. แตกไฟล์ที่ได้บน Raspberry Pi เข้าไปใน folder ที่ได้ จะพบไฟล์ setup.py
    3. พิมพ์ python ./setup.py install
  2. ในกรณที่ติดตั้ง pip [13]  ไว้แล้ว สามารถใช้คำสั่ง 
pip install gdata

สร้าง Python Code 

ท่านที่ไม่คุ้นเคยกับการใช้งาน gdata library อาจต้องทำงานเยอะหน่อย มีเอกสารบนเว็บมากมายให้ศึกษา แต่ที่ผมแนะนำคือการอ่าน code ตัวอย่างประกอบไปด้วย ซึ่งดูได้จาก [12 , 14 ] จะทำให้พบทางลัดในการทำงาน เพราะบางครั้งการทำตามขั้นตอนปรกติอาจเยิ่นเย้อไปหน่อย  ผมสรุปเป็น 3 ขั้นตอนง่าย ๆ ดังนี้

1. การทำ Authentication  

ทาง Google เรียก Create client ซึ่งผลลัพธ์ของขั้นตอนนี้เราจะได้ client ที่นำไปใช้งานต่อไป

import gdata.data
import gdata.docs.data
import gdata.docs.client

def create_client():
   source="<Any string>"
   client = gdata.docs.client.DocsClient(source=source)
   client.http_client.debug=False
   client.ClientLogin(
   "<your google email address>",
   "<your password>",
   service=client.auth_service,
   source=source)
              return client
...

2. การหาตำแหน่งจัดเก็บเอกสาร

ในทางปฎิบัติเราต้องไปสร้าง folder ไว้กับ Google Drive ไว้ก่อนล่วงหน้าครับ ด้วยวิธีการปรกติ สมมุติว่าเราให้ folder ที่เอาไว้เก็บภาพของเราชื่อ "home_pics" ซึ่งข้อมูลตรงนี้เราต้องนำมาบอกให้โปรแกรมเรารับทราบ

def get_folder() :
   col = None
   for resource in self.client.GetAllResources(uri='/feeds/default/private/full/-/folder'):
         if resource.title.text == "home_pics" :
               col = resource
                break   
        
    return col

ผลลัพธ์จาก code นี้เราจะได้ col ซึ่งเก็บข้อมูลของ Folder ที่เราต้องการนำภาพไปเก็บไว้

 3. การ upload ไฟล์รูปภาพ

 def upload_image():
            folder_resource = get_folder()
            file_path="<image file location>"
   doc = gdata.docs.data.Resource(type='document',title="my home picture")
   media = gdata.data.MediaSource()
   media.SetFileHandle(file_path, 'image/jpeg')
   doc = self.client.CreateResource(doc, media=media, collection=folder_resource)
   return doc


จับมารวมกัน

การเขียน code ของผมนั้น ผมชอบทำเป็น Modular จนเป็นนิสัย ดังนั้นตัวอย่างตรงนี้เป็นเรื่องของไสตล์ ไม่ได้อิงหลักการอะไรพิเศษหรือ framework อะไร นะครับ

ทำ config file จะเป็นที่เราใช้เก็บข้อมูล สมมุติให้ชื่อ myconf.conf เนื้อหาก็จะประมาณนี้

[gdrive]
source=xxxxxx

[gmail]
name=xxxxxx
user=xxxxx@gmail.com
pwd=xxxxxx

[upload_folder]
folder=home_pics


2. สร้าง class

import os.path
import sys

import gdata.data
import gdata.docs.data
import gdata.docs.client
import ConfigParser

conf_file="myconf.conf"

class RaspiGData :

    def __init__(self):

        config  = ConfigParser.ConfigParser()
        config.read(conf_file)
        self.source=config.get('gdrive',source)   
        self.username=config.get('gmail','user')
        self.pwd=config.get('gmail','pwd')
        self.folder=config.get('upload_folder','folder')
        self.create_client()

    def create_client(self):
        self.client = gdata.docs.client.DocsClient(source=self.source)
        self.client.http_client.debug = False
        self.client.ClientLogin(self.username,self.pwd,service=self.client.auth_service, source=self.client.source)
   
    def get_folder(self):
            col = None
            for resource in self.client.GetAllResources(
                     uri='/feeds/default/private/full/-/folder'):
                    if resource.title.text == self.folder :
                        col = resource
                        break   
       
            return col

    def upload(self, file_path, folder_resource):

            doc = gdata.docs.data.Resource(type='document', title=os.path.basename(file_path))
            media = gdata.data.MediaSource()

        media.SetFileHandle(file_path, 'image/jpeg')
            doc = self.client.CreateResource(doc, media=media, collection=folder_resource)
            return doc

    def upload_image(self, image_file_path):
            folder_resource = self.get_folder()
            if not folder_resource:
                    raise Exception('Could not find the %s folder' % self.folder)
            doc = self.upload(image_file_path, folder_resource)



รวมตัวกับภาคแรก 

ท่านอาจต้องย้อนกลับไปดู flow chart ที่ให้ไว้ในภาคแรกกันก่อน (หากลืมไปแล้ว)  code ในภาคนี้จะมาทำงานต่อจาก การบันทึกภาพเป็น jpeg นั้นเอง






มีบางอย่างไม่ถูกต้อง ?

 พิจารณ Flow Chart แล้วก็ไม่น่าจะมีอะไรไม่ถูกต้อง จับภาพแล้วก็นำส่งไปที่ GDrive เลย algorithm นั้นถูกต้องแล้ว แต่ลองนึกถึงข้อเท็จจริงในทางปฎิบัตินะครับ  ปัญหาที่จะเกิดขึ้นคือ

1. Overhead ในการ login ไปที่ GDrive ทำบ่อย ๆ เข้า Google จะมองว่ามีเจตนาไม่ดีแอบแฝง อาจถูกบล๊อกจาก Google ได้ ดังนั้นควรทำเพียงครั้งเดียว

2. การเชื่อมต่อ internet เป็นอะไรที่บอกยากว่าจะเราจะได้ bandwidth คงที่ตลอดเวลาหรือไม่ ดังนั้นหากอยู่ในช่วงเวลาที่ upload ไม่เสร็จสักที ระบบจะเกิดคอขวดขึ้นทันที

เพื่อลดปัญหาดังกล่าวผมจะเพิ่มตัวช่วยเข้าไปอีก 3 ตัวคือ
1. threading ช่วยแยกงานออกเป็นสองงานอิสระต่อกันไม่ต้องรอ โดยผมแยกงานจับภาพนิ่งออกจากงาน upload ภาพนิ่ง

2. Queue แบบ First In First Out (FIFO) เพื่อใช้เก็บชื่อแฟ้มข้อมูลไว้ บน Raspi เพื่อส่งต่อไปให้งาน Upload อีกทอดหนึ่ง และ

3. Daemon  เพื่อให้เราทำการ authenticate เข้าใช้บริการเพียงครั้งเดียว แล้วใช้ได้ไปจนงานเสร็จ ก็ทำให้งานนั้นทำตัวเป็น daemon เสียเลย






ค่อยมาว่าต่อกันในภาคสามเน้อ ครับ

---------------------------
เอกสารอ้างอิง
[1] http://www.tightvnc.com/
[2] http://www.computerhope.com/jargon/s/ssh.htm
[3] http://www.computerhope.com/jargon/f/ftp.htm
[4] http://www.computerhope.com/unix/scp.htm
[5] http://www.computerhope.com/unix/rsync.htm
[6] http://portforward.com/english/routers/port_forwarding/
[7] http://raspi.tv/2013/how-to-use-dropbox-with-raspberry-pi
[8] http://drive.google.com
[9] http://mogshade.wordpress.com/2012/12/23/simple-home-security-with-raspberry-pi-and-dropbox/
[11] https://www.copy.com/home/
[12] https://developers.google.com/gdata/articles/python_client_lib
[13] https://pip.pypa.io/en/latest/installing.html
[14] http://code.google.com/p/gdata-python-client/source/browse/#hg%2Fsamples%2Fdocs

ความคิดเห็น