以前、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を書いたことがなかったので、それを試しています。