we haven’t had that spirit here since…

4G GPTAS も動き始めました。

で、やはり15年前のテクノロジーには無理があります。そこで、色々と考えたのですが、EPICS base で色々と書き直すのが良いのではと思い始めました。そこで、1日ぐらいでどこまでできるか試してみました。

目標:アマゾンで売っている中華USB温度計(1000円強)を epics で読む。

この温度計はとっても安いので、実験室のそこいらじゅうにおいて色々な場所の温度をモニターしています。結晶成長する時に部屋の温度変化は大敵なので。今のところ、github に転がっていた linux 用読み取りコードと手製の script を組み合わせて web 上に温度変化を表示していますが、これをepicsで読んでみようという目標です。epics は raspberry pi 4B で走らせます。中華温度計は USB HID デバイスなのでちょっと一筋縄ではいかないため、最初には良い課題かと思いました。

EPICS を Raspberry Pi 4B に載せる

アカウント登録のため ldap 走らせる。(ホームディレクトリはNFSなのでautofsも)
(これはうちの研究室の特殊事情です。いらない人はいらない。)
sudo apt-get install autofs
sudo echo "/home /etc/auto.home" | sudo tee -a /etc/auto.master
sudo echo "* -fstype=nfs4,soft xxx.xxx.xxx.xxx:/volume1/labhome&" | tee -a /etc/auto.home
sudo systemctl restart  autofs

sudo apt-get install libnss-ldapd
## check and edit if necessary: /etc/ldap/ldap.conf
## /etc/nsswitch.conf
## /etc/nslcd.conf
## /etc/pam.d/common-password
## /etc/pam.d/common-session
EPICS を入れる
sudo apt-get install libreadline-dev
sudo apt-get install libpcre3-dev
(if necessary, install g++ and make)
cd "~/work/tmp/epics" (ここをworking directory にしました。適宜変えてください)
wget https://epics.anl.gov/download/base/base-7.0.6.tar.gz
tar zxvf base-7.0.6.tar.gz
cd base-7.0.6
make

時間かかります。。。30分ぐらい?

echo "export EPICS_BASE=\${PWD}/base-7.0.6" > setEnvVar
echo "export EPICS_HOST_ARCH=\$(\${EPICS_BASE}/startup/EpicsHostArch)" >> setEnvVar
echo "export PATH=\${EPICS_BASE}/bin/\${EPICS_HOST_ARCH}:\${PATH}" >> setEnvVar
source setEnvVar

softIoc

動いた!ともかく、base は入った。getting started に従って test.db も作って softIoc -d test.db も試しました。問題なし。

Asyn と Stream Device をいれます。
(~/work/tmp/epics で)
mkdir support
cd support
git clone https://github.com/epics-modules/asyn.git
cd asyn/
## edit asyn/configure/RELEASE(以下私のRELEASE fileの主要な変更点)
HOME=/home/sato
SUPPORT=$(HOME)/work/tmp/epics/support
EPICS_BASE=$(HOME)/work/tmp/epics/base-7.0.6
##
(IPAC, SNCSEQ, CALC, SSCANはこの時点では comment out)
make

cd ~/work/tmp/epics/support
git clone https://github.com/paulscherrerinstitute/StreamDevice.git
cd StreamDevice/
## 同様に stream のなかの configure/RELEASE も編集します
##
ASYN=$(SUPPORT)/asyn
PCRE_INCLUDE=/usr/include
PCRE_LIB=/usr/lib
##
rm GNUmakefile
make
(これでできるはずです)
EPICS に calc と drvAsynUSBHID を入れる

中華温度計は USB HID デバイスなので KEK ご謹製の drvAsynUSBHID で読むことを考えました。あと、中華温度計はバイナリで妙なデータを返してくるので calc を入れて、scalcout を使ってバイナリデータを温度に変換することを考えました(ここが一番ややこしかった。)

calc モジュールを入れる

