Arduino + EXCEL VBA シリアル通信モニタ

#概要
　仕事で温度センサーの精度検証を行っており、その際に活用できるかなと思いEXCEL VBA で行うシリアル通信モニタを作成しました。

今までは記録用の資料を作成し、ArduinoIDEのツールの「シリアルモニタ」で表示された温度を転記、計器で実測した値を入力して精度の確認の資料としてきました。

またArduinoIDEのツール「シリアルプロッタ」でグラフを見て推移を確認していました。ですが、このグラフでは細かい数値を確認することが出来ず困っていました。

そこで何とかできないかとモニタリングの方法を調べました。いくつかの方法がありましたが、シリアルデータをＥＸＣＥＬで出力する方法を用意しておけば今後、別データのモニタリングが必要になった場合にも応用が利くのではないかとこの方法を採用しました。

今回は5分置きに計測した温度がEXCELに出力されるようにしました。また出力シートに実測した値を計測中にも入力できるようにと考え作成しました。

#参考にしたサイト
下記のサイトでArduino + EXCEL VBA　については書かれておりましたので、参考にさせていただきました。
https://blogs.yahoo.co.jp/nobita_rx7/28090650.html

# Arduino側の設定
精度検証を行いたいセンサーの値をカンマ区切りで取得させています。

使用するセンサー：
　[atlas-scientific RTD Temperature](https://www.atlas-scientific.com/product_pages/circuits/ezo_rtd.html)
　[防水型 温度センサー DS18B20](https://www.amazon.co.jp/UMEMOTO-%E9%98%B2%E6%B0%B4%E5%9E%8B-%E6%B8%A9%E5%BA%A6%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC-DS18B20%E4%BA%92%E6%8F%9B/dp/B0082MYD06)

取得するシリアルデータフォーマット
　温度１（atlasの温度データ）,温度２（DS18B20の温度データ）
　例
　　28.021,28.22

# EXCEL VBA シリアル通信モニタ
シリアル通信について
　シリアル通信をするために参考にしたサイトで紹介されていた[EasycCmm](http://easycomm.brothersoft.jp/)のモジュールを使用します。

仕様：
　メインシートにてポート番号を指定可能。
　開始ボタンを押すとモニタリングが開始される。
　モニタリングが開始されるとテンプレートシートを元に新規ブックが作成され、モニタリングされた値が出力される。
　モニタリング中もデータシートを編集が可能（実測値の入力などを平行して行えるようにする）
　終了ボタンを押すとモニタリングが終了される。

##EXCEL VBAコード
メインシートのプログラム：
開始ボタン、停止ボタンがあり、ポート番号を設定できるメインシートです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/409074da-8dc5-de9a-1e09-cc6028d130de.png)

※プログラムの中で使われているshtMain.Range("ポート番号")は「名前の管理」で定義されているセルから参照しています。
※Worksheetのオブジェクト名を下記のように変更して参照しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/5cb962be-c0be-9508-61d5-660697296e14.png)


```vb:shtMain
Option Explicit

Enum E_COL
    C_時刻 = 1
    C_AtlasRTDCircuit
    C_DS18B20
End Enum

Private WithEvents m_monitoring As clsMonitoring

'-------------------------------
' モニタリングを開始します
'-------------------------------
Sub MonitoringStart()
    Dim bk As Workbook
    Dim sht As Worksheet
    Dim r As Long
    
    Set m_monitoring = New clsMonitoring
    
    'テンプレートシートを元に新しいブックを作成
    shtTemp.Copy
    
    Set bk = ActiveWorkbook
    Set sht = bk.Sheets(1)
    
    'カーソルを初期位置に設定
    r = 3
    sht.Cells(r, C_時刻).Select
    ActiveCell.Offset(0, 0).Activate
    
    '５分間隔でモニタリング開始
    Call m_monitoring.MonitoringStart(CInt(shtMain.Range("ポート番号")), 5, sht, r)
End Sub

'-------------------------------
' モニタリングを終了します
'-------------------------------
Sub MonitoringEnd()
    If m_monitoring Is Nothing Then
        Exit Sub
    End If
    
    Call m_monitoring.MonitoringEnd
    
    Set m_monitoring = Nothing
    
    MsgBox "モニタリングを停止しました。"
End Sub

'-------------------------------
' モニタリングイベント：モニタリング結果をシートに反映
'-------------------------------
Private Sub m_monitoring_DisplaySerialData(sht As Worksheet, r As Long, value As String)
    Dim vals As Variant
    
    vals = Split(value, ",")
    
    sht.Cells(r, C_時刻) = Format(Now(), "yyyy/mm/dd hh:mm:ss")
    sht.Cells(r, C_AtlasRTDCircuit) = vals(0)
    sht.Cells(r, C_DS18B20) = vals(1)
End Sub
```

テンプレートシート：
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191429/f975dfd7-0dc0-78ac-1d95-a83575490214.png)

