โชคดีที่ทางผู้พัฒนา Scratch ได้สร้างสิ่งที่เรียกว่า Remote Sensors Protocol ไว้ ทำให้ Scratch 1.4 สามารถสื่อสารได้กับโปรแกรมอื่นได้ ซึ่งมีประโยชน์ตรงที่ทำให้ Scratch 1.4 สามารถยืมความสามารถจากโปรแกรมอื่น หรือโปรแกรมที่พัฒนาขึ้นมาเองได้ ซึ่งเป็นการขยายความสามารถออกไปได้มากกว่ากว่าการใช้ GPIO Server อย่างเดียว และในบทความตอนนี้จะใช้ Protocol นี้ทำงานร่วมกับโปรแกรมที่พัฒนาด้วยภาษา Python ครับ
รู้จักกับ Remote Sensors Protocol
1. การเปิดใช้งาน
การเปิดใช้งาน Protocol นี้ต้องอาศัย sensor value block หรือ sensor block ซึ่งอยู่ในกลุ่ม Sensing Blocksโดยการเลื่อนเมาส์ไปวางบน Block อันใดอันหนึ่ง แล้วทำการคลิ๊กขวา จะเห็น popup menu ดังภาพ แล้วเลือกรายการ enable remote sensor connections
แล้วจะได้พบกับ message windows ดังภาพ คลิ๊ก OK เพื่อปิดหน้าต่าง
หลังจากนี้ Scratch 1.4 จะทำการเปิิดพอร์ตสื่อสาร หมายเลข 42001 ขึ้นมาเพื่อรอการสื่อสารจากโปรแกรมอื่น
2. โครงสร้างข้อความ
การสื่อสารระหว่างโปรแกรมอื่นกับ Scratch 1.4 จะใช้การส่งข้อความไปมาระหว่างกัน โดยข้อความนั้นแบ่งเป็นสองส่วนดังภาพส่วนที่เป็น Message Length จะมีความยาวคงที่คือ 4 Bytes โดยจะบรรจุค่าความยาวของ Message หรือข้อความจริงไว้ และส่วนที่เหลือจะเป็นส่วนที่เก็บข้อความที่ใช้จริง ข้อความที่ใช้ได้คือ
1. ข้อความคำเดียว (single word string) เช่น cat ,book-1, hello, etc
2. ข้อความที่มีหลายคำ โดยต้องเขียนไว้ในเครื่องหมายคำพูด เช่น "hello world", "sing a song", etc
3. ตัวเลข 1,2,3,-4.0,-1200, etc
4. ค่าทางตรรกะ คือ true , false
3. Block ที่ใช้ในรับ-ส่งข้อความ
3.1 รับข้อความจากโปรแกรมอื่น
เช่น
หมายถึง เมื่อ Scratch 1.4 รับข้อความ "cat" จากโปรแกรมภายนอก ให้ทำเสียง "meow"
3.2 ส่งข้อความออกไป
เช่น
เป็นการส่งข้อความ "I see a cat" ออกไปยังโปรแกรมข้างนอก
การสื่อสารระหว่าง Scratch 1.4 กับ Python 3.x
ทีมงานผู้พัฒนา Scratch ได้ทำตัวอย่างการสื่อสารระหว่าง Scratch กับ Python ไว้แล้ว (ดูเพิ่มเติม)แต่เป็นชุดคำสั่งที่ใช้งานได้กับ Python 2.x ผมได้นำมาดัดแปลงนิดหน่อยเพื่อให้งานได้กับ Python3.x ดังนี้ (หากต้องการใช้งานกับ Python 2.X ท่านสามารถดาวน์โหลดได้จากที่นี่)
ScratchPy.py : (ดาวน์โหลด)
import socket
import time
from array import array
import struct
#reference to https://wiki.scratch.mit.edu/wiki/Communicating_to_Scratch_via_Python
class Py2Scratch14():
def __init__(self,host='localhost',port=42001):
#the first 4-byte of message contains size of the real message
self.prefix_len = 4
self._port = port
self._host = host
self._socket = None
def connect(self):
try:
self._socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self._socket.connect((self._host,self._port))
except socket.error :
self._socket = None
def send(self,cmd):
if self._socket is None :
return
n = len(cmd)
#to create first 4 bytes
a = array('u') #array of unicode
a.append(chr((n >> 24) & 0xFF)) # first left most byte
a.append(chr((n >> 16) & 0xFF)) # second byte
a.append(chr((n >> 8) & 0xFF)) # third byte
a.append(chr(n & 0xFF)) # forth byte
self._socket.send(a.tostring() + cmd)
def sendCMD(self,cmd):
#this can be used with Python3
if self._socket is None :
return
# first 4 bytes contains size of message
self._socket.send(len(cmd).to_bytes(4, 'big'))
# then send the command to Scracth
self._socket.send(bytes(cmd,'UTF-8'))
def _read(self, size):
"""
Reads size number of bytes from Scratch and returns data as a string
"""
data = b''
while len(data) < size:
try:
chunk = self._socket.recv(size-len(data))
except socket.error :
pass
if chunk == '':
pass
data += chunk
return data
def _recv(self):
"""
Receives and returns a message from Scratch
"""
prefix = self._read(self.prefix_len)
msg = self._read(self._extract_len(prefix))
#return prefix + msg
return msg
def receive(self):
in_msg = self._recv().decode('UTF-8')
return self._parse(in_msg)
def _extract_len(self, prefix):
"""
Extracts the length of a Scratch message from the given message prefix.
"""
return struct.unpack(">L", prefix)[0]
def _parse(self, msg):
msg = msg.replace('"','')
splited = msg.split(" ")
if len(splited) == 2 :
return (splited[0],splited[1],None)
else:
return (splited[0],splited[1],splited[2])
def sensorupdate(self, data):
"""
Given a dict of sensors and values, updates those sensors with the
values in Scratch.
"""
if isinstance(data, dict):
msg = 'sensor-update '
for key in data.keys():
msg += '"%s" "%s" ' % (str(key), str(data[key]))
self.sendCMD(msg)
def broadcast(self,msg):
_msg = 'broadcast "'+msg+'"'
self.sendCMD(_msg)
ต่อไปนี้เราจะสามารถนำเอา Python นี้ทำหน้าที่เป็นตัวกลางสื่อสารระหว่าง โปรแกรมอื่นที่เขียนด้วย Python กับ Scratch 1.4ในตัวอย่าง
ตัวอย่าง
จะทดสอบโดยการสั่งให้ Sprite ใน Scratch หมุนตามเข็มนาฬิกา 360 องศา เมื่อได้รับข้อความว่า "rotate_right" หลังจากหมุนเสร็จก็ส่งข้อความ "finish" ไปบอก Python
Python code : test_scratch_01.py
from ScratchPy import Py2Scratch14
sc = Py2Scratch14()
sc.connect() # start connect to Scratch
sc.broadcast("rotate_right")
rcv = sc.receive()
if rcv[1] == 'finish' :
sc.broadcast("rotate_left")
Scratch Script :
ขั้นตอนการทำงาน
1. เปิดใช้งาน Remote Sensor Protocol ตามที่กล่าวมาข้างต้น
2. สร้าง Scratch script ตามตัวอย่าง
3. เขียน Python ตามตัวอย่าง บันทึกในชื่อ test_scratch_01.py
4. ใช้คำสั่ง python3 test_scratch_01.py
[ควบคุม Servo motor ด้วย REMOTE SENSORS PROTOCOL]
Sign up here with your email
ConversionConversion EmoticonEmoticon