概要
Excelでシリアル通信をするためのVBAコードの紹介です。
構成
シリアル通信用のクラスを"clsCOM_Port"という名称で定義し、インスタンスして使用しています。
注意事項
COMポート番号が2桁だと上手く動きません。。。
対策はクラスライブラリのコードの冒頭にコメントしてます。
シリアル通信クラス
クラスモジュールを作成し以下のコードを張り付ける
'COMポートが2ケタだとうまく行かないので1ケタにする対策
'ターミナルから以下コマンドを入力
' Set devmgr_show_nonpresent_devices = 1
' Start devmgmt.msc
'
' 注)windows7以降では、すべてのプログラム→アクセサリ→コマンドプロンプトの部分で右クリックして管理者として実行が必要
'
'表示→非表示デバイスの表示 で接続されていないすべてのデバイスが表示される。あどは不要なCOMポートを消していく
'********************************************************************************
' COMポートから送受信するマクロ
'********************************************************************************
'WindowsAPI参照定義
'クリエイト(オープン)
Private Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" _
(ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare PtrSafe Function SetCommState Lib "kernel32" _
(ByVal hCommDev As Long, _
lpDCB As typDeviceControlBlock) As Long
Private Declare PtrSafe Function SetupComm Lib "kernel32.dll" _
(ByVal hFile As Long, _
ByVal dwInQueue As Long, _
ByVal dwOutQueue As Long) As Long
'パージ(クリア)
Private Declare PtrSafe Function PurgeComm Lib "kernel32.dll" _
(ByVal hFile As Long, _
ByVal dwFlags As Long) As Long
'タイムアウト設定
Private Declare PtrSafe Function SetCommTimeouts Lib "kernel32" _
(ByVal hFile As Long, _
lpCommTimeouts As COMMTIMEOUTS) As Long
'エラークリア
Private Declare PtrSafe Function ClearCommError Lib "kernel32" ( _
ByVal hPort As Long, _
lpErrors As Long, _
lpStat As COMSTAT _
) As Long
'リードアクセス
Private Declare PtrSafe Function ReadFile Lib "kernel32" _
(ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, _
lpNumberOfBytesRead As Long, _
lpOverlapped As Long) As Long
'ライトアクセス
Private Declare PtrSafe Function WriteFile Lib "kernel32" _
(ByVal hFile As Long, _
lpBuffer As Any, _
ByVal nNumberOfBytesToWrite As Long, _
lpNumberOfBytesWritten As Long, _
lpOverlapped As Long) As Long
'クローズ
Private Declare PtrSafe Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
'デバイス制御ブロック構造体の定義
Private Type typDeviceControlBlock
DCBlength As Long '構造体のサイズ
BaudRate As Long 'ボーレイト(bps)の設定
fBitFields As Long 'ビット単位のフィールド定義
'fBinary :1 ; バイナリモードかどうか
'fParity :1 ; パリティチェックの有無
'fOutxCtsFlow :1 ; CTSを監視するかどうか
'fOutxDsrFlow :1 ; DSRを監視するかどうか
'fDtrControl :2 ; DTRによるハンドシェーク(2ビット)
'fDsrSensitivity :1 ; TrueのときDSRがオフのときの受信データを無視する
'fTXContinueOnXoff :1 ;Xoff文字を送信した後も送信を続けるかどうか
'fOutX :1 ; TrueのときXoff文字を受信すると送信を停止しXon文字で再開
'fInX :1 ; Trueのとき受信バッファの空きに応じてXoff、Xon文字が送信される
'fErrorChar :1 ; Trueのときパリティエラーの処理をする
'fNull :1 ; Trueのときヌル文字は破棄される
'fRtsControl :2 ; RTSによるハンドシェーク(2ビット)
'fAbortOnError :1 ; Trueのときエラーが発生したら読み書きを終了する
'fDummy2 :17; 未使用
wReserved As Integer '予約
XonLim As Integer '受信バッファ中のデータが何バイトになったらXon文字を送るかを指定
XoffLim As Integer '受信バッファの空きが何バイトになったらXoff文字を送るかを指定
ByteSize As Byte '1データのビット数(5,6,7,8)を指定
Parity As Byte 'パリティの方式を指定
'0・・・NOPARITY(パリティなし)
'1・・・ODDPARITY(奇数パリティ)
'2・・・EVENPARITY(偶数パリティ)
'3・・・MARKPARITY(常にマーク)
StopBits As Byte 'ストップビット数を指定 '0=1ビット、1=1.5ビット、2=2ビット
XonChar As Byte 'Xon文字を指定
XoffChar As Byte 'Xoff文字を指定
ErrorChar As Byte 'パリティエラーの場合に使う文字を指定
EofChar As Byte '非バイナリモードの場合のデータ終了文字の指定
EvtChar As Byte 'イベントを生成する文字を指定
wReserved1 As Integer '予約
End Type
'***** タイムアウト設定構造体の定義 *****
Private Type COMMTIMEOUTS
ReadIntervalTimeout As Long '文字の読み込みの待ち時間
ReadTotalTimeoutMultiplier As Long '読み込みの1文字あたりの時間
ReadTotalTimeoutConstant As Long '読み込みの定数時間
WriteTotalTimeoutMultiplier As Long '書き込みの1文字あたりの時間
WriteTotalTimeoutConstant As Long '書き込みの定数時間
End Type
'バッファサイズ
Private Const INP_BUFSIZE As Long = 65536 '入力バッファサイズ
Private Const OUT_BUFSIZE As Long = 2048 '出力バッファサイズ
Private Const PURGE_TXCLEAR As Long = &H4 '送信バッファクリア
Private Const PURGE_RXCLEAR As Long = &H8 '受信バッファクリア
'ファイルアクセスモード
Private Const GENERIC_READ = &H80000000 '読み取り用定数
Private Const GENERIC_WRITE = &H40000000 '書き込み用定数
Private Const OPEN_EXISTING = 3 '既存のファイルの処理
'パリティ
Private Const NOPARITY = 0 'パリティなし
Private Const ODDPARITY = 1 '奇数パリティ
Private Const EVENPARITY = 2 '偶数パリティ
Private Const MARKPARITY = 3 '常にマーク
Private Const SPACEPARITY = 4 'スペース
'ストップbit幅
Private Const ONESTOPBIT = 0 '1ビット
Private Const ONE5STOPBITS = 1 '1.5ビット
Private Const TWOSTOPBITS = 2 '2ビット
Private pDCB As typDeviceControlBlock 'デバイス制御ブロック構造体
Private pTIMEOUT As COMMTIMEOUTS 'タイムアウト設定構造体
Private pComm As Long 'ハンドル
Private Type COMSTAT
fCtsHold As Long
fDsrHold As Long
fRlsHold As Long
fXoffHold As Long
fXoffSent As Long
fEof As Long
fTxim As Long
fReserved As Long
cbInQue As Long
cbOutQue As Long
End Type
'********************************************************************************
'オープン:引数1 COMポート名称("COM5"とか) 引数2:ボーレート
'********************************************************************************
Public Sub OpenCOM(ByVal iPortNum As String, ByVal iRate As Long)
'シリアルポートをオープンする
pComm = CreateFile(iPortNum, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
If (pComm = -1) Then 'ポートのオープン失敗
MsgBox "COM port open error!!": End
End If
'***** 通信条件の設定 *****
With pDCB
.BaudRate = iRate 'ボーレート
.fBitFields = &H1 'バイナリモード
.fBitFields = pDCB.fBitFields Or &H2 'パリティチェックなし
.ByteSize = 8 'ビット長の指定
.Parity = NOPARITY 'パリティなし
.StopBits = ONESTOPBIT 'ストップビット数を1指定
End With
'状態確認
If (SetCommState(pComm, pDCB) = 0) Then
Call CloseCOM: MsgBox "COM state error!!": End
End If
'バッファの設定
If (SetupComm(pComm, INP_BUFSIZE, OUT_BUFSIZE) = 0) Then
Call CloseCOM: MsgBox "COM buffer error!!": End
End If
'バッファのクリア
If (PurgeComm(pComm, PURGE_TXCLEAR) = 0) Then
Call CloseCOM: MsgBox "COM purge error!!": End
End If
'タイムアウトの設定
pTIMEOUT.ReadIntervalTimeout = 0 '文字の読み込み待ち時間
pTIMEOUT.ReadTotalTimeoutMultiplier = 10 '読み込みの1文字あたりの時間
pTIMEOUT.ReadTotalTimeoutConstant = 10 '読み込みの定数時間
pTIMEOUT.WriteTotalTimeoutMultiplier = 10 '書き込みの1文字あたりの時間
pTIMEOUT.WriteTotalTimeoutConstant = 10 '書き込みの定数時間
If (SetCommTimeouts(pComm, pTIMEOUT) = 0) Then
Call CloseCOM: MsgBox "COM timeout setting error!!": End
End If
End Sub
'********************************************************************************
'クローズ
'********************************************************************************
Public Sub CloseCOM()
Call CloseHandle(pComm)
pComm = 0 'ハンドルをクリアしておく
End Sub
'********************************************************************************
'受信バッファクリア
'********************************************************************************
Public Function ClearBuffer() As Boolean
Dim rtn As Boolean
Rt = PurgeComm(pComm, PURGE_RXCLEAR)
If (Rt = 0) Then
rtn = False '0が返ってきたら失敗
Else
rtn = True
End If
ClearBuffer = rtn
End Function
'********************************************************************************
'リード
'********************************************************************************
Public Function Read()
Dim ByteBuffer() As Byte '受信バッファ
Dim StrBuffer As String '受信バッファ
ByteBuffer = ReadByte() 'バイナリで受信
StrBuffer = StrConv(ByteBuffer, vbUnicode) 'Ascii文字列をユニコード文字列に変換
Read = StrBuffer
End Function
'********************************************************************************
'バイナリリード
'********************************************************************************
Public Function ReadByte()
Const PACKETSIZE As Integer = 64
Dim ByteBuf() As Byte '受信バッファ
Dim getSize As Long '受信データサイズ
Dim totalSize As Long
totalSize = 0
Do
'受信バッファをパケットサイズ分だけ広げる
ReDim Preserve ByteBuf(totalSize + PACKETSIZE)
'パケットサイズ
If (ReadFile(pComm, ByteBuf(totalSize), PACKETSIZE, getSize, ByVal 0) = 0) Then 'データの受信
Call CloseCOM: End
End If
'受信したサイズを累積カウントする
totalSize = totalSize + getSize
'取得サイズがゼロになったら終了する
If getSize <= 0 Then
Exit Do
End If
Loop
'バッファサイズを実受信サイズに合わせて余分を削ぎ落とす
If (totalSize > 0) Then
ReDim Preserve ByteBuf(totalSize - 1)
Else
ReDim ByteBuf(0)
End If
ReadByte = ByteBuf
End Function
'********************************************************************************
'バイナリ送信
'********************************************************************************
Public Function SendByte(ByRef mData() As Byte) As Boolean
Dim PACKETSIZE As Long '一度にまとめて送るデータのサイズ
Dim remainSize As Long 'まだ送信してない残りのデータサイズ
Dim fLen As Long '送信されたバイト数
Dim fLoc As Long '送信するバイト位置
Dim ret As Boolean
ret = True
PACKETSIZE = 16 '一度にまとめて送るサイズの設定(上限はPCスペックと受信側の都合で決まる)
remainSize = UBound(mData) + 1 '未送信データサイズ=全データサイズ
fLoc = 0
Do
'残データ量がパケットサイズ以下ならパケットサイズ縮小する
If (remainSize < PACKETSIZE) Then
PACKETSIZE = remainSize
End If
'送信処理
If (WriteFile(pComm, mData(fLoc), PACKETSIZE, fLen, ByVal 0) = 0) Then 'データの送信
Call CloseCOM
ret = False
Exit Do
End If
fLoc = fLoc + PACKETSIZE '次に送信するバイト位置
remainSize = remainSize - PACKETSIZE '残データサイズ更新
'残データ量がなくなったら送信完了
If (remainSize <= 0) Then
Exit Do
End If
Loop
SendBin = ret
End Function
'********************************************************************************
'アスキー送信
'********************************************************************************
Public Function SendAscii(ByVal iAscii As String) As Boolean
Dim ByteBuffer() As Byte
Dim ByteBufferSize As Integer
'バイナリバッファのサイズを決定
ByteBufferSize = Len(iAscii)
ReDim ByteBuffer(ByteBufferSize)
'アスキーをバイナリに変換
For i = 0 To ByteBufferSize - 1
ByteBuffer(i) = Asc(Mid(iAscii, i + 1, 1))
Next
'バイナリ送信
Call SendByte(ByteBuffer)
End Function
使い方
Sub testCOM()
'COMポートを開く
Dim CP As clsCOM_Port
Set CP = New clsCOM_Port
Call CP.OpenCOM("COM5", 115200) '引数:COM番号,ボーレート
'バッファクリア
Call CP.ClearBuffer
'文字列送信
CP.SendAscii ("r" & vbCrLf)
'受信バッファからバイナリ配列としてデータ取得
x = CP.ReadByte()
'COMポートを閉じる
CP.CloseCOM
End Sub