◇USB-シリアル-Python/Java:Raspi/Ubntu/WIn
RasPiでUSBシリアルアクセスする
RaspberryPi3にはデフォルトでUSBシリアル(FT232)のドライバが組まれているため、 変換ケーブルをUSBに挿してシリアルアクセスすることができます。
Raspi2台をUSBシリアルで対向させてみました。
「USB-DSUB9pinケーブル」2本をDSUBクロスアダプタ(NULLMODEM)でつないで、両USBを2台のRaspiに繋ぎます。
端末ソフトcuを用いてアクセス
まず、USB-シリアルがデバイスとしてどう見えているかをdmesgコマンドで調べます。
$ dmesg | grep FTDI usb 1-1.2: Manufacturer: FTDI usbserial: USB Serial support registered for FTDI USB Serial Device [ftdi_sio 1-1.2:1.0: FTDI USB Serial Device converter detected usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0 ftdi_sio ttyUSB0: FTDI USB Serial Device converter now disconnected from ttyUSB0通常"ttyUSB0"になるようです。/dev/ttyUSB0がデバイスとなります。
cuコマンドがインストールされていない場合apt-getでインストールします。
$ sudo apt-get install cu
アクセス前にケーブルが挿さっていることを確認の上デバイスのモードを666に変えておきます。
$ ls -l /dev/ttyUSB0 # ケーブルが刺さっていれば次のような行がでる crw-rw---- 1 root dialout 188, 0 11月 26 22:01 /dev/ttyUSB0 $ sudo chmod 666 /dev/ttyUSB0 # 立ち上げ直したりケーブルを挿しかえる度に必要双方のRasPiでcuコマンドを起動します。
cuコマンドでは-sでボーレート、-lでデバイスを指定します。
$ cu -s 115200 -l /dev/ttyUSB0 Connected. (シリアルアクセスの開始) hello (相手が打った文字が出る:こちらで打ったものは相手側に出る)キーを打つと、相手側に表示されます。
~.(チルダドット)を打つとcuは切断し終了となります。
(切断といってもこちら側での話で相手には切断したことは伝わりません)
Pythonで装置側とコントロール側を模してアクセス
RasPi2台を簡単な装置役とコントローラー役に分けてシリアル通信を行うプログラムを作成してみました。
言語としてPythonを用いています。 Pythonは変数の宣言や型定義がなく極めて貧弱で危険な言語ですので、Javaの導入を別途検討しています。
装置役のプログラムはカウンターを持ち3秒に1回カウントアップしてはシリアル端子に値を出します。
シリアル端子から"reset"コマンドを受け取るとカウンタをリセットします。
"stop"コマンドを受け取るとプログラムを停止終了します。
コントローラー役のプログラムはキーボードから人間が入力するコマンドを解釈し、シリアル端子に制御コマンドを出します。
装置から送られてくる最新値を記憶し、人間からの要求コマンドで画面に記憶してある値を表示します。
装置役プログラム
# -*- coding: utf-8 -*- # 内部カウンタの値をUSBシリアルに出力し続ける疑似装置 # USBシリアルから次のコマンドを受け付ける # reset : カウンタを0に戻す # stop : 終了する (quit/end/exit)も可 import threading import time import serial class WriteThread(threading.Thread): def __init__(self,com_): # 決まり事 super(WriteThread, self).__init__() # 必須 self.com = com_ # 注意!selfを忘れるとグローバル変数になる self.count = 0 self.stop_flag = False def run(self):# 決まり事 while( not self.stop_flag ): self.count = self.count + 1 self.com.write("count="+str(self.count)+"\r\n") time.sleep(3) class ReadThread(threading.Thread): def __init__(self,com_,writer_): # 決まり事 super(ReadThread, self).__init__() # 必須 self.com = com_ self.writer = writer_ def run(self): # 決まり事 while(True): line = readline(self.com) if( line=="reset" ): self.writer.count=0 if( line=="quit" or line=="end" or # 行分割は行の最後に \ line=="exit" or line=="stop" ): self.com.write(line+"\r\n") self.com.close() self.writer.stop_flag = True break def readline(com_): # Srialのreadlineは2.6以降は行が読めないので自作 line_raw = "" while(True): char = com_.read() if( char=="" ): return "" if( char=="\n" or char=="\r"):break line_raw = line_raw+char line = line_raw.decode('utf-8') return line def main(): com = serial.Serial('/dev/ttyUSB0',115200) writer = WriteThread(com) writer.start() reader = ReadThread(com,writer) reader.start() if __name__ == '__main__': main()
コントローラー役プログラム
# -*- coding: utf-8 -*- # USBシリアルに繋がった疑似装置を制御する。 # 次のキーボード入力コマンドがある # reset: 疑似装置にresetコマンドを送る # get : 疑似装置から受っとった最新の値を表示する # end : 終了する # stop : 疑似装置を停止する import threading import time import serial import os class WriteThread(threading.Thread): def __init__(self,com_,reader_): super(WriteThread, self).__init__() # 必須 self.com = com_ self.reader = reader_ def run(self): while( True ): command_raw = raw_input("> ") command = command_raw.decode('utf-8') if( command == "reset" ): self.com.write("reset\r\n"); elif( command == "get" ): print self.reader.value elif( command == "end" ): os._exit(0) break elif( command == "stop" ): self.com.write("stop\r\n") elif( command == "help" or command =="?" ): print "get: print current value" print "reset: reset counter on equipment" print "end: end this program" print "stop: stop equipment" else: print "unknown command" class ReadThread(threading.Thread): def __init__(self,com_): super(ReadThread, self).__init__() # 必須 self.com = com_ self.value = "" self.stop_flag = False def run(self): while(True and not self.stop_flag): line = readline(self.com) if( line != "" ):self.value = line def readline(com_): # PySrialのreadlineは2.6以降は行が読めないので自作 line_raw = "" while(True): char = com_.read() if( char=="" ): return "" if( char=="\n" or char=="\r"):break line_raw = line_raw+char line = line_raw.decode('utf-8') return line def main(): com = serial.Serial('/dev/ttyUSB0',115200) reader = ReadThread(com) writer = WriteThread(com,reader) reader.start() writer.start() if __name__ == '__main__': main()
Pythonプログラミング注意点
前の記事◇Pythonプログラミングにおける最重要事項 に幾つかの注意事項を上げました。
それ以外の点として
・serial.Serialのgetlineは使い物にならない
serial.SerialのgetLineは"\r\n"を行の終わりと解釈しますので、 cuのように"\n"のみを送る相手からの行を受け取ることはできません。ここでは行制御関数readline(com_)を自作しました。
・serial.Serialにcancel_read()はない
cancel_read()でread()を止めることはできません。
・スレッドでは先頭にsuper(xxx,self).__init__()
これがないと実行時エラーとなります。
Ubunto(intel 64bit)/Java
ubntuでもUSBシリアルドライバは標準で入っており、cuコマンドも、インストールすれば同じように動きました。
$ sudo apt install cu後は/dev/ttyUSB0になることなども含め同じです。
JavaでUSBシリアルを使うにはlibrxtx-javaをインストールする必要があります。
$ sudo apt-get install librxtx-java 次のファイルが作られます /usr/share/java/RXTXcomm.jar /usr/lib/jni/librxtxSerial.so javaコンパイル時にこのjarを指定します javac -classpath .:/usr/share/java/RXTXcomm.jar sTest.java 実行時はjarとsoを指定します export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/jni/ export CLASSPATH=.:$CLASSPATH:/usr/share/java/RXTXcomm.jar java sTest
次のような形でアクセスできます。
ライブラリを標準化する
RXTXcomm.jarとlibrxtxSerial.soを「標準」扱いとし、java/javacで明示する必要をなくすには 次の手続きをとります。
# RXTXcomm.jarをJavaのデフォルト検索フォルダに入れる
# まずはjreフォルダ検索
$ sudo find / -name jre -print
/usr/lib/jvm/java-7-oracle/jre
# jreの下のlib/extフォルダに入れる(lib/ではなくlib/ext/)
$ sudo cp /usr/share/java/RXTXcomm.jar \
/usr/lib/jvm/java-7-oracle/jre/lib/ext/
#--------------
# librxtxSerial.soを標準にする
$ cd /etc/ld.so.conf.d/
$ sudo vi jni.conf
/usr/lib/jni/ <- この1行だけ入れて保存
$ sudo ldconfig -v
Windows(64bit)JavaでUSB-シリアルアクセスする
http://jlog.org/rxtx-win.html などから、
- RXTXcomm.jar
- rxtxSerial.dll
RXTXcomm.jarをJavaの\lib\extフォルダ、例えば
C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext C:\Program Files\Java\jre7\lib\extにコピーします。
rxtxSerial.dllはbinフォルダ、例えば
C:\Program Files\Java\jdk1.7.0_71\jre\bin C:\Program Files\Java\jre7\binにコピーします。
| 固定リンク

