บทความนี้กล่าวถึงการใช้ไลบรารี pySerial ของภาษาไพธอนบนบอร์ด Raspberry Pi หรือ RPi ทั้งรุ่น 3 และ 4 เพื่อเชื่อมต่อกับพอร์ตสื่อสารอนุกรม (Serial Port) ซึ่งตัวบอร์ดสามารถทำได้ 2 ลักษณะคือ ใช้ฮาร์ดแวร์อย่าง ET-CONV10/RS232 HAT ที่ได้เขียนถึงในหนังสือ กับการใช้พอร์ต USB เชื่อมต่อกับตัวแปลงเป็นพอร์ตสื่อสารอนุกรม (USB to Serial Port) ดังภาพที่ 1 โดยบทความนี้เป็นการใช้แบบที่ 2 เพื่อเรียกใช้ pySerial สำหรับตรวจสอบว่ามีพอร์ตอนุกรมเชื่อมต่ออยู่กี่พอร์ตและชื่ออะไรบ้าง ดังตัวอย่างในภาพที่ 8
บทความนี้ประกอบด้วย 3 ตอน คือ
- List the serial ports connected to the RPi with pySerial.
- List the serial ports connected to the RPi with pySerial and PyQt5.
- LEDs on/off via PyQt5 and serial communication.
ตรวจสอบการเชื่อมต่อ USB
คำสั่งสำหรับการตรวจสอบว่ามีอุปกรณ์ใดบ้างที่เชื่อมต่อกับพอร์ต USB ของ Raspberry Pi มีรูปแบบของคำสั่งดังนี้
lsusb
เมื่อสั่งงาน lsusb กับ Raspberry Pi ที่ไม่ได้เชื่อมต่ออุปกรณ์เพิ่มเติมใด ๆ จะได้ผลลัพธ์ดังภาพที่ 2
เมื่อเสียบ RS232-to-USB เข้ากับพอร์ต USB ดังภาพที่ 1 และสั่ง lsusb อีกครั้งจะได้้ผลลัพธฺดังภาพที่ 3 ซึ่งแสดงรายการอุปกรณ์เชื่อมต่อ Device 005 เป็น ID 0403:6015 Future Technology Devices International, Ltd Bridge ( I2C/SPI/UART/FIFO ) ซึ่งเป็นโมดูลแปลงสัญญาณ RS232-to-USB รุ่น ET-USB/RS232 MINI
นอกจากนี้ เมื่อเชื่อมต่อโมดูลแปลง USB เป็น RS232 เข้ากับพอร์ต จะได้ชื่อของอุปกรณ์เป็นไดเร็กทอรี เช่น /dev/ttyUSB0 เป็นต้น ดังนั้นเมื่อสังงานด้วยคำสั่งต่อไปนี้ จะได้ผลลัพธ์เป็นดังภาพที่ 4 และ 5 สำหรับกรณีที่ยังไม่ได้เสียบ ET-USB/RS232 MINI เข้ากับ RPi และผลลัพธ์หลังจากเสียบเข้ากับพอร์ต USB เป็นที่เรียบร้อยแล้วตามลำดับ
ls /dev/ttyUSB*
pySerial
ไลบรารี pySerial เป็นไลบรารีสำหรับเชื่อมต่อพอร์ตสื่อสารอนุกรมของภาษาไพธอน ทำให้สามารถสื่อสารอนุกรมกับอุปกรณ์ภายนอกได้ โดยคุณสมบัติของ pySerial เป็นดังนี้
- รองรับทุกแพล็ตฟอร์ม
- สามารถกำหนดคุณสมบัติของพิร์ตสื่อสารอนุกรมได้โดยตรงจากคำสั่งของไลบรารี
- รองรับข้อมูลหลายขนาด (different byte sizes) รองรับค่าบิตสต็อป รองรับการใช้บิตพาริตี และรองรับการควบคุมทิศทางการทำงานผ่านทางบิต RTS/CTS และ/หรือ Xon/Xoff
- ทำงานได้ทั้งแบบมีการตั้งหรือไม่ตั้งระยะเวลาไทม์เอาต์ (time out)
- ใช้คำสั่งรับและส่งเป็นคำสั่งเดียวกับไฟล์ คือ read และ write
- ไลบรารีเขียนด้วยไพธอน
- เข้ากันได้กับไลบรารี io
- พอร์ตถูกตั้งค่าให้รับและส่งข้อมูลแบบไบต์
ติดตั้ง
โดยปกติแล้ว pySerial ติดตั้งเป็นไลบรารีหลักตัวหนึ่งของ Python รุ่น 3 จึงไม่จำเป็นต้องติดตั้ง แต่สามารถสั่งการติดตั้ง pySerial ผ่านทาง pip สั่งงานดังรูปแบบคำสั่งต่อไปนี้
pip install pyserial
สำหรับกรณีที่ต้องการติดตั้งแบบอัพเกรดไลบรารีให้เป็นรุ่นใหม่ให้ใช้คำสั่งต่อไปนี้แทนคำสั่งด้านบน
pip install –upgrade pyserial
หลังจากติดตั้งเสร็จสิ้นหรืออัพเกรดเป็นรุ่นใหม่เป็นที่เรียบร้อยให้ตรวจสอบการใช้งานโดยเข้าโปรแกรม python และนำเข้า serial เพื่อแสดงรุ่นของไลบรารีดังภาพที่ 6
การสร้างวัตถุประเภทสื่อสารอนุกรม
การใช้งานไลบรารี pySerial จะต้องสร้างวัตถุประเภทสื่อสารอนุกรมขึ้นมาโดยรูปแบบของการสร้างวัตถุมีดังนี้
วัตถุ = serial.Serial(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False )
โดยที่
- port ชื่อของพอร์ตที่ต้องการเชื่อมต่อ ซึ่งมีชื่อเรียกแตกต่างกันไปตามระบบปฏิบัติการ เช่น /dev/ttyUSB0 สำหรับระบบปฏิบัติการ Linux หรือ COM4 บนระบบปฏิบัติการ Windows เป็นต้น
- baudrate เป็นค่าอัตราการสื่อสารในหน่วย bps (bits-per-second) ซึ่งมีค่าได้ดังนี้
- 50
- 75
- 110
- 134
- 150
- 200
- 300
- 600
- 1200
- 1800
- 2400
- 4800
- 9600
- 19200
- 38400
- 57600
- 115200
- 230400
- 460800
- 500000
- 576000
- 921600
- 1000000
- 1152000
- 1500000
- 2000000
- 2500000
- 3000000
- 3500000
- 4000000
- bytesize คือ ขนาดของข้อมูล ซึ่งมีค่าได้ดังนี้
- FIVEBITS
- SIXBITS
- SEVENBITS
- EIGHTBITS
- parity คือ การเปิดใช้งานการตรวจสอบการรับส่งด้วยการใช้พาริตีบิต ซึ่งสามารถกำหนดค่าได้ดังนี้
- PARITY_NONE
- PARITY_EVEN
- PARITY_ODD
- PARITY_MARK
- PARITY_SPACE
- stopbits คือ จำนวนบิตที่ใช้สำหรับเป็นบิตสิ้นสุด ซึ่งมีค่าได้ดังนี้
- STOPBITS_ONE
- STOPBITS_ONE_POINT_FIVE
- STOPBITS_TWO
- timeout คือ ค่าตัวเลขทศนิยมที่เป็นหน่วยเวลาของการตรวจสอบการเกิดไทม์เอาต์ของการอ่านข้อมูลจากพอร์ตสื่อสาร ดดยมีลักษณะของค่า 3 แบบ คือ
- None สำหรับให้รอจนกว่าจะมีข้อมูลเข้า
- 0 สำหรับการทำงานแบบ non-blocking หรือไม่ต้องรอจนกว่าจะมีข้อมูล นั่นหมายความว่าถ้าพบข้อมูลก็อ่านไม่พบก็ข้ามไปไม่ต้องรอ
- X สำหรับรอจนกว่าจะพบข้อมูลเป็นเวลา X วินาที
- xonxoff คือ การเปิดหรือปิดการทำควบคุมการรับส่งข้อมูลด้วยซอฟต์แวร์
- rtscts คือ การเปิดหรือปิดการทำควบคุมการรับส่งข้อมูลด้วยฮาร์ดแวร์ผ่านทางขา RTS/CTS สำหรับการส่งข้อมูล
- dsrdtr คือ การเปิดหรือปิดการทำควบคุมการรับส่งข้อมูลด้วยฮาร์ดแวร์ผ่านทางขา DSRDTR สำหรับการรับข้อมูล
- write_timeout คือ การกำหนดค่าไทม์เอาต์กรณีของการส่งข้อมูล ซึ่งปกติจะทำงานแบบ none-blocking หรือส่งเสร็จไม่ต้องรอ
ค่า exception ที่ได้มีดังนี้
- ValueError พารามิเตอร์ที่กำหนดมีค่าที่ไม่ถูกต้อง
- SerialException สำหรับกรณีที่ไม่พบอุปกรณ์ที่กำหนดใน port
การเปิดพอร์ต
การเปิดพอร์ตของวัตถุที่สร้างไว้ สามารถใช้งานตามคำสั่งต่อไปนี้
วัตถุ.open()
ปิดการเชื่อมต่อ
กรณีของการปิดการสื่อสารเมื่อไม่ต้องการใช้งานหรือถือครองพอร์ตเอาไว้ ให้เรียกใช้คำสั่งตามรูปแบบต่อไปนี้
วัตถุ.close()
ตรวจสอบสถานะ
การตรวจสอบว่าพอร์ตที่สร้างขึ้นนั้นถูกเปิดใช้งานไปก่อนหรือไม่สามารถใช้คำสั่งตรวจสอบดังนี้
ผลลัพธ์ = วัตถุ.isOpen()
ตัวอย่างการสร้าง เปิด ปิด และตรวจสอบสถานะเป็นดังภาพที่ 7 ซึ่งจะพบว่า หลังจากมีการสร้างวัตถุไปแล้วจะทำให้การเปิดการเชื่อมต่อนั้นทำงานทันที เมื่อสั่ง open() ซ้ำจะเกิดความผิดพลาด
ส่งข้อมูล
คำสั่งสำหรับการนำออกข้อมูลมีรูปแบบการใช้งานดังนี้
วัตถุ.write( ข้อมูล )
กรณีที่ต้องการนำออกข้อมูลจากบัฟเฟอร์ออกไปทั้งหมดเพื่อให้บัฟเฟอร์ว่างใช้คำสั่งดังนี้
วัตถุ.flushOutput()
รับข้อมูล
การรับข้อมูลเข้าสามารถทำโดยคำสั่งตามรูปแบบต่อไปนี้
ข้อมูล = วัตถุ.read()
ข้อมูล = วัตถุ.read( จำนวนไบต์ )
กรณีที่ต้องการล้างค่าของบัฟเฟอร์นำเข้าให้ใช้คำสั่งต่อไปนี้
วัตถุ.flushInput()
การตรวจสอบการรอเพื่อรับข้อมูลสามารถทำโดยใช้คำสั่ง inWaiting() ดังนี้
จำนวนข้อมูล = วัตถุ.inWaiting()
ตัวอย่างโปรแกรม
ขั้นตอนวิธีของโปรแกรมตัวอย่างเป็นดังนี้
- สร้างวัตถุ
- กำหนดชื่อพอร์ตให้กับวัตถุ
- เปิดพอร์ต
- ถ้า error แสดงว่าไม่มีพอร์ตนั้น
- ถ้าผ่านแสดงว่าพบพอร์ตตามชื่อที่เปิด
- ปิดพอร์ต
- ถ้ายังไม่ครบทุกชื่อให้เปลี่ยนชื่อ และกลับไป 3
ตัวอย่างโปรแกรมต่อไปนี้เป็นการแสดงรายชื่อพอร์ตอนุกรมที่เชื่อมต่อกับบอร์ด Raspberry Pi ผ่านทางพอร์ต USB และตัวอย่างผลลัพธ์ของโปรแกรมเป็นดังภาพที่ 8
import serial
s = serial.Serial()
portNames = [
"/dev/ttyUSB0",
"/dev/ttyUSB1",
"/dev/ttyUSB2",
"/dev/ttyUSB3",
"/dev/ttyACM0",
"/dev/ttyACM1",
"/dev/ttyACM2",
"/dev/ttyACM3"
]
for pname in portNames:
try:
s.port = pname
s.open()
if s.isOpen():
print("Found {}.".format(pname))
except:
pass
print("End of program.")
สรุป
จากบทความนี้จะพบว่าการตรวจสอบว่ามีพอร์ตสื่อสารใดบ้างที่ถูกใช้ในการต่อเชื่อมระหว่างอุปกรณ์กับบอร์ด Raspberry Pi นั้นทำได้ด้วยการกำหนดชื่อพอร์ตที่ต้องการตรวจสอบ เมื่อทำการเปิดใช้งานพอร์ตแล้วเกิดความผิดพลาดย่อมแสดงว่าพอร์ตนั้นไม่ได้เชื่อมต่อ ด้วยหลักการนี้จึงถูกนำมาแปลงเป็นการเขียนโปรแกรมตัวอย่าง นอกจากนี้จะพบว่า ถ้าต้องการนำโปรแกรมนี้ไปใช้งานกับระบบปฏิบัติการอื่น ได้แก่ Microsoft Windows หรือ Apple macOS จะต้องเปลี่ยนชื่อของพอร์ตที่ต้องการค้นหาให้เหมาะสมกันด้วย ดังนี้
- Windows ใช้ชื่อขึ้นต้นเป็น COM
- Linux จะใช้ชื่อขึ้นต้นด้วย /dev/ttyUSB และ /dev/ttyACM
- macOS จะใช้ชื่อขึ้นต้นเป็น /dev/cu.
สุดท้ายนี้ หวังว่าบทความนี้คงมีประโยชน์บ้างไม่มากก็น้อย และขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดต้องการพูดคุยสามารคอมเมนท์ได้เลยครับ
แหล่งอ้างอิง
- PyPi : pyserial
- pythonhost.org: Welcome to pySerial’s documentation.
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-10-26, 2022-01-01