モニタリングクラス：
　モニタリングの開始、終了を行います。
　シリアルデータ書き込み時に出力イベントを発生させています。

```vb:clsMonitoring.vb
Option Explicit

' 取得したシリアルデータを表示するイベント
Public Event DisplaySerialData(sht As Worksheet, r As Long, value As String)

' モニタリング終了フラグ
Private m_endFlg As Boolean

'-------------------------------
' モニタリングを開始します
'
' ※spanMinute: 10 の場合は（10:10,10:20,10:30～）という10分ごとの区切りで実行されます。
'-------------------------------
Function MonitoringStart(portNo As Integer, spanMinute As Integer, sht As Worksheet, stRow As Long)
    ec.COMn = portNo 'COM3を指定します
    ec.Setting = "9600,n,8,2" '通信条件(ボーレート，パリティビット数，データビット数，ストップビット数)の設定
    ec.HandShaking = ec.HANDSHAKEs.RTSCTS '通信ハンドシェークの設定
    ec.Delimiter = ec.DELIMs.CrLf 'データの区切りを示す文字列を設定します
    ec.OutBuffer = 100& * 1024& '現在処理の対象になっているポートの送信バッファを100kBに設定します
    
    Dim value As String
    Dim r As Long
    Dim nextTime As Date
    
    r = stRow
    Do While Not m_endFlg
        ' 指定分ごとに実行
        nextTime = GetNextTimeMinute(spanMinute)
    
        ' 指定分たつまでの間、EXCELが編集できないのは困るので、DoEventでイベントを処理しながら待機
        Do While nextTime > Now()
            DoEvents
            Sleep (100)
            If m_endFlg Then
                Exit Do
            End If
            
            value = Replace(ec.AsciiLine, vbCr, "")
            ec.InBufferClear '受信バッファをクリア
        Loop
        
        ' 時間が経過した場合、取得したシリアルデータを表示
        RaiseEvent DisplaySerialData(sht, r, value)
        
        r = r + 1
        
        DoEvents
    Loop
    
    'ポートを閉じる
    ec.COMn = -1 '終了処理

End Function

'-------------------------------
' モニタリングを終了します
'-------------------------------
Function MonitoringEnd()
    m_endFlg = True
End Function

'-------------------------------
' 次の計測時間を取得します
'-------------------------------
Function GetNextTimeMinute(mi As Integer) As Variant
    Dim t As Date
    Dim ms As String
    Dim m As Integer
    
    t = Now()
    m = Mid(Format(t, "HH:MM:SS"), 4, 2)
    m = m - m Mod mi + mi
    
    ms = "00:" & Right(Format(t, "HH:MM:SS"), 5)
    
    
    GetNextTimeMinute = t - TimeValue(ms) + TimeValue(WorksheetFunction.RoundDown(m / 60, 0) & ":" & m Mod 60 & ":00")
End Function

```

#まとめ
メインシート側でデータフォーマットを判断するようにプログラムを作成したので、今回とは違うフォーマットデータのモニタリングが必要になった場合も最小限の修正で対応が可能になったと思います。
グラフデータは出来上がったデータを元に、グラフツールを使って作成する形でしばらく様子を見たいと思います。