cd ~/work/tmp/epics/support/
wget https://github.com/epics-modules/calc/archive/R3-6-1.tar.gz
tar zxvf R3-6-1.tar.gz
cd calc-R3-6-1/
##edit configure/RELEASE (いつも通りなので詳細は割愛。今回は SSCAN と SNCSEQ をはずしました)
make

drvAsynUSBHID を入れる(hidraw を使います。libusb じゃない方です。cmake も入れます。)

sudo apt-get install libhidapi-hidraw0 libhidapi-dev cmake
cd ~/work/tmp/epics/support
wget http://cerldev.kek.jp/trac/EpicsUsersJP/raw-attachment/wiki/epics/bbb/debian/USBHID/USB_SP4T/drvAsynUSBHID_20170704.tar.gz
tar zxvf drvAsynUSBHID_20170704.tar.gz
mv drvAsynUSBHID-master-4b0e4ee13cb65512d4ccb12a82d6ab3aacb3b0e5 drvAsynUSBHID
(master-の後の数字はこれとは違うかもしれません。)
## Edit configure/RELEASE (今回はasyn, StreamDevice, TOP, SUPPORT等を設定します。)
## Makefile を一応チェックします。(変更なし)
## (重要)中華温度計読み取りのため、drvAsynUSBHID を少しいじります。いじり方は別に書きます。
make
さて、ようやく中華温度計読み取り IOC を作ります!

読み取りIOC の名前を testTEMPer として ~/work/tmp/epics/TEST の中に作ります。
makeBaseApp.pl でテンプレートを作ります。

mkdir ~/work/tmp/epics/TEST/testTEMPer
cd ~/work/tmp/epics/TEST/testTEMPer
makeBaseApp.pl -t ioc testTEMPer
makeBaseApp.pl -i -t ioc testTEMPer

で、出来上がった testTEMPer のなかの configure/RELEASE をいじります。

## 追加/変更する内容
SUPPORT=~/work/tmp/epics/support
ASYN=$(SUPPORT)/asyn
STREAM=$(SUPPORT)/StreamDevice
DRVASYNUSBHID=$(SUPPORT)/drvAsynUSBHID

続いて testTEMPerApp/src/Makefile をいじります。追加するのは

PROD_LDLIBS += -lhidapi-hidraw

testTEMPer_DBD += asyn.dbd
testTEMPer_DBD += stream.dbd
testTEMPer_DBD += drvAsynUSBHID.dbd
testTEMPer_DBD += calc.dbd

testTEMPer_LIBS += asyn
testTEMPer_LIBS += stream
testTEMPer_LIBS += drvAsynUSBHID
testTEMPer_LIBS += calc

(つまり asyn, stream, drvAsynUSBHID, calc を使いますよ、と書くわけです。)

testTEMPerApp/Db/Makefile をいじります。追加するのは:

DB += testTEMPer.db

で、つくります

cd ~/work/tmp/epics/TEST/testTEMPer
make

実行スクリプト st.cmd をいじります

cd ~/work/tmp/epics/TEST/testTEMPer/iocBoot/ioctestTEMPer/
## edit st.cmd 修正/追加内容は以下の通り
epicsEnvSet("STREAM_PROTOCOL_PATH", ".:${TOP}/testTEMPerApp/Db" )
drvAsynUSBHIDConfigure($(dev), "0x0c45", "0x7401", "", "/dev/hidraw1", "1")
epicsEnvSet("dev", "HID1")
dbLoadRecordes("db/testTEMPer.db", "USER=TEST,DEV=$(dev)")
chmod u+x st.cmd
IOC本体はできました。次に、testTEMPer database ファイルと protocol ファイルを作ります。

中華温度計は /dev/hidraw1 (0じゃないんです、、、) に binary で0x00 0x01 0x80 0x33 0x01 0x00 0x00 0x00 0x00を送ると binary で 8byte 返してきます。このうち(0から数えて)2,3byte目が温度情報です。(buf[3]&0xFF + buf[2]<<8)*125.0/32000. が温度です。なんとややこしい。(あれ、今気がついたけど、これ、short (2byte)の little endian で読むだけじゃないの?アホだった???まぁ、いいや。)
で、proto ファイル(~/work/tmp/epics/TEST/testTEMPer/testTEMPerApp/Db/testTEMPER.proto)は以下の様な感じになります。

