วันพุธที่ ๑๑ ตุลาคม พ.ศ. ๒๕๖๐

การกำหนดให้ Application ที่เราเขียนให้ทำงานอัตโนมัติตั้งแต่การ Boot

จากที่ได้เขียนโปรแกรมลงใน Raspberry Pi และนำไปฝึกอบรมให้ผู้สนใจทำตาม จะมีคำถามที่น่าสนใจจากผู้เข้าอบรมอยู่คำถามหนึ่งคือ

"เมื่อเราจะใช้ Application เราไม่ต้องมาต่อเม้าส์ ต่อจอ และคีย์บอร์ด เพื่อมาคลิกให้ Application ทำงานทุกครั้งด้วยหรือ"

ขอตอบในเบื้องต้นว่า สมัยที่ยังไม่มีวินโดวส์ เราจะเจอไฟล์ประเภท autoexec.bat ที่เขียนขึ้นมาเพื่อให้มันทำงานทันทีหลังจาก boot DOS เสร็จ ฉันใดก็ฉันนั้น ใน Linux Raspbian ก็สามารถสั่งให้มันทำงานตั้งแต่ boot เลยก็ได้เช่นกัน และมีหลายวิธีด้วยกัน แต่วิธีที่ง่ายที่สุด และเหมาะสมกับงานควบคุม on-off อุปกรณ์ไฟฟ้า ได้แก่ การเขียนไฟล์เพิ่มลงไปใน crontab จะง่ายที่สุด เรามาเริ่มทำกันเลยดีกว่า

ที่ terminal ใช้คำสั่ง

1. sudo crontab -e

ให้เพิ่มคำสั่งต่อไปนี้ ในบรรทัดท้ายสุด

@reboot python /home/pi/iot-application.py &


สมมุติว่า โปรแกรมของเราชื่อว่า iot-application.py
และอยู่ใน path ของ /home/pi/

2. Save ด้วยการกด Ctrl+o แล้วกด enter
3. ปิดด้วยการกด Ctrl+x

4. ทดลอง Boot เครื่องใหม่เพื่อทดสอบว่าทำได้จริงหรือไม่ ด้วยคำสั่ง

sudo reboot

ถ้ามันทำงานถูกต้องทุกประการ ถือว่า ได้ตามวัตถุประสงค์ทุกประการ

ข้อสงสัย ให้ชวนคิด

สมมุติว่า เรามี Application หนึ่งสำหรับรดน้ำสวนเมล่อน อีก Application สำหรับรดน้ำในสวนมะเขือเทศ และต้องการให้ทำงานทั้ง 2 Application พร้อมกัน ตั้งแต่ตอน Boot คุณคิดว่า มันสามารถทำงานได้ไหม และจะเขียนคำสั่งอย่างไร



วันพุธที่ ๒๙ มิถุนายน พ.ศ. ๒๕๕๙

Internet of Things (IoT) ตอนที่ 5

ตอนที่ 5 ว่าด้วยเรื่อง Raspberry Pi กับการต่อ input ที่เป็น micro switch เข้าทาง GPIO

การต่อสัญญาณเข้าไปประมวลผลใน MCU ของ Raspberry Pi เปรียบเสมือนกับการที่เราใช้ keyboard ป้อนข้อมูลเข้าเครื่องคอมพิวเตอร์ เพียงแต่ Raspberry Pi ได้ออกแบบ GPIO สำหรับให้เราต่ออุปกรณ์อิเล็คทรอนิกส์ เช่น สวิทช์ เพื่อส่งสัญญาณไปบอก MCU ว่ามีการกด หรือส่งสัญญาณไป แล้วคำสั่งใน MCU จะไปประมวลผลเพื่อไปควบคุมเครื่องใช้ไฟฟ้า หรืออุปกรณ์อิเล็คทรอนิกส์อีกทีหนึ่ง

แต่การต่อไมโครสวิทช์ ที่เราจะต่อวงจรเองนั้น มีความซับซ้อนและมีปัญหาหลาย ๆ อย่าง ในกรณีที่จะนำไปใช้งานจริง จึงต้องทดสอบ และพิถีพิถัน ให้มากยิ่งขึ้น

