Excel VBAでTCP/IP通信しよう(wsock32.dllを使用)

VBAでTCP/IP通信しよう プログラミング

Excel VBAでTCP/IP通信するプログラムを紹介します。

TCP/IP通信にはwsock32.dllを使います。

VBAのModuleにwsock32.dllを使用するための宣言と関数を記述して、Sheetにユーザーインターフェーズのプログラムを記述します。

VBAでTCP/IP通信するときのイメージ

VBAのプログラムは、Sheetにユーザーインターフェースのプログラムを記述して、ModuleにTCP/IP通信プログラムを記述します。

具体的な動きは、Sheetにボタンを配置してユーザーからの要求を受け取り、ModuleのTCP/IP関数を使って、接続した機器と通信します。

VBAのTCP/IP通信の全体イメージ

プログラミング

VBAの標準モジュールの追加

標準モジュールの追加方法です。

『開発』ー『Visual Basic』から『Microsoft Visual Basic for Applications』を表示します。
『挿入(I)』ー『標準モジュール(M)』を選択します。

『挿入』-『標準モジュール』

『Book1 ー Module1(コード)』が表示されます。左側のプロジェクトエクスプローラーに『標準モジュール』ー『Module1』が追加されます。

Book1-Module1(コード)の表示

Module1にプログラムを記述する準備が出来ました。

Module1 プログラム

TCP/IP通信のプログラムです。これを『Book1ーModule1(コード)』に記述します。
宣言、IPアドレス取得関数、接続関数、切断関数、送受信関数、ウェイト関数です。

※コードを分けて掲載してますが、すべてModule1に記述します。

1.宣言

Option Explicit

Public Const INADDR_NONE = &HFFFF
Public Const INADDR_ANY = &H0

Public Const WSA_DESCRIPTIONLEN = 256
Public Const WSA_DescriptionSize = WSA_DESCRIPTIONLEN + 1
Public Const WSA_SYS_STATUS_LEN = 128
Public Const WSA_SysStatusSize = WSA_SYS_STATUS_LEN + 1

Public Const INVALID_SOCKET = -1
Public Const SOCKET_ERROR = -1
Public Const SOCK_STREAM = 1
Public Const SOCK_DGRAM = 2
Public Const MAXGETHOSTSTRUCT = 1024
Public Const AF_INET = 2
Public Const PF_INET = 2

Public Const FIONREAD = &H8004667F
Public Const FIONBIO = &H8004667E
Public Const FIOASYNC = &H8004667D

Public Const WORK_TRUE = 0
Public Const WORK_FALSE = -1

Public Declare PtrSafe Function closesocket Lib "wsock32.dll" _
                                (ByVal s As Long) As Long
Public Declare PtrSafe Function connect Lib "wsock32.dll" _
                                (ByVal s As Long, _
                                sName As sockaddr, _
                                ByVal namelen As Long) As Long
Public Declare PtrSafe Function ioctlsocket Lib "wsock32.dll" _
                                (ByVal s As Long, _
                                ByVal cmd As Long, _
                                argp As Long) As Long
Public Declare PtrSafe Function recv Lib "wsock32.dll" _
                                (ByVal s As Long, _
                                ByVal buf As Any, _
                                ByVal lngLen As Long, _
                                ByVal flags As Long) As Long
Public Declare PtrSafe Function send Lib "wsock32.dll" _
                                (ByVal s As Long, _
                                buf As Any, _
                                ByVal lngLenlen As Long, _
                                ByVal flags As Long) As Long
Public Declare PtrSafe Function Socket Lib "wsock32.dll" Alias "socket" _
                                (ByVal af As Long, _
                                ByVal lngType As Long, _
                                ByVal protocol As Long) As Long

Public Declare PtrSafe Function htons Lib "wsock32.dll" _
                                (ByVal hostshort As Long) As Integer
Public Declare PtrSafe Function inet_addr Lib "wsock32.dll" _
                                (ByVal cp As String) As Long
Public Declare PtrSafe Function gethostbyname Lib "wsock32.dll" _
                                (ByVal strName As String) As Long
Public Declare PtrSafe Function WSAStartup Lib "wsock32.dll" _
                                (ByVal wVersionRequested As Long, _
                                lpWSAData As WSADataType) As Long
Public Declare PtrSafe Function WSACleanup Lib "wsock32.dll" () As Long
Public Declare PtrSafe Function WSAGetLastError Lib "wsock32.dll" () As Long

Public Declare PtrSafe Sub MemCopy Lib "kernel32" Alias "RtlMoveMemory" _
                               (hpvDest As Any, _
                               hpvSource As Any, _
                               ByVal cbCopy As Long)

Type HostEnt
    h_name As Long
    h_aliases As Long
    h_addrtype As Integer
    h_length As Integer
    h_addr_list As Long
End Type

Type sockaddr
    sin_family As Integer
    sin_port As Integer
    sin_addr As Long
    sin_zero As String * 8
End Type

Type WSADataType
    wVersion As Integer
    wHighVersion As Integer
    szDescription As String * WSA_DescriptionSize
    szSystemStatus As String * WSA_SysStatusSize
    iMaxSockets As Integer
    iMaxUdpDg As Integer
    lpVendorInfo As Long
End Type

Type SocketCtrlData
    mlngSock As Long
    musrSockBuf As sockaddr
    musrStartup As WSADataType
End Type

2.IPアドレス取得

'IPアドレス取得
Private Function wrkHostNameAlias(ByVal hstName As String) As Long
    Dim ret As Long
    Dim adr As Long
    Dim ip As Long
    Dim heHost As HostEnt
    
    ip = inet_addr(hstName)
    
    If ip = INADDR_NONE Then
        If gethostbyname(hstName) <> WORK_TRUE Then
            MemCopy heHost, ByVal ret, Len(heHost)
            MemCopy adr, ByVal heHost.h_addr_list, 4
            MemCopy ip, ByVal adr, heHost.h_length
        Else
            ip = INADDR_NONE
        End If
    End If
    
    wrkHostNameAlias = ip
    
End Function

関数
wrkHostNameAlias(ByVal hstName As String) As Long

引数
hstName:文字列のIPアドレス

戻り値
数値化したIPアドレス

3.接続

'接続
Public Function wrkConnectTCP(Sockdat As SocketCtrlData, address As String, 
                                port As String) As long
    Dim adr As Long
    
    wrkConnectTCP = WORK_FALSE
    
    '初期化
    If WSAStartup(&H101, Sockdat.musrStartup) <> WORK_TRUE Then Exit Function
    
    'Socket
    Sockdat.mlngSock = Socket(AF_INET, SOCK_STREAM, 0)
    If Sockdat.mlngSock = WORK_FALSE Then Exit Function
    
    'Connect
    With Sockdat.musrSockBuf
        adr = wrkHostNameAlias(address)
        .sin_family = AF_INET
        .sin_port = htons(CLng(port))
        .sin_addr = adr
        .sin_zero = String$(8, 0)
    End With
        
    If connect(Sockdat.mlngSock, Sockdat.musrSockBuf, _
                                Len(Sockdat.musrSockBuf)) <> WORK_TRUE Then
        Exit Function
    End If
    DoEvents

    'ioctlsocket
    If ioctlsocket(Sockdat.mlngSock, FIONBIO, True) <> WORK_TRUE Then 
        Exit Function
    End If
    
    wrkConnectTCP = WORK_TRUE

End Function

関数
wrkConnectTCP(Sockdat As SocketCtrlData, address As String, port As String) As long

引数
Sockdat:SocketCtrlData構造体のポインタ
address:文字列のIPアドレス
port:ポート番号

戻り値
WORK_TURE/WORK_FALSE

4.切断

Public Sub wrkDisconnectTCP()
    closesocket mySockdat.mlngSock
    Call WSACleanup
End Sub

関数
wrkDisconnectTCP()

5.送受信

'送受信
Public Function wrkCommTCP(sckn As Long, bSnddat() As Byte, _
                                strRet As String) As Long
    Dim lngRet As Long
    Dim lngAll As Long
    Dim strRev As String * 1000
    
    wrkCommTCP = 0
    strRet = ""
    
    '送信
    Call send(sckn, bSnddat(0), UBound(bSnddat) + 1, 0)
    
    'ウェイト
    Call timewait(0.1)
    
    '受信
    Do
        DoEvents
        lngRet = recv(sckn, ByVal strRev, 1000, 0)
        
        If lngRet > 0 Then
            strRet = strRet & Left$(strRev, lngRet)
            wrkCommTCP = wrkCommTCP + lngRet
            If InStr(strRet, vbCrLf) > 0 Then Exit Do
        ElseIf lngRet = SOCKET_ERROR Then
            wrkCommTCP = SOCKET_ERROR
            Exit Do
        End If
          
    Loop
    
End Function

関数
wrkCommTCP(sckn As Long, bSnddat() As Byte, strRet As String) As Long

引数
sckn: SocketCtrlData構造体のmlngSock
bSnddat():送信するバイナリ配列
strRet:返送データ文字列

戻り値
受信した文字数/SOCKET_ERROR

※受信終了の条件は様々なので、接続する機器の仕様を確認してください。
※バイナリデータの場合は可変長のバイト配列を使います。終了条件はバイト数です。

6.ウェイト

'ウェイト
Private Sub timewait(sec As Single)
    Dim sttmr As Single
    Dim ctmr As Single
    
    If sec < 0 Then Exit Sub
    sttmr = Timer
    
    Do
        ctmr = Timer
        If ctmr < sttmr Then ctmr = ctmr + 24# * 60 * 60
        If ctmr > sttmr + sec Then Exit Sub
        DoEvents
    Loop

End Sub

関数
timewait(sec As Single)

引数
sec:一時停止する時間(秒)

Sheet1 プログラム

ユーザーインターフェースのプログラムです。
Sheet1にCommandButton1、CommandButton2、CommandButton3を配置します。

ボタンは以下の処理を行います。
①CommandButton1:接続
②CommandButton2:切断
③CommandButton3:送信

C4、C6、C7のセルは以下の内容となります。
④セルC4:接続成功/失敗
⑤セルC6:送信コマンド
⑥セルC7:受信データ

Excel Sheet にボタンを配置(TCP/IP通信)

※コードを分けて掲載してますが、すべてShee1に記述します。

1.変数の宣言

Option Explicit

'ソケットデータ
Private mySockdat As SocketCtrlData

2.接続

'接続
Private Sub CommandButton1_Click()
    Dim ip As String
    Dim port As String
    
    ip = "192.168.0.102"
    port = "56789"
        
    'TCP接続
    If wrkConnectTCP(mySockdat, ip, port) = SOCKET_ERROR Then
        Range("C4") = "NG"
    else
        Range("C4") = "OK"
    End If
End Sub

サンプルプログラムは以下の設定になっているので、実際に接続する機器の設定にしてください。

IPアドレス:”192.168.0.102”
ポート番号:56789

3.切断

'切断
Private Sub CommandButton2_Click()
    closesocket mySockdat.mlngSock
    Call WSACleanup
    Range("C4") = ""
End Sub

4.送受信

'送受信
Private Sub CommandButton3_Click()
    Dim strSnd As String
    Dim binSnd() As Byte
    Dim strRev As String
    Dim lenStr As Long
    Dim cnt As Long
    
    'C6セルの内容を送信する
    strSnd = Range("C6") & vbCrLf
    lenStr = Len(strSnd) - 1
    
    ReDim binSnd(lenStr)
    
    For cnt = 0 To lenStr
        binSnd(cnt) = Asc(Mid(strSnd, cnt + 1, 1))
    Next
    
    '送受信
    If wrkCommTCP(mySockdat.mlngSock, binSnd, strRev) = SOCKET_ERROR Then
        If WSAGetLastError() > 0 Then
            closesocket mySockdat.mlngSock
            Call WSACleanup
        End If
    End If
    
    '返送データをC7のセルに表示する
    Range("C7") = strRev
    
End Sub

受信の成功/失敗に関わらず、wrkCommTCPからの受け取ったデータをC7セルに表示します。

今回紹介したプログラムを使用してRaspberry PIとWiFi通信している動画の紹介です。

(1分46秒)

(3分29秒)

Excelのグラフをアニメーションで動かす方法を紹介しています。

(47秒)

動作確認

接続と切断、データの送信と受信を確認します。手順は以下の通りです。
(1.と4.は接続している機器側の動きです)

1.接続機器を接続待ち状態にします
2.VBAで接続機器と接続します
3.VBAから接続機器にコマンドを送信します
4.接続機器はコマンドを受け取りVBAにデータ返送します
5.VBAは受信したデータをSheetに表示します
6.VBAから接続機器と切断します

Command1をクリックして接続します。成功すればC4セルに”OK”を表示します。失敗すると”NG”を表示します。

Excel VBA ポートオープン(TCP/IP通信)

Command3をクリックして接続した機器にコマンドを送信します。
下図はC6のセルに『SET,1,1』と記述していますが、実際は接続した機器のコマンドにします。

Excel VBA コマンド送信(TCP/IP通信)

接続した機器からデータを受信します。VBAはデータの終了を示すデリミタ(Cr/Lf)を検出します。
受信データをC7セルに表示します。

CommandButton2をクリックして接続した機器とから切断します。

Excel VBA データ受信とポートクローズ(TCP/IP通信)

以上で動作確認終了です。

まとめ

Excel VBAでTCP/IP通信するプログラムを紹介しました。

特別なプログラムの開発環境がなくても、ExcelでTCP/IP通信に対応したオシロスコープやデジタルマルチメータを直接制御できるようになります。

VBAでRS232C通信する方法を紹介しています。

いろいろなセンサーの使い方を紹介しています。

コメント