TERMINATOR="";
LockTimeout = 500;
ReplyTimeout = 100;
ReadTimeout = 100;
WriteTimeout = 1000;

setsw {
    out "%r";
}

getsw {
    MaxInput = 8;
    out 0x00 0x01 0x80 0x33 0x01 0x00 0x00 0x00 0x00;
    in "%8c";
}

重要な点:MaxInput を指定して terminator を無視させます。その上で%8c で unsigned char で8byte 読み込みます。(それでも、なんかwarning 行ってくるけど、、、多分stream deviceのバグ。)

次に、database file ですが、次の通りです:

record(stringin, "$(USER):testTEMPer:get") {
    field(DTYP, "stream")
    field(FTVL, "UCHAR")
    field(SCAN, "1 second")
    field(INP, "@testTEMPer.proto getsw $(DEV)")
    field(NELM, 8)
    field(FLNK, "$(USER):testTEMPer:calctemp")
}
record(scalcout, "$(USER):testTEMPer:calctemp"){
    field(INAA, "$(USER):testTEMPer:get")
    field(CALC, "((BYTE(AA[2,2])<<8)+BYTE(AA[3,3]))*125.0/32000.")
    field(OUT, "$(USER):testTEMPer:temperature")
    field(FLNK, "$(USER):testTEMPer:temperature")
}
record(ai, "$(USER):testTEMPer:temperature"){
    field(INP, "")
    field(FLNK, "")
    field(SCAN, "Passive")
}

重要な点:stream から UCHAR で8個分読みます。testTEMPer.proto に定義された、getsw を使います。$(DEV)は st.cmd に定義した HID1 ですね。1秒おきに定期的に読みます。(温度なんで)で、その読み取りはstringin (文字列型)にして forward link (FLNK) で calctemp に送ります。calctemp ではscalcout を使ってbyte ごとに読んで(AA[2,2]という様に始まりと終わりを指定しないとエラーになることに注意)、それをシフトしたりなんだかんだりで温度に変換します。で、それを FLINKで testTEMPer:temperature に送ります。で、ai (analog input)で受けます。このrecordは何もしないので”Passive” の何もしないrecordにしておきます。

まだあります。中華温度計のデバイスファイル /dev/hidraw1 の読み取り許可を与えねばなりません。
手は色々とあります。今回は

udev rules 弄りました
/etc/udev/rules.d/50-usb-temper.rules を書きました。内容は:

KERNEL=="hidraw*",ATTRS{idVendor}=="0c45",ATTRS{idProduct}=="7401",MODE="0666"

##本当は、/dev/hidraw* を dialout のgroup にして、userの副グループにdialout を足すのが綺麗な気がしますが、今回は楽しました。
これで、温度計を抜き差しします。

これで、動くはず。

まず、testTEMPer を動かします。

cd ~/work/tmp/epics/TEST/testTEMPer/iocBoot/ioctestTEMPer/
./st.cmd

これでEPICS IOCが動きます。

他のターミナルを開いて

cd ~/work/tmp/epics
source setEnvVar
caget TEST:testTEMPer:temperature

で温度が読めます。

camonitor TEST:testTEMPer:temperature

で連続モニターができます。

ここまでくればあとは簡単。

CS-Studio (Phoebus)をダウンロードします。

https://controlssoftware.sns.ornl.gov/css_phoebus/
MacOS用は Java も入っていて簡単に動きます。(他は試してないです。)
このページのinstructionに従ってインストールします。

CSS_Phoebus.app を立ち上げて、

Applications->Display->New Display
を選びます。あとは、この画面に要素を配置していくだけです。要素のPV Name というところに、TEST:testTEMPer:temperature を入れると勝手にIOCに繋いでデータを取ってきてくれます。
Text Update
Meter
StripChart
を配置した例は以下の通り。走らせるには緑の右矢印をクリック。下の画面になります。