วงจรการต่อสวิทช์

การต่อสวิทช์ที่เกี่ยวข้องกับงาน MCU นั้น มี 2 วิธี ได้แก่


  • การต่อให้ส่งสัญญาณไปยัง MCU เป็น  0 volt หรือ Active Low
  • การต่อให้ส่งสัญญาณไปยัง MCU เป็น 3.3 volt หรือ Active Hi

วงจรสวิทช์ที่ทำงานแบบ Active Low






กรณีที่เป็น 0 volt (active low) นั่นหมายถึง ปกติแรงดันไฟฟ้าที่รออยู่เป็นสถานะ Hi หรือมีแรงดันไฟฟ้าประมาณ 3.3 volt เพราะกระแสไฟฟ้าจะไหลจาก vcc 3.3v ผ่าน R1 ที่เป็นความต้านทานขนาด 10K และผ่าน R2 ขนาด 1K ไปยังขา GPIO IN 

แต่เมื่อเรากดสวิทช์ สถานะ Hi จะเปลี่ยน Low เพราะว่า ขากราวด์ (0 volt) จะไหลผ่านสวิทช์ ไปสู่ R2 แล้วไปสู่ขา GPIO IN  ในขณะที่ R1 มีความต้านทานสูงถึง 10 K มันจึงไม่สามารถไหลผ่านไปได้ ตามหลักการของไฟฟ้าที่กระแสไฟฟ้าจะไหลผ่านสิ่งที่มีความต้านทานน้อยกว่า

วงจรสวิทช์ที่ทำงานแบบ Active Hi



การทำงานของวงจรสวิทช์แบบนี้ จะทำงานตรงกันข้ามกับแบบแรก ให้พิจารณาที่สวิทช์และ R1 จะสลับกับภาพแรก ดังนั้น เมื่อกดสวิทช์แรงดันไฟฟ้า 3.3 volt จากขา Vcc จะไหลผ่านสวิทช์ผ่านไปยัง R2 แล้วไปยังขา GPIO IN ประมาณแรงดันที่ 3.3 volt หรือ Active Hi นั่นเอง


การเขียนโปรแกรมทั้งสองวิธีก็แตกต่างกัน แต่ในปัจจุบันอุปกรณ์ที่เป็น micro switch ที่ใช้ในงาน MCU มักจะทำมาขายสำเร็จรูปมีความต้านทานอยู่ภายในอยู่แล้ว จึงมีขาเสียบ 3 ขา คือขา Vcc, GND และขาต่อ GPIO 

ในบอร์ดของ Arduino อาจต้องต่อวงจรแบบนี้ แต่สำหรับ Raspberry Pi แล้ว เขาออกแบบมาให้สามารถต่อวงจรไม่ต้องใช้ R ทั้ง 2 ตัวเลยก็ได้ ดังนี้



จากวงจรสวิทช์ที่ต่อใน breadboard นั้น เราสามารถต่อขาจาก Vcc 3v3 จาก GPIO มาเข้าขาเข้าสวิทช์ ส่วนขาออกสวิทช์ก็สามารถต่อไปยัง GPIO input ได้เลย แต่เราต้องกำหนดใน setup เป็นดังนี้


GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

มาดู Source code ในการเขียนคำสั่งเพื่อรับ input จาก switch กันเลย

