ArduinoとオシロスコープをExcelで制御して測定する

##1. はじめに
休日に家で[電子工作](https://www.google.co.jp/search?q=電子工作)をするのに[オシロスコープ](https://www.google.co.jp/search?hl=ja&as_q=オシロスコープ)が欲しくなりRIGOLのDS1054Zを昨年購入しました。当初はPCと接続することは特に考えていなかったのですがこのモデルはUSBでPCと接続すると[USBTMC](https://www.google.co.jp/search?hl=ja&as_q=USBTMC)(USB Test & Measurement Class)機器として認識され[SCPIコマンド](https://www.google.co.jp/search?hl=ja&as_q=SCPI%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)で制御できることがわかりました。

また、手元に[UART](https://www.google.co.jp/search?hl=ja&as_q=UART)でコマンド制御できる[Arduino Leonardo](https://www.google.co.jp/search?q=arduino+leonardo)があります(→[Arduino Leonardoで多目的ツールの製作](https://qiita.com/pbjpkas/items/97dbf835b0aab6725e94))。

そこでオシロスコープとArduino Leonardoの制御をExcelで試しました。以下にその測定システムの概要を示します。
![測定システム概要図](https://qiita-image-store.s3.amazonaws.com/0/221758/af31397b-ac47-83db-44b0-77847bfff29f.png)


##2. 計測器をPCで制御する
計測器によって制御方法はさまざまです。DS1054ZはUSBまたはLANでPCと接続しSCPIコマンドで制御します。

###2.1 ハードウェア(物理インターフェース)
代表的な物理インターフェースを以下に挙げます。

- [GPIB](https://www.google.co.jp/search?hl=ja&as_q=GPIB)(General Purpose Interface Bus)
  - もともとは[HP-IB](https://www.google.co.jp/search?hl=ja&as_q=HP-IB)(Hewlett-Packard Instrument Bus)、かつての[HP](http://www.hp.com)の計測機器部門である[キーサイト](https://www.keysight.com/main/home.jspx?cc=US&lc=eng)が生みの親
  - [IEEE 488](https://www.google.co.jp/search?hl=ja&as_q=IEEE+488)として標準化
- LAN
- RS-232C
- USB

参考：[https://ja.wikipedia.org/wiki/IEEE_488](https://ja.wikipedia.org/wiki/IEEE_488)

###2.2 ソフトウェア
キーサイトの[テスト・システムでのIVIドライバの評価 : IVIが最適な場合](http://literature.cdn.keysight.com/litweb/pdf/5990-3186JAJP.pdf)では以下の4つが挙げられています。

> - SCPI（プログラマブル計測器用標準コマンド）
- プログラムまたは言語固有のドライバ（LabVIEW ドライバなど）
- 専用ドライバ（さまざまなベンダの測定器に付属）
- IVI などのオープンなドライバ

これに加えて、

- 機器固有のコマンド(SCPI登場より前の古い計測器)

があります。

##3. VISAライブラリ
ExcelでSCPIコマンドを使用して計測器を制御するにはVISA-COMライブラリを参照設定します。

###3.1 VISAライブラリ
[VISA](https://www.google.co.jp/search?hl=ja&as_q=GPIB+VISA)(Virtual Instrument Software Architecture)は[IVI Foundation](http://www.ivifoundation.org/)によって策定された計測器制御の共通規格で、この規格に則って実装されたVISAライブラリがいくつかの計測器ベンダから提供されています。VISAライブラリの一例を以下に示します。日本語ドキュメントの豊富さ、動作環境、使用条件などベンダによりさまざまです。

- [Keysight IO Librarise Suite](https://www.keysight.com/main/editorial.jspx?cc=JP&lc=jpn&ckey=2304673&nid=-11143.0.00&id=2304673)
  - Keysight IO Libraries Suiteのインストールの際にVISAライブラリが一緒にインストールされます。
- [NI-VISA](http://sine.ni.com/nips/cds/view/p/lang/ja/nid/12145)
  - ナショナルインスツルメンツ社が実装したVISAライブラリ。
- [TekVISA](https://jp.tek.com/oscilloscope/tds7054-software/tekvisa-connectivity-software-v411)
  - テクトロニクス社が実装したVISAライブラリ。
- [KI-VISA](https://www.kikusui.co.jp/download/jp/?fn=com_kivisa)
  - 菊水電子工業が実装したVISAライブラリ。

VISA-COMライブラリは[Microsoft COM](https://www.google.co.jp/search?hl=ja&as_q=microsoft+component+object+model)形式のVISAライブラリです。

###3.2 Keysight IO Libraries Suite
DS1000ZシリーズのUltraSigma Instrument Connectivity DriverにはNI-VISAが同梱されていますがナショナルインスツルメンツ社の[計測器制御の基本](http://www.ni.com/white-paper/4359/ja/)にはExcelの例がありません。計測器制御のプログラミングに慣れていればVisual Basicのサンプルを見ながらExcel VBAに置き換えることができるかもしれませんがちょっと敷居が高いです。

一方キーサイトのIO Libraries Suiteは

- 自動計測の情報が[計測技術情報ライブラリ](http://jp.em.keysight.com/tmo/TechnicalSupport/Automatic-measuring/Auto-measuring_top/auto_mgmt_top.html?cmpid=50745)にまとまっている
- Excelのサンプルプログラムがある
- 無償、かつ、他社の測定器ユーザも使用できる

ということでこちらを使用させていただきます。

2018/6/13追記
RIGOLの[MSO1000Z&DS1000Z Programming Guide](http://int.rigol.com/File/TechDoc/20151218/MSO1000Z&DS1000Z_ProgrammingGuide_EN.pdf)にNI-VISAのExcelのサンプルプログラムが掲載されているのをコメント欄で@shikasamaさんに教えていただきました。処理自体はKeysightのVISAとNI-VISAとで変わりませんがメソッドの名前や引数の与え方が若干異なっています。


##4. 試作プログラム
###4.1 用意するもの
- Keysight IO Libraries Suite
  - [IO Libraries Suite ソフトウェアダウンロードサイト](https://www.keysight.com/find/iolib)
      - IO Libraries SuiteとInstrument Control Bundle(IO Libraries Suite / Command Expert / BenchVue Platformの統合パッケージ)があります
      - IO Libraries Suiteは2018 Update 0.2を使用しました
  - [IO Libraries Suite 簡易取扱説明書 最新版](http://www.keysight.co.jp/find/iolib_jp_quick) 
     - [IO Libraries Suite 2017 簡易取扱説明書(PDF)](https://www.keysight.com/upload/cmc_upload/All/IO_Lib_18_0_quick_JP_20170523.pdf)
- Arduino Leonardo(あるいはUARTで文字列の送受信ができるマイコンボードなど)
- SCPIコマンドをサポートするオシロスコープ
- Microsoft Excel

###4.2 USB制御
[初級プログラム作成アプリケーション・ノート](http://jp.em.keysight.com/tmo/TechnicalSupport/Automatic-measuring/beginner_program/beginner.htm)の[Excel 2010/VBA による測定器制御プログラム入門](http://www.home.agilent.com/agilent/redirector.jspx?action=ref&cname=agilent_editorial&ckey=2337359&cc=JP&lc=jpn&mcr=true&cmpid=51605)の6. 注意事項に従い、掲載されているサンプルプログラムを使用しました。

####4.2.1 補足
- Excel 2010/VBA による測定器制御プログラム入門に記載のAgilentはKeysightの旧社名です。
- VISA AddressはPCに計測器を接続しIO Libraries Suiteに同梱のKeysight Connection Expertで確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/d25ee54e-08fa-2bd7-9a79-f61e25aa7a69.png)

####4.2.2 プログラム
``` vb
Sub gpib_test()
    Dim dstSheet As Worksheet
    Set dstSheet = Worksheets("test")                                      'Excelの"test"シートへ戻り値を出力
    Dim RM As New VisaComLib.ResourceManager
    Dim DSO As New VisaComLib.FormattedIO488                               'DSO : Digital Storage Oscilloscope
    Set DSO.IO = RM.Open("USB0::0xXXXX::0xXXXX::XXXXXXXXXXXXXX::0::INSTR") 'VISA Address
    DSO.WriteString "*IDN?"
    dstSheet.Cells(2, "C") = Replace(DSO.ReadString(), vbLf, "")           '改行コードを取り除いてC2セルへ書込む
    DSO.IO.Close
    Set DSO = Nothing
    Set RM = Nothing
End Sub
```

####4.2.3 実行結果
PCにオシロスコープを接続して実行したのがこちらです。"*IDN?"コマンドを送信し受信したデータをC2セルに出力できました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/4e6f7601-3dc0-f3d0-ef14-527c12a57121.png)

###4.3 RS232C制御
4.2のUSB制御プログラムに以下の改修を行いました。

- VISA Addressは ASRL6::INSTR (Arduino Leonardoに接続しているFT-232RQがCOM6として認識されているため)
- RS232Cの通信設定を追加(参考:[Communicating with Instruments using RS-232](https://www.keysight.com/main/editorial.jspx?cc=JP&lc=jpn&ckey=558637&nid=-11143.0.00&id=558637))
- 送信する文字列は"ver"
  - "ver"を受信するとスケッチのファイル名とビルド日時を応答するプログラムをArduino Leonardoに焼いてあります
- WriteString → ReadStringのあいだにウェイトを追加
  - Sleepを作成し50msのSleepを最大10回行う
- 受信したデータから制御文字を削除

####4.3.1 プログラム
``` vb
Private Declare Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long)

Sub asrl_test()
    Dim i As Long
    Dim j As Long
    Dim dstSheet As Worksheet
    Set dstSheet = Worksheets("test")
    Dim strVal As String
    Dim strArr() As String
    
    Dim RM As New VisaComLib.ResourceManager
    Dim TB As New VisaComLib.FormattedIO488  'TB : Target Board
    
    Set TB.IO = RM.Open("ASRL6::INSTR")
    Dim sfc As VisaComLib.ISerial            'RS232C通信設定
    Set sfc = TB.IO
    sfc.BaudRate = 9600
    sfc.DataBits = 8
    sfc.Parity = ASRL_PAR_NONE
    sfc.StopBits = ASRL_STOP_ONE
    sfc.FlowControl = ASRL_FLOW_NONE
    
    TB.IO.TerminationCharacter = 10
    TB.IO.TerminationCharacterEnabled = True
    
    TB.WriteString "ver"
    
    For i = 1 To 10                          'Write→ReadのあいだにSleepを入れる
        Sleep (50)
        strVal = ""
        strVal = TB.ReadString()
        ReDim strArr(1 To Len(strVal))
        
        For j = 1 To UBound(strArr)          '制御文字を削除する
            strArr(j) = Mid(strVal, j, 1)
            If Asc(strArr(j)) <= 31 Or 127 <= Asc(strArr(j)) Then
                strArr(j) = ""
            End If
        Next j
        strVal = Join(strArr, "")
        
        If strVal <> "" Then
            dstSheet.Cells(3, "C").Value = strVal
            Exit For
        End If
        'DoEvents
    Next
        
    TB.IO.Close
    Set TB = Nothing
    Set RM = Nothing
End Sub
```

####4.3.2 実行結果
PCにArduino Leonardoを接続して実行したのがこちらです。"ver"コマンドを送信し返ってきたデータをC3セルに出力できました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/21b36b8e-3b19-982a-0a85-2b9ca27f4e4c.png)


##5. ArduinoとオシロスコープをExcelで制御して測定する
ExcelからオシロスコープとArduino Leonardoへコマンドを送信し応答を得られたのでいよいよ本題の測定を行います。

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/221758/575e9c4d-2fe6-0349-02e4-a1bfcd45ada6.jpeg)

この写真は

- ExcelからArduino LeonardoへRS232Cで自作のコマンドを送信し[tone()](https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/)関数で矩形波パルスを生成
- 生成したパルスをオシロスコープのCH1へ入力
- ExcelからオシロスコープへSCPIコマンドを送信しCH1の信号の周波数を測定

しているところです。

###5.1 仕様(のような何か)
####5.1.1 Excel
![image.png](https://qiita-image-store.s3.amazonaws.com/0/221758/932d8243-06aa-2ea3-2b41-884b41a2ccea.png)

#####5.1.1.1 書式
- DSO(Digital Storage Oscilloscope)のVISA ADDRESSをC6セルへ記述する
- TB(Target Board、ここではArduino Leonardo)のVISA ADDRESSをC7セルへ記述する
- 1行につき一つのコマンドを10行目以降に記述する
  - A列：制御対象機器
      1. DSO
      2. TB
      3. SYS(SYStem、Excel VBAのウェイト調整やメッセージボックス表示)
      4. //(コメント)
  - B列：送信データ
  - C列：受信データ
      - 受信データが数値の場合はExcelの書式設定で小数点以下の桁数を適宜設定のこと
- DSOのコマンドはSCPIコマンドそのもの
  - :TIM:SCAL 0.01 (時間軸スケールを0.01秒に設定)
  - :MEAS:FREQ? CHAN1 (CH1の信号の周波数を測定)
  - SCPIコマンドに"?"が含まれる場合はReadString()でデータを受信しC列へ出力する
- TBのコマンドは5.1.2参照
- SYSのコマンドは以下の2つ
  - wait1000ms (1000msの待ち)
  - msgbox (メッセージボックスをポップアップする)

#####5.1.1.2 動作
- テスト開始ボタンの押下をもってコマンド実行を開始する
- 実行中のコマンドのセルをSelectする
- A列が空白の行をもってコマンド実行を終了する

####5.1.2 Arduino Leonardo
Arduinoに[tone()](https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/)という任意の周波数でデューティ比50%の矩形波パルスを生成する関数が用意されていますのでこの関数を叩くコマンドをArduino Leonardoに実装しました。

- "toneon \<frequency\>"というコマンドを受信すると tone(10, frequency) を実行します。
- 指定された周波数の矩形波パルスをDigital Pin #10に出力します。

###5.2 テストベンチの構築
Arduino LeonardoのDigital Pin #10をプローブでつまんでオシロスコープのCH1へ接続します(写真ではPin #10に接続されている抵抗をプローブでつまんでいます)。

###5.3 プログラム
4.2.2および4.3.1のプログラムに以下の変更を加えています。
1. ActiveSheetの表からデータを入出力するように変更
2. RS232Cのデータ受信部分は実機に合わせてタイミングやループ回数、文字列操作を変更
3. Excel VBAの処理を制御するプログラムを追加

``` vb
Option Explicit

Private Declare Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long)

Const COLUMN_CMD = "A"       'パーサ用コマンド
Const COLUMN_TXD = "B"       '送信データ
Const COLUMN_RXD = "C"       '受信データ

Const LINE_CMD = 10          'パーサ用コマンド開始行

Const VISA_ADDR_DSO = "C6"   'DSO : Digital Storage Oscilloscope
Const VISA_ADDR_TB = "C7"    'TB  : Target Board

'「テスト開始」ボタン押下で呼出すマクロ
Sub Parser()
    Dim i As Long
    Dim strCmd As String
    Dim strTxd As String
    
    i = LINE_CMD
    Do While ActiveSheet.Cells(i, COLUMN_CMD) <> ""
    
        strCmd = ActiveSheet.Cells(i, COLUMN_CMD)
        strTxd = ActiveSheet.Cells(i, COLUMN_TXD)
        
        ActiveSheet.Cells(i, COLUMN_CMD).Select
        
        If InStr(strCmd, "//") > 0 Then
            '何もしない
        
        ElseIf strCmd = "DSO" Then
            Call ControlDSO(i, strTxd)
        
        ElseIf strCmd = "TB" Then
            Call ControlTB(i, strTxd)
        
        ElseIf strCmd = "SYS" Then
            Call ControlSystem(i, strTxd)
        
        Else
            MsgBox ("unknown command")
        
        End If
            
        i = i + 1
    Loop
End Sub

Function ControlDSO(line As Long, strTxd As String)
    Dim visaAddr As String
    visaAddr = ActiveSheet.Range(VISA_ADDR_DSO)
    
    Dim RM As New VisaComLib.ResourceManager
    Dim DSO As New VisaComLib.FormattedIO488
    
    Set DSO.IO = RM.Open(visaAddr)
    DSO.WriteString strTxd
    
    If InStr(strTxd, "?") > 0 Then
        ActiveSheet.Cells(line, COLUMN_RXD) = Replace(DSO.ReadString(), vbLf, "")
    End If
    
    DSO.IO.Close
    Set DSO = Nothing
    Set RM = Nothing
End Function

Function ControlTB(line As Long, strTxd As String)
    Dim i As Long
    Dim j As Long
    Dim strVal As String
    Dim strArray() As String
'    Dim dbgStr As String
    
    Dim visaAddr As String
    visaAddr = ActiveSheet.Range(VISA_ADDR_TB)
    
    Dim RM As New VisaComLib.ResourceManager
    Dim TB As New VisaComLib.FormattedIO488  'TB : Target Board
    
    Set TB.IO = RM.Open(visaAddr)
    Dim sfc As VisaComLib.ISerial            'RS232C通信設定
    Set sfc = TB.IO
    sfc.BaudRate = 9600
    sfc.DataBits = 8
    sfc.Parity = ASRL_PAR_NONE
    sfc.StopBits = ASRL_STOP_ONE
    sfc.FlowControl = ASRL_FLOW_NONE
    
    TB.IO.TerminationCharacter = 10
    TB.IO.TerminationCharacterEnabled = True
    
    TB.WriteString strTxd
    
    strVal = ""
    For i = 1 To 3                           'Write→ReadのあいだにSleepを入れる
        Sleep (10)
        DoEvents
        
        strVal = strVal + TB.ReadString()
        ReDim strArray(1 To Len(strVal))
        
        For j = 1 To UBound(strArray)        '制御文字を削除する
            strArray(j) = Mid(strVal, j, 1)
            If Asc(strArray(j)) <= 31 Or 127 <= Asc(strArray(j)) Then
'                dbgStr = CStr(i) + "," + CStr(j) + "," + CStr(Asc(strArray(j)))
'                MsgBox (dbgStr)
                strArray(j) = ""
            End If
        Next j
        strVal = Join(strArray, "")
        
        If i <= 2 Then
            strVal = strVal + "/"
        End If
        
        If strVal <> "" Then
            ActiveSheet.Cells(line, COLUMN_RXD).Value = strVal
            'Exit For
        End If
    Next
    
    TB.IO.Close
    Set TB = Nothing
    Set RM = Nothing
End Function

Function ControlSystem(line As Long, strTxd As String)
    Dim i As Long
    
    If strTxd = "wait1000ms" Then
        For i = 1 To 10
            Sleep (100)
            DoEvents
        Next
    
    ElseIf strTxd = "msgbox" Then
        MsgBox ("wait...")
        
    End If
End Function
```

##6. おわりに
- 測定器とマイコンボードをExcelで一元的に制御して測定するのは思っていたより難しくないと思いました。
  - [Keysight IO Librarise Suite](https://www.keysight.com/main/editorial.jspx?cc=JP&lc=jpn&ckey=2304673&nid=-11143.0.00&id=2304673)と[計測技術情報ライブラリ](http://jp.em.keysight.com/tmo/TechnicalSupport/Automatic-measuring/Auto-measuring_top/auto_mgmt_top.html?cmpid=50745)のおかげで百数十行のExcelのマクロでシンプルながらも自動計測システムを組めました。

- Excelで測定器を制御して測定を行いその結果がそのままExcelに保存されるのは便利と思いました。
  - 筆者が学生だった頃はPC-9801の拡張スロットにGPIBボードを挿してGPIBケーブルでオシロスコープをつないでフロッピーディスクに測定結果を保存したのをPC-9801とは別のWindows PCに移してExcelで開いて、、、などとやっていたので隔世の感があります。

- VISA/VISA-COM、共通コマンド、SCPIコマンドが計測器業界で共通なのはありがたいと思いました。
  - 今回たまたま手持ちのオシロスコープで制御プログラム(Excelマクロ)を作成しましたがSCPIコマンドをサポートするオシロスコープであればメーカーを問わずプログラムを再利用できてノウハウも無駄にならずに済むのはメリットと思いました。