以前、Windows Form を使ってシリアル(COM)ポートでの通信を試してみました。
WindowsFormで、シリアルCOMポートから受信したデータを処理する
.Net以外でのシリアルポートで通信する方法を調べてみたところ、 Rubyのserialport
gemがあったたため試してみました。
環境
アプリの流れ
localhost:4567
にアクセス- シリアルポートを選択して、受信ボタンを押す
- Z-1170によるアップロード
- アップロードしたデータを画面に表示
内蔵シリアルポート一覧の取得
以下を参考に、win32ole
を使って取得しました。
利用可能なシリアルポート一覧を取得する方法(Windows) | TipsZone
なお、今回は内蔵のシリアルポートのみ対象にしたため、 Win32_SerialPort
のみ取得します。
また、取得したシリアルポート名称はそのまま使うとSinatraで表示する際に文字化けをするため、UTF-8に変換します。
def list_com_port locator = WIN32OLE.new("WbemScripting.SWbemLocator") services = locator.ConnectServer() result = {} services.ExecQuery("SELECT * FROM Win32_SerialPort").each do |item| # 取得したitem.Nameはそのままでは文字化けするため、UTF-8にする result[item.DeviceID] = to_utf8(item.Name) end result end def to_utf8(str) str ? NKF.nkf('-w', str) : "" end
シリアルポートからの受信
以下の公式ドキュメントやBlogを参考にしました。
接続については公式ドキュメントに
This behaves like SerialPort#new, except that you can pass a block to which the new serial port object will be passed. In this case the connection is automaticaly closed when the block has finished.
と記載されていたため、今回は new
メソッドではなく、open
メソッドを使いました。
This behaves like SerialPort#new — Documentation for serialport (1.3.0)
また、操作待ちのための sleep 5
を入れています。
ほかに、例外 EOFError
を捉えることで受信完了とみなすために read_timeout
の値を設定しました。デフォルトの 0
のままでは例外は発生せず、ずっと待っているような動きでした。
def recieve(port_name) result = [] # close()のことを考えないよう、open()を使うようにしてみた SerialPort.open(port_name, baud: 9600, data_bits: 8, stop_bits: 1, parrity: SerialPort::NONE) do |sp| # 操作する時間も考えて、とりあえず5秒待つ sleep 5 # 指定しなかったり、"0"だと常に待ち続けてしまうので、 # とりあえず"5000"ミリ秒待っても来ない場合はタイムアウトにしとく sp.read_timeout = 5000 loop do begin # 読み込んだデータには末尾に改行と空白が入っているので、それらを落とす r = sp.readline.chomp.strip p r result << r # readline()は最後まで来るとEOFを返すので、それで通信終了とみなす rescue EOFError break end end end result end
スクリーンショット
Z-1170に「1,2,3」の3つのデータを受信・表示しています。
実行前
実行後
ソースコード
Gistに上げておきました。
Ruby + serialport + Sinatra で、シリアルCOMポートから受信したデータを表示するサンプル
なお、1ファイルでSinatraを書いたことがなかったので、それを試しています。