Physical computing ︰《四》 Serial Port ︰ 2. 讀碼學寫

180px-Sillysymphonies-uglyducking1939

220px-Mute_Swan_Cygnets_detail

 

辛棄疾‧醜奴兒

少年不識愁滋味,
愛上層樓,愛上層樓。
為賦新詞強說愁。

而今識盡愁滋味,
欲說還休,欲說還休。
卻道天涼好個秋。

在《 λ 運算︰概念導引《四》》一文中,我們提到了竿影測時的『華表』或許淵源於『仿生』的脛骨。『模仿』的學習方法,就如《何謂寫程式?!》所說的︰

古今文人經由唸『好文章』來學寫『文章』;正像是今天透過讀『好程式』開始設計『程式』一樣,使用適當的『程式庫』 library ,就是『出典』的吧!所以何不《吃著魚釣魚!!有何不可?》的呢!!

Minicom 』是一個 Linux 上『終端機』模擬軟體,據 WiKi 上講︰

A common use for Minicom is when setting up a remote serial console, perhaps as a last resort to access a computer if the LAN is down.

曾經是 PC + Modem + Netscape 上網際網路衝浪,古早有 Mainframe + Terminal ,從應用的角度來看,序列埠可以說是軟硬體『常青樹』的吧!

Stdstreams-notitle.svg
終端機 text terminal 

220px-Analogue_modem_-_acoustic_coupler
數據機 Modem

樹莓派基金會大力推動 python 語言, python 上有一個著名的 pyserial 的序列埠程式庫,現今的版本是 v2.7 ,在 raspbian 裡可以這樣安裝︰

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python-pip
sudo pip install pyserial

# 簡易終端機範例
pi@raspberrypi ~ $ which miniterm.py
/usr/bin/miniterm.py

# v2.7 版 Source Code
pi@raspberrypi ~ $ more /usr/bin/miniterm.py

#!/usr/bin/python

# Very simple serial terminal
# (C)2002-2009 Chris Liechti <cliechti@gmx.net>

# Input characters are sent directly (only LF -> CR/LF/CRLF translation is
# done), received characters are displayed as is (or escaped trough pythons
# repr, useful for debug purposes)

import sys, os, serial, threading

EXITCHARCTER = '\x1d' # GS/CTRL+]
MENUCHARACTER = '\x14' # Menu: CTRL+T

def key_description(character):
"""generate a readable description for a key"""
ascii_code = ord(character)
if ascii_code < 32:
return 'Ctrl+%c' % (ord('@') + ascii_code)
else:
return repr(character)
--More--(2%)
……

它裡頭附有一個『 /usr/bin/miniterm.py 』之簡單終端機程式,可以當作『序列埠』程式的『學習範本』,更由於 python 是互動式的『解譯器』,下述 v2.2 版本的程式之簡短,相信讀者自能從

python miniterm-v2.2.py -p /dev/ttyUSB0 -b 115200 

體會『讀寫』程式的吧!!

#!/usr/bin/env python

# Very simple serial terminal
# (C)2002-2004 Chris Liechti <cliecht@gmx.net>

# Input characters are sent directly (only LF -> CR/LF/CRLF translation is
# done), received characters are displayed as is (or as trough pythons
# repr, useful for debug purposes)
# Baudrate and echo configuartion is done through globals
import sys, os, serial, threading, getopt

EXITCHARCTER = ‘\x04’ #ctrl+D

#first choose a platform dependant way to read single characters from the console
if os.name == ‘nt’:
import msvcrt
def getkey():
while 1:
if echo:
z = msvcrt.getche()
else:
z = msvcrt.getch()
if z == ‘\0’ or z == ‘\xe0’: #functions keys
msvcrt.getch()
else:
if z == ‘\r’:
return ‘\n’
return z

