Ruby + serialport + Sinatra で、シリアル(COM)ポートから受信したデータを表示する

以前、Windows Form を使ってシリアル(COM)ポートでの通信を試してみました。
WindowsFormで、シリアルCOMポートから受信したデータを処理する

.Net以外でのシリアルポートで通信する方法を調べてみたところ、 Rubyserialport gemがあったたため試してみました。

 

環境

 

アプリの流れ

  1. localhost:4567 にアクセス
  2. シリアルポートを選択して、受信ボタンを押す
  3. Z-1170によるアップロード
  4. アップロードしたデータを画面に表示

 

内蔵シリアルポート一覧の取得

以下を参考に、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つのデータを受信・表示しています。

実行前

f:id:thinkAmi:20140528055740p:plain

実行後

f:id:thinkAmi:20140528055754p:plain

 

ソースコード

Gistに上げておきました。
Ruby + serialport + Sinatra で、シリアルCOMポートから受信したデータを表示するサンプル

なお、1ファイルでSinatraを書いたことがなかったので、それを試しています。

 

その他参考