Python

Pythonでシリアル通信する方法:計測器等から数値を取得

更新日:

製造業でエンジニアをしていると、「計測器等から数値を自動取得したい」という案件に出くわします。そこで計測器等からシリアル通信を利用して、数値を取得するソフトウェアを作るわけですね。

製造業界の標準はわかりませんが、私の界隈ではWindowsソフトウェアは「C#やVB、VBA」で作られることが多く、その辺の事情に疎い私には作れません。

そこで、シリコンバレーのPythonマスター:酒井先生のおかげでちょっとできるようになってきたPythonを使って計測器からシリアル通信でデータを取得するプログラムを作ってみました。これ、イケますよ!!

私の環境は以下の通りです。

  • Win7 Pro 32bit
  • Python3.6(Anaconda)
  • 計測器:シリアルデータ垂れ流しの機器

Pythonでシリアル通信する方法

Pythonでシリアル通信によるデータ取得を実現するためには、以下のステップで作業します。

ステップ

  • 計測器の設定(ドライバ等)
  • PythonにpySerialのインストール
  • Pythonプログラミング

以上、簡単なようですが初心者の私にはけっこうハマりどころが多く、「通信はしているようだけど、欲しいデータが取れない」状況でずいぶん悩みました。が、結果思うようなソフトが作れましたので、Python+シリアル通信はおすすめですよ!なんといってもモジュールをインストールするだけでシリアル通信プログラムが書けるなんて、本当に楽だと思います。

それでは、各項目を詳しく解説していきます。

計測器の設定(ドライバ等)

私が使った計測器は、下記のような仕様でした。

  • USBポートが仮想COMポートになっている
  • ボタン押下で計測した数値をシリアル出力

上記の内容が何のことかわからない場合は、計測器のマニュアルを読むとよいでしょう。通信仕様が書かれている親切なものもありますし、計器によっては公開されていない場合もあります。

最近はUSB通信でデータの転送ができるものが増えてきましたが、USB通信といっても中身は「仮想COM」で、実際にはシリアル通信という仕様も少なくありません。私が使用した計測器もこの「仮想COM」仕様でした。USBポートはあるものの、Windowsのデバイスマネージャを開くと、USB Serial PortとしてCOM番号とともに認識されています。

この通信仕様であれば、以下で説明するPython+pySerialモジュールでデータ取得が可能になります。通常は計測器をUSBケーブルでPCに接続するだけで、汎用ドライバがインストールされ、計測器をUSB Serial Port(COM4)などと認識します。ただし、計測器のメーカによってはドライバが必要になることがあるかもしれません。

PythonにpySerialのインストール

Pythonでシリアル通信を実現するモジュールはいくつかあるようです。しかし、このpySerialが一番日本語の解説が多く、ソースコードもわかりやすかったため選びました。ハマりどころはありましたが、pySerialのインストールから実際のデータ取得までは非常にスムーズにできました。

私はPython環境をAnacondaで構築していますので、AnacondaにpySerialモジュールを下記の方法でインストールしました。

1.Anaconda Promptの起動

Windowsマークを押しAnaconda3フォルダ内のAnaconda Promptをクリックします。(都合でWin10の画像になっていますが、Win7でも操作方法は同じです。)

2.pySerialのインストール

起動したAnaconda Prompt画面に、下記のコマンドを入力しエンターキーを押します。

pip install pyserial

インターネットに接続できていれば、正常にインストールできるでしょう。

Pythonプログラミング

これでPythonプログラムからシリアル通信にて計測器データを取得する準備ができました。次はいよいよPythonプログラミングです。

今回使用した計測器は、CLEARボタン押下で取得したデータを出力する単純な仕様になっていたため、簡単なソースコードでデータ取得できました。しかし!!!取得したデータに「符号(+記号など)」や、アルファベットなど不要な文字が含まれていたため、非常に苦労しました。

実際に完成したコードは以下の通りです。

import serial
 
comport = serial.Serial('COM4', baudrate=19200, parity=serial.PARITY_NONE)
recv_data = comport.read(12)
meas_data = float(recv_data.decode('utf-8').split('+')[1])            
comport.close()

コード解説

import serial

これはpythonにpySerialモジュールを読み込むおまじないですね。
このときにimport pySerialなどとしないよう注意します。大文字ー小文字の違いにも気を付けてください。

comport = serial.Serial('COM4', baudrate=19200, parity=serial.PARITY_NONE)

ここで計測器のシリアルポート設定をしています。

serial.Serial('COM4', baudrate=19200, parity=serial.PARITY_NONE)でどういった設定でシリアル通信するのかを書き、その設定をcomportという変数に格納します。

Windowsで表示されたCOMポート番号と同じポート番号を設定し、ボーレートも合わせます。parityについては未設定でも問題ないかもしれませんが、お約束なので入れておきましょう。

Windows側のCOMポートの設定状況を確認するには、計測器をパソコンに接続した状態で

  1. コントロールパネル
  2. デバイスとプリンターからデバイスマネージャ
  3. ポート

と進み、計測器のポート番号(USB Serial Port(COM4)等)を右クリックしプロパティを見ることで確認できます。

recv_data = comport.read(12)

このコードでやっとPCと計測器が通信します。今回の場合は計測器側からCLEARボタンを押すことでデータを出力する仕様でしたので、プログラムを実行しておけば、CLEARボタンを押したタイミングでデータを取得し、recv_dataという変数に格納します。

comport.read(12)readメソッドで通信先の計測器からデータを読み出しています。このときの(12)は、計測器から出力されるデータのうち12桁までを吸い上げるという意味です。

meas_data = float(recv_data.decode('utf-8').split('+')[1])

最後のこのコードのところで本当にハマりました。例えば、取得したいデータがマルチメータの12.56mvなどという値で、素直に12.5698700000などというデータが取得できればよかったのですが、まず文字化けしてしまって読めませんでした。通信に失敗しているのかと思いましたが、どうも文字コードが怪しいと踏んでいろいろ試した結果が、decodeメソッドです。

decodeは文字コードを変換するメソッドで、今回は取り出したデータrecv_dataを標準的なutf-8に変換します。ただ、、、これに符号が付いていたりアルファベットが付いていたりします。

そこで不要な符号を除去するためにsplit関数を使って必要な部分だけ取り出します。

recv_data.decode('utf-8').split('+')[1]とすると、recv_dataをutf-8に文字コード変換したあと、その文字を+という符号を境に切り分けてくれます。この切り分けられた文字たちは配列に入るため、[1]として、配列の2番目(0,1,2,3・・・となっているため[1]が2番目になります)だけ取り出しますよ、と設定します。

さらにその文字をfloatで型変換し、このあと扱いやすい 浮動小数点数に変換。それを最終的にmeas_dataという変数に納めます。このmeas_dataに入っている値こそが、今回取りたかった値なのでした。

comport.close()

最後は、通信のために開いたポートを閉じてあげます。お作法ですね。

計測器が接続されたシリアルポートを自動で探してくる方法

計測器のシリアルポートのCOM番号は何かの拍子で変わることがあります。そのたびにWindowsのデバイスマネージャを開いて・・・ポート番号を調べて・・・とやっていては手間です。

そこで以下のような方法でポート番号が変わっても自動で検索してポート番号を設定させることができます。pySerialライブラリをインストールすることで使えるようになるserial.toolsからlist_portsを読み込んで実装します。

実装例:

from serial.tools import list_ports

device_name = "***"

def comport_search():
    ports = list_ports.comports()
    device = [info for info in ports if device_name in info.serial_number]
    try:
        return device[0].device
    except: 
        print("計測器が接続されていません")
        exit()

このコードのキモは、計測器のUSBチップに元々書き込まれている固有のIDや製品名を識別に使うことです。serial.toolsで拾ってこれる情報は公式ドキュメントに書かれていますので確認しましょう

上記の例では.serial_numberというクラスに私の欲しい情報(計測器を識別する情報)が書かれていました。製品によっては、.descriptionだったりするので、そこは情報を取ってみて考えましょう。

上記コード中では、

device_name = "***"

***の部分に、製品を識別する一意の文字をいれ、取得した製品情報と照らし合わせています。このサンプルコードでは、関数comport_search()を呼んだ時に、計測器が接続されたシリアルポート番号が返ってきます。(接続されていれば)

ですので、冒頭のシリアル通信のプログラムと組み合わせれば、シリアルポート番号を意識せず、計測器の接続ができるようになりますね!細かいことですが、シリアルポート番号を調べるのも人手間かかりますので、ぜひ実装しておきたい機能かと思います。

同じ識別文字や番号を持つ計測器をPCに2台以上つなぐ場合、上記サンプルコードでは正常に機能しませんので注意が必要です。

参考にしたサイト

pySerialのインストール、Pythonでのシリアル通信プログラミング、文字コード変換の方法については、下記サイトを参考にしました。

【Python入門】pySerialでシリアル通信を実行する方法を解説

Pythonで文字コードを変換する方法【初心者向け】

[Python]ArduinoのCOMポートが変わってもコードを編集せずにポートを開く方法

さいごに

書いてみればわずか数行の簡単なコードでしたが、取得したデータの加工でずいぶん手間取りました。IoT隆盛の昨今、Raspberry PiなどのIoTデバイスからシリアル通信でデータを取得するケースは増えそうです。今回のケースで勉強になりよかったです。

Pythonプログラムによりシリアル通信でデータが取得できるようになれば、今後は以下のような展開も可能です。

  • 温度ロガー等のデータをデータベースに随時格納する
  • GUIプログラムを組んで、Windowsからマウス操作のみでデータ取得ができるようにする
  • 取得したデータがしきい値範囲内か判定する

などなど。あとはアイデア次第で仕事における様々な分野が自動化できますね!Pythonプログラミングは楽しいです^^

タグ

カテゴリー

  • この記事を書いた人
アバター

from-age35

中小企業エンジニアです。35歳で急遽プログラミングを覚えることになり、PythonやJavaScriptなどをゆっくりマイペースに覚えています。先端スキルには疎いですが、楽しくコーディングしてます♪最近の興味は【WEB開発】です

-Python
-

Copyright© 35からのプログラミング , 2019 All Rights Reserved Powered by AFFINGER5.