elif os.name == ‘posix’:
import termios, sys, os
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
new[6][termios.VMIN] = 1
new[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, new)
s = ” # We’ll save the characters typed and add them to the pool.
def getkey():
c = os.read(fd, 1)
#~ c = sys.stdin.read(1)
if echo: sys.stdout.write(c); sys.stdout.flush()
return c
def clenaup_console():
termios.tcsetattr(fd, termios.TCSAFLUSH, old)
sys.exitfunc = clenaup_console #terminal modes have to be restored on exit…

else:
raise “Sorry no implementation for your platform (%s) available.” % sys.platform

CONVERT_CRLF = 2
CONVERT_CR = 1
CONVERT_LF = 0

def reader():
“””loop forever and copy serial->console”””
while 1:
data = s.read()
if repr_mode:
sys.stdout.write(repr(data)[1:-1])
else:
sys.stdout.write(data)
sys.stdout.flush()

def writer():
“””loop and copy console->serial until EOF character is found”””
while 1:
c = getkey()
if c == EXITCHARCTER:
break #exit app
elif c == ‘\n’:
if convert_outgoing == CONVERT_CRLF:
s.write(‘\r\n’) #make it a CR+LF
elif convert_outgoing == CONVERT_CR:
s.write(‘\r’) #make it a CR
elif convert_outgoing == CONVERT_LF:
s.write(‘\n’) #make it a LF
else:
s.write(c) #send character
#print a short help message
def usage():
sys.stderr.write(“””USAGE: %s [options]
Miniterm – A simple terminal program for the serial port.

options:
-p, –port=PORT: port, a number, default = 0 or a device name
-b, –baud=BAUD: baudrate, default 9600
-r, –rtscts: enable RTS/CTS flow control (default off)
-x, –xonxoff: enable software flow control (default off)
-e, –echo: enable local echo (default off)
-c, –cr: do not send CR+LF, send CR only
-n, –newline: do not send CR+LF, send LF only
-D, –debug: debug received data (escape nonprintable chars)

“”” % (sys.argv[0], ))

if __name__ == ‘__main__’:
#initialize with defaults
port = 0
baudrate = 9600
echo = 0
convert_outgoing = CONVERT_CRLF
rtscts = 0
xonxoff = 0
repr_mode = 0

#parse command line options
try:
opts, args = getopt.getopt(sys.argv[1:],
“hp:b:rxecnD”,
[“help”, “port=”, “baud=”, “rtscts”, “xonxoff”, “echo”,
“cr”, “newline”, “debug”]
)
except getopt.GetoptError:
# print help information and exit:
usage()
sys.exit(2)

for o, a in opts:
if o in (“-h”, “–help”): #help text
usage()
sys.exit()
elif o in (“-p”, “–port”): #specified port
try:
port = int(a)
except ValueError:
port = a
elif o in (“-b”, “–baud”): #specified baudrate
try:
baudrate = int(a)
except ValueError:
raise ValueError, “Baudrate must be a integer number, not %r” % a
elif o in (“-r”, “–rtscts”):
rtscts = 1
elif o in (“-x”, “–xonxoff”):
xonxoff = 1
elif o in (“-e”, “–echo”):
echo = 1
elif o in (“-c”, “–cr”):
convert_outgoing = CONVERT_CR
elif o in (“-n”, “–newline”):
convert_outgoing = CONVERT_LF
elif o in (“-D”, “–debug”):
repr_mode = 1

#open the port
try:
s = serial.Serial(port, baudrate, rtscts=rtscts, xonxoff=xonxoff)
except:
sys.stderr.write(“Could not open port\n”)
sys.exit(1)
sys.stderr.write(“— Miniterm — type Ctrl-D to quit\n”)
#start serial->console thread
r = threading.Thread(target=reader)
r.setDaemon(1)
r.start()
#and enter console->serial loop
writer()

sys.stderr.write(“\n— exit —\n”)

 

此致『 K 程式』更有興趣的讀者︰

pyserial/serial/tools/miniterm.py 》;

需要文件?

Welcome to pySerial’s documentation 》。