--------------------------------------------------------------------
import RPi.GPIO as GPIO
import time
green = 18
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False) 
GPIO.setup(green,GPIO.OUT) 
GPIO.setup(17,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
i=0
while True:        
        if (GPIO.input(17)==1):        
                i+=1        
                print "button pressed: ", i
--------------------------------------------------------------------
จาก code นี้เพื่อแสดงให้เห็นถึงปัญหาจากการกดปุ่ม ในกรณีที่เขียนโปรแกรมแบบ poll หรือเข้าไปสอบถาม มันจะลูปซ้ำกันหลายครั้งในระยะที่เรากดครั้งหนึ่ง  นี้คือผลลัพธ์ของการกดปุ่ม 1 ครั้ง ด้านล่างนี้
จะเห็นว่ากดหนึ่งครั้งมันจะลูปประมาณ 1801 ครั้ง หากมีการกดสวิทช์ตัวถัดมา หรือตัวอื่น ๆ อาจจะมีปัญหา โดยเฉพาะมีการ delay ด้วย ดังนั้น raspberry pi จึงได้จัดให้มีคำสั่งสำหรับการเขียนแบบ ตรวจสอบการทำงานของปุ่มกดที่ขอบขาขึ้น และขอบขาลง คำสั่งดังตัวอย่างต่อไปนี้
--------------------------------------------------------------------
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
i=0 while True: GPIO.wait_for_edge(17, GPIO.RISING)
i+=1 print "button pressed: ",i
-----------------------------------------------
ผลลัพธ์ที่ได้ จะแสดงข้อความว่า
     button pressed:  1
เพียงครั้งเดียวต่อการกด 1 ครั้ง เมื่อเรากดอีกครั้ง ข้อความจะเพิ่มเป็น
     button pressed:  2
แต่กรณีเราออกแบบให้มีสวิทช์หลายตัว เช่น ระบบจราจรที่กำลังจะทำให้ดูเป็นตัวอย่าง หากเขียนให้รอการกดปุ่มตามลำดับแบบนี้ ถ้าหากผู้ใช้กดปุ่มไม่เป็นไปตามลำดับก็จะมีปัญหา
มาดูวงจรของการทำสวิทช์ 4 ตัวเพื่อควบคุมจราจร 4 แยก ที่มีการกดปุ่มไฟเขียนตามลำดับ ดังนี้
----------------------------------------------------------
import RPi.GPIO as GPIO import time
ch1g = 21
ch1y = 20 
ch1r = 16
ch2g = 25
ch2r = 24
ch3g = 27
ch3r = 17
GPIO.setmode(GPIO.BCM) GPIO.setup(ch1g,GPIO.OUT) GPIO.setup(ch1y,GPIO.OUT) GPIO.setup(ch1r,GPIO.OUT) GPIO.setup(ch2g,GPIO.OUT) GPIO.setup(ch2r,GPIO.OUT) GPIO.setup(ch3g,GPIO.OUT) GPIO.setup(ch3r,GPIO.OUT)
GPIO.setup(05,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(06,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(13,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(19,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
def ch1():          
            GPIO.output(ch1g,GPIO.HIGH)          
            GPIO.output(ch2r,GPIO.HIGH)          
            GPIO.output(ch3r,GPIO.HIGH)          
            GPIO.output(ch1r,GPIO.LOW)
            GPIO.output(ch1y,GPIO.LOW)
            GPIO.output(ch2g,GPIO.LOW)
            GPIO.output(ch3g,GPIO.LOW)
def ch2():
            GPIO.output(ch2g,GPIO.HIGH)
            GPIO.output(ch3r,GPIO.HIGH)
            GPIO.output(ch1r,GPIO.HIGH)
            GPIO.output(ch1g,GPIO.LOW)
            GPIO.output(ch1y,GPIO.LOW)
            GPIO.output(ch2r,GPIO.LOW)
            GPIO.output(ch3g,GPIO.LOW)
def ch3():
            GPIO.output(ch3g,GPIO.HIGH)
            GPIO.output(ch2r,GPIO.HIGH)
            GPIO.output(ch1r,GPIO.HIGH)
            GPIO.output(ch3r,GPIO.LOW)
            GPIO.output(ch1y,GPIO.LOW)
            GPIO.output(ch2g,GPIO.LOW)
            GPIO.output(ch1g,GPIO.LOW)
while True:
               GPIO.wait_for_edge(05, GPIO.RISING)
               ch1()
               print "channel 1: "
               GPIO.wait_for_edge(06, GPIO.RISING)
               ch2()
               print "channel 2: "
              GPIO.wait_for_edge(13, GPIO.RISING)
               ch3()
               print "channel 3: "
----------------------------------------------------------------------------
ผลลัพธ์การทำงานของโปรแกรมดังแสดงในคลิป ด้านล่างนี้