Pythonで測定器の制御〜GPIB編〜
ひさびさのブログ更新は備忘録がわりの記事です。アマチュア無線については最近アクティビティがほとんどゼロになっているので何も書くことがありません。
お仕事(化学会社で研究職をやっています)で古い(実はそうでもないですが)分析機器を使っていますが、ほぼすべての分析機器・測定器は制御を自動化したりデータを取り込んだりするためにコンピュータと接続されています。よくあるトラブルが本体の測定装置よりもコンピュータのほうが先に故障してしまうことで、しかもハードやOSが古いせいで修理不能であったり、かといってコンピュータを新しくするとインタフェイスボードや制御ソフトウェアまで更新せざるをえなくなり、さらに新しい制御ソフトウエアは手持ちの古い測定に対応していなかったりと、たかだか数万円のコンピュータが壊れただけなのに、全部新しくしなければならない、その総費用は数千万円になることがザラにあったりして、ものすごく不合理です。
現在仕事で頻繁に使っている装置も、コンピュータ(DELLのPC、OSはWindows Vista)の動作が怪しくなってきており、そろそろ更新したいのだけど、上記のような事情でPCだけの更新は無理になりました。そこで、装置の制御をLinux上のPCで自力でやることにしました。
この装置は、ContecのPCIボードのGPIBアダプタによってGPIBで制御されているですが、ContecのデバイスドライバがWindows用しかないことが判明(Linuxもあるけど古いカーネルにしか対応していない)。そこで、Agilent 82357bというUSB-GPIBアダプタを使うことにしました。このアダプタは結構有名なので、Linux用のドライバが用意されていました。以下、このアダプタを使ってLinux(Ubuntu 18.04 LTS, カーネル 5.3.0)上で装置を動かすまでの手順を示します。
Linux-GPIBのインストール
Linux-GPIBのWebページsourceforge.net
で最新のバージョンのソースファイルをダウンロードしてきます(今日現在 バージョン4.3.3)。次に、ソースを展開し、インストールします。
$ tar xzvf linux-gpib-4.3.3.tar.gz $ cd linux-gpib-4.3.3 $ tar xzvf linux-gpib-kernel-4.3.3.tar.gz $ cd linux-gpib-kernel-4.3.3 $ make $ sudo make install $ cd ../ $ tar xzvf linux-gpib-user-4.3.3.tar.gz $ cd linux-gpib-user-4.3.3 $ ./configure $ make $ sudo make install
pythonとバインドするため開発ツールが入っていない場合はあらかじめ、
$ sudo apt install python3-dev libboost-dev python3-setuptools
としておきます(そうでないとmake時にエラーが出ます)。
アダプタのファームウェアの用意
Agilentのファームウェアはここのページにあるので、最新のものをダウンロードします(gpib_firmware-2008-08-10.tar.gz)。
$ tar xvzf gpib_firmware-2008-08-10.tar.gz $ cd linux_gpib_firmware-master/agilent_82357a/ $ ls 82357a_fw.hex firmware.c lsusb_postload.txt README lsusb_initial.txt measat_releaseX1.8.hex
このmeasat_releaseX1.8.hexがファームウェア本体となっています。
fxloadのインストール
入ってなければapt installでインストールしておきます。
gpib.confの設定
GPIBのインタフェイスとGPIB機器の登録を済ませておきます。場所は/usr/local/etc/gpib.confです。interfaceの節にAgilent 82357a、device節に各GPIB機器の機器名やプライマリーアドレスなどを設定しておきます。
interface { minor = 0 /* board index, minor = 0 uses /dev/gpib0, minor = 1 uses /dev/gpib1, etc. */ board_type = "agilent_82357a" /* type of interface board being used */ name = "agi" /* optional name, allows you to get a board descriptor using ibfind() */ pad = 0 /* primary address of interface */ sad = 0 /* secondary address of interface */ timeout = T3s /* timeout for commands */ eos = 0x0a /* EOS Byte, 0xa is newline and 0xd is carriage return */ set-reos = yes /* Terminate read if EOS */ set-bin = no /* Compare EOS 8-bit */ set-xeos = no /* Assert EOI whenever EOS byte is sent */ set-eot = yes /* Assert EOI with last byte on writes */ base = 0 /* Base io ADDRESS */ irq = 0 /* Interrupt request level */ dma = 0 /* DMA channel (zero disables) */ master = yes /* interface board is system controller */ } device { minor = 0 /* minor number for interface board this device is connected to */ name = "agilent_34401a" /* device mnemonic */ pad = 12 /* The Primary Address */ sad = 0 /* Secondary Address */ }
カーネルモジュールのロード
$ sudo modprobe gpib_common $ sudo modprobe agilent_82357a
USBデバイスの設定
PCにUSBを差し込むと、アダプタの赤LEDのみ点灯しているはずです。USBバス番号を確認しておきます。
$ lsusb Bus 001 Device 009: ID 0957:0718 Agilent Technologies, Inc. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 003 Device 003: ID 413c:2107 Dell Computer Corp. Bus 003 Device 002: ID 413c:3200 Dell Computer Corp. Mouse
バス番号001、デバイス番号009でアダプタが認識されているのを確認しました。
fxloadでプラグする
$ sudo fxload -D /dev/bus/usb/001/009 -t fx2 -I XXXXX/measat_releaseX1.8.hex
XXXXXはファームウェアがあるディレクトリです。再度USBバス番号をチェックすると、
$ lsusb
Bus 001 Device 010: ID 0957:0718 Agilent Technologies, Inc.
.....
なぜかデバイス番号が一つ増えています。もう一度、fxloadします。うまく行けば、アダプタの緑のLEDが点灯します(この時点では、赤と緑の両方のLEDが点灯しているはず)。
$ sudo fxload -D /dev/bus/usb/001/010 -t fx2 -I XXXXX/measat_releaseX1.8.hex
GPIBデバイスファイルの権限を変更
/dev/gpib0を通じて制御できるようになるのですが、rootのものなのでアクセス権を付与しておきます。
$ sudo chmod 666 /dev/gpib0
ライブラリのシンボリックリンクを張る
makeして作られたライブラリが/usr/local/lib/の下にできるのだが、実行時には/libのライブラリを見に行くので、シンボリックリンクを張っておく(バグでしょうか?)。
$ sudo ln -s /usr/local/lib/libgpib.so.0 /lib/libgpib.so.0
GPIBの設定を反映させる
$ sudo gpib_conf
うまくいけば、アダプタのLEDが緑だけ点灯しているはずで、これでようやく使えるようになりました。やれやれ。
動作チェック
ibtestというGPIBアダプタとGPIB機器をチェックしたりかんたんな通信ができるツールがあるので、実行してみます。
$ ibtest Do you wish to open a (d)evice or an interface (b)oard? (you probably want to open a device): d enter primary gpib address for device you wish to open [0-30]: 12 trying to open pad = 12 on /dev/gpib0 ... You can: w(a)it for an event write (c)ommand bytes to bus (system controller only) send (d)evice clear (device only) change remote (e)nable line (system controller only) (g)o to standby (release ATN line, system controller only) send (i)nterface clear (system controller only) ta(k)e control (assert ATN line, system controller only) get bus (l)ine status (board only) go to local (m)ode change end (o)f transmission configuration (q)uit (r)ead string perform (s)erial poll (device only) change (t)imeout on io operations request ser(v)ice (board only) (w)rite data string send group e(x)ecute trigger (device only) :
のように動かせます。例えば、DMM(キーサイトの34401A)を使って電圧値を取得したい場合は、
: w enter a string to send to your device: read? sending string: read? ...... : r enter maximum number of bytes to read [1024]: 1024 trying to read 1024 bytes from device... received string: '-1.02559000E-04 ' Number of bytes read: 16 gpib status is: ibsta = 0x2100 < END CMPL > iberr= 0 ibcntl = 16
のように、wコマンドで文字列を送信して("read?")、rコマンドで文字列を受信する("-1.02559000E-04\r\n")という具合です。
Pythonで制御
PythonでGPIBを制御する方法は、Linux-GPIBに付属しているライブラリとVISAライブラリを使う方法がありますが、今回は前者を使ってみます。例として、DMMと接続してトリガ測定で10回連続して電圧値を取得するスクリプトを作ってみました。
import gpib import time dev = gpib.find("agilent_34401a") gpib.write(dev, "*rst") time.sleep(0.1) gpib.write(dev, "*cls") time.sleep(0.1) gpib.write(dev, "trig:coun 10") time.sleep(0.1) gpib.write(dev, "init") time.sleep(1) gpib.write(dev, "fetc?") time.sleep(0.1) str = gpib.read(dev, 1024) print(str.decode().strip())
バッファから取り出した値はバイト型になって扱いづらいので、.decode()して文字列型に変換する必要があります。.strip()で行末の改行文字などを取り除いておきました。通信コマンドを受け付けるまで若干の時間を要するので、各コマンド間に0.1秒のスリープを設けました。実行結果はこのような結果になります。
+2.40874090E+01,+2.40881940E+01,+2.40894820E+01,+2.40883620E+01,+2.40887800E+01,+2.40903930E+01,+2.40902050E+01,+2.40898490E+01,+2.40905400E+01,+2.40893980E+01
次はUSB接続、シリアル接続についても説明します(備忘録として)。