〇VB6 MSCommでシリアル通信

VB6でシリアル通信を行う場合には大抵、MSCommコントロールを使います。
他の方法、C言語みたいにAPIをゴリゴリ叩く方法も考えられますが見たことありません。
そこで、MSCommコントロールの使い方を調べて行きたいと思います。
MSCommコントロールでは 1〜16 までのポートしか開けませんので注意が必要です。

まずは、通常コントロールが並んでいる場所にMSCommコントロールは無いと思いますので追加します。


メニューのプロジェクト→コンポーネントを選択します。


Microsoft Comm Control 6.0 のチェックボックスをONにして適用ボタンを押します。
そうすると、コントロールの場所に電話のコントロールが追加されたはずです。


▼受信イベントを拾う
コントロールをフォームに貼り付けてダブルクリックします、そうすると、Private Sub MSComm1_OnComm() というイベントが作成されたと思います。
そこで、次の様に書き換えてみましょう。

Private Sub MSComm1_OnComm()
    Select Case MSComm1.CommEvent
        Case comEvReceive
            MSComm1.InputLen = 0            '読み取るバイト数を指定 0は全て読み込む
            Label1.Caption = MSComm1.Input
        End Select
End Sub
これは、MSCommがデータを受信した時にLabelの文字を書き換えるコードです。


▼ポートへの接続
次にポートへの接続とイベントの閾値を設定します。

Private Sub Form_Load()
          MSComm1.CommPort = 4
          MSComm1.Settings = "9600,N,8,1"
          MSComm1.PortOpen = True
          MSComm1.RThreshold = 1
End Sub
COMポートは4番、ボーレート9600、パリティなし、データは8ビット、ストップビットは1にてポートを開けます。
後は、見ての通りですが、RThreshold を設定しないとイベントが発生しません。
RThreshold を 1 に設定すると、受信バッファに 1byte が格納されるたびにイベントが発生して値を受信できます。
文字列モード(デフォルト)で開いている場合には1文字づつ取得したとしてもバッファに値が残っている限り何度も何度もイベントが発生します。
バイナリモード(MSComm1.InputMode = comInputModeBinary)で開いている場合には格納されるたびにイベントが発生しますが、バッファに値が残っていてもイベントは発生しません(一度のイベントでバッファ内容を全て取得する必要がある)

▼文字の送信
MSComm1.OutBufferCount = 0      'バッファクリア
Do
    MSComm1.Output = "送信文字列" & vbCr & vbLf
Loop While MSComm1.OutBufferCount >= 1
上記コードを走らせると送信完了まで待機し、文字が送信されます。


▼文字の受信 イベントで1文字づつ
Private Sub MSComm1_OnComm()
    Select Case MSComm1.CommEvent
        Case comEvReceive
            MSComm1.InputLen = 1            '読み取るバイト数を指定 0は全て読み込む
            str = str + MSComm1.Input
        End Select
End Sub
イベントが発生するごとに1文字づつ読み込んでゆきます、受信した文字が全て読み込まれるまで何度も何度もイベントが発生します。


▼文字の受信 タイマーなどでループして
Private Sub Timer1_Timer()
    If MSComm1.InBufferCount < 0 Then
        str = str + MSComm1.Input
    End If
    Text1.Text = str
End Sub
タイマーなどで繰り返し監視することで文字列を受信します。


▼バイナリの送信
MSComm1.OutBufferCount = 0      'バッファクリア
Dim out(4) As Byte        'バイト配列の定義 0〜4 まで、5個の配列
out(0) = 0
out(1) = 1
out(2) = 2
out(3) = 3
out(4) = 4
Do
    MSComm1.Output = out
Loop While MSComm1.OutBufferCount >= 1
上記コードを走らせると送信完了まで待機し、バイナリが送信されます。
VB6で注意が必要なのが配列定義を4にすると実際には0〜4までの配列が作成されて5個が確保されているという事です。
忘れていると最後の要素が初期値の0のままで0を最後に送信する事になります。


▼バイナリの受信
Private Sub Form_Load()
          MSComm1.CommPort = 1
          MSComm1.Settings = "9600,N,8,1"
          MSComm1.InputMode = comInputModeBinary
          MSComm1.NullDiscard = False ' 0 を受信する Trueにすると0を無視する
          MSComm1.PortOpen = True
          MSComm1.RThreshold = 1
End Sub

Private Sub MSComm1_OnComm()
    Select Case MSComm1.CommEvent
        Case comEvReceive
            Dim b() As Byte
            Dim i As Integer
            MSComm1.InputLen = 0            '読み取るバイト数を指定 0は全て読み込む
            b = MSComm1.Input
            For i = LBound(b) To UBound(b)
                str = str + " " + CStr(b(i))
            Next i
        End Select
End Sub
ポートを開く前にバイナリモードに設定し、0を受信するようにします(初期値はこのままなので必要ないかも)
その後、バイト配列にて受信します(0を送信されたら0として受信します)
読み取るバイト数を1に設定すると文字列と違い何度もイベントが発生しないので、0に設定して一度のイベントでバッファの内容を全て読み込む必要があります。


■ポートをスキャンして通信機器を探す

通信機器に接続するときにポート番号を調べて設定し接続するのは面倒です。
そこで、ポートをスキャンする方法を考えます。

まず、なんらかの機器が接続されているポートを総当たりで調べます。
その後、開いているポートに対してコマンドを送信して機器を確認します。

しかし、接続する通信機器に特定のコマンドを送ったら、必ず決まったコマンドを返すという機能が無いと特定の機器であるのか識別できませんので、
私の場合には、t[cr][lf] と送った場合に、必ず NG[cr][lf] というコマンドを返す機能を通信機器に追加しました。

以下のコードは、 t[cr][lf] と送ったら、必ず NG[cr][lf] が返ってくるが前提になります。
Option Explicit
Private Declare Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As Long)

Private Function connect(ByVal port As Integer) As Boolean
    connect = False
    If MSComm1.PortOpen Then MSComm1.PortOpen = False
On Error GoTo Err1
    MSComm1.CommPort = port
    MSComm1.Settings = "9600,E,8,1" '偶数パリティで設定しているため注意
    MSComm1.DTREnable = False
    MSComm1.PortOpen = True
    connect = True
Err1:
End Function

Private Function portScan() As Integer
    Dim portNo As Integer
    Dim str As String
    
    For portNo = 1 To 16
        If connect(portNo) Then
            MSComm1.OutBufferCount = 0      'バッファクリア
            Do
                MSComm1.Output = "t" & vbCr & vbLf
            Loop While MSComm1.OutBufferCount >= 1
            
            Dim i As Integer
            For i = 1 To 100
                Sleep 10
                If MSComm1.InBufferCount > 3 Then
                    str = MSComm1.Input
                    If str = ("NG" + vbCrLf) Then
                        '接続成功
                        portScan = portNo
                        Exit Function
                    End If
                End If
            Next i
        End If
    Next portNo
    portScan = -1 '接続先のポートが見つからなかった
End Function

Private Sub portScanReset()
    'ポート検索用に設定されているため、自分のアプリケーション用に設定しなおす
    MSComm1.InputLen = 1
    MSComm1.RThreshold = 1
End Sub

Private Sub Form_Load()
    Dim portNo As Integer
    portNo = portScan()
    
    If portNo <> -1 Then
        Form1.Caption = "接続先ポート番号 : " & portNo
    Else
        Form1.Caption = "接続エラー"
    End If
    portScanReset
End Sub
開いているポートを探すには、とりあえず開けてみてエラーが発生しないかどうかで判断できました。
その後は、開いたポートに対して特定のコマンドを送ってみて応答により判断しています。
アプリ起動後に接続されたポートに対しては検知できませんでした。


▲トップページ > Visual BASIC と C#