Skip to content

cSerialPort Advanced

📖 Table of Contents


Flow Control Details

Flow control coordinates the sender and receiver rates to prevent data loss. cSerialConfig supports five flow control modes.

Flow Control Types

ModeConstantDescription
No flow controlfcNoneNo flow control
Software flow controlfcXonXoffControlled via XON/XOFF characters
Hardware flow control RTS/CTSfcRtsCtsControlled via RTS/CTS signal lines
Hardware flow control DTR/DSRfcDtrDsrControlled via DTR/DSR signal lines
Mixed flow controlfcRtsCtsAndXonXoffBoth hardware and software flow control

Software Flow Control (XON/XOFF)

The sender receives XOFF(&H13) to pause sending when data volume is excessive, and XON(&H11) to resume sending.

vb
cfg.FlowControl = fcXonXoff
' Can customize XON/XOFF characters
cfg.XonChar = &H11
cfg.XoffChar = &H13

You can also manually send XON/XOFF:

vb
sp.SendXOff    ' Request peer to pause sending
sp.SendXOn     ' Request peer to resume sending

Hardware Flow Control (RTS/CTS)

Uses RTS/CTS signal lines for hardware handshake. Sender transmits data only when CTS is high.

vb
cfg.FlowControl = fcRtsCts
' Setting FlowControl automatically sets RtsControl to rcHandshake

Note: After enabling hardware flow control, manual SetRTS/SetDTR may be overridden by flow control mechanisms.

DTR/RTS Control Modes

ModeConstantDTR/RTS Behavior
DisabledcDisable/rcDisableSignal line stays low
EnabledcEnable/rcEnableSignal line stays high
HandshakedcHandshake/rcHandshakeAutomatically managed by flow control
TogglercToggleRTS only, pulls high when data present

Timeout Strategy Details

Win32 serial port timeout is controlled by five parameters. Understanding their working mechanism is crucial.

Timeout Parameters

ParameterDescription
ReadIntervalTimeoutMaximum interval between two read characters(ms)
ReadTotalTimeoutMultiplierPer-byte timeout multiplier(ms/byte)
ReadTotalTimeoutConstantRead fixed timeout(ms)
WriteTotalTimeoutMultiplierWrite per-byte timeout multiplier
WriteTotalTimeoutConstantWrite fixed timeout(ms)

Read Timeout Calculation

Total timeout = ReadTotalTimeoutMultiplier × requested data amount + ReadTotalTimeoutConstant

Three Preset Modes

1. Non-Blocking Read

Read returns immediately, reads data if available, returns empty if not.

vb
cfg.SetNonBlockingRead
' Equivalent to:
' ReadIntervalTimeout = MAXDWORD (&HFFFFFFFF)
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 0

2. Blocking Read

Wait until specified byte count is read, may block permanently.

vb
cfg.SetBlockingRead
' Equivalent to:
' ReadIntervalTimeout = 0
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 0

⚠️ Warning: Blocking mode will hang permanently if no data arrives. Recommend using with ReadTotalTimeoutConstant.

3. Read With Timeout

Specify total timeout duration.

vb
cfg.SetReadTimeout 2000    ' 2 second timeout
' Equivalent to:
' ReadIntervalTimeout = 50
' ReadTotalTimeoutMultiplier = 0
' ReadTotalTimeoutConstant = 2000

Custom Timeout Example

Calculate timeout by byte count, suitable for scenarios with known data length:

vb
' Per byte 10ms + fixed 100ms
cfg.ReadIntervalTimeout = 50
cfg.ReadTotalTimeoutMultiplier = 10
cfg.ReadTotalTimeoutConstant = 100
' Reading 100 bytes total timeout = 10×100 + 100 = 1100ms

Binary Protocol Handling

Send Binary Frame

vb
' Build Modbus RTU request frame
Dim frame(7) As Byte
frame(0) = &H01    ' Slave address
frame(1) = &H03    ' Function code: read holding registers
frame(2) = &H00    ' Start address high byte
frame(3) = &H00    ' Start address low byte
frame(4) = &H00    ' Register count high byte
frame(5) = &H0A    ' Register count low byte
frame(6) = &HC5    ' CRC low byte
frame(7) = &HCD    ' CRC high byte

sp.WriteData frame

Receive and Parse Binary Frame

vb
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
    Dim buf() As Byte
    Dim n As Long
    n = m_Port.ReadData(buf)

    If n < 5 Then Exit Sub    ' Modbus response at least 5 bytes

    ' Parse Modbus response
    Dim slaveAddr As Byte
    Dim funcCode As Byte
    slaveAddr = buf(0)
    funcCode = buf(1)

    If funcCode And &H80 Then
        ' Exception response
        Dim errCode As Byte
        errCode = buf(2)
        Debug.Print "Modbus exception: " & errCode
    Else
        ' Normal response
        Dim byteCount As Byte
        byteCount = buf(2)
        Debug.Print "Received " & byteCount & " bytes of data"
    End If
End Sub

Hex Display Debugging

vb
Private Sub m_Port_DataReceived(ByVal BytesCount As Long)
    Dim buf() As Byte
    m_Port.ReadData buf

    Dim hexStr As String
    Dim i As Long
    For i = 0 To UBound(buf)
        hexStr = hexStr & Right$("0" & Hex$(buf(i)), 2) & " "
    Next
    Debug.Print "RX: " & Trim$(hexStr)
End Sub

Virtual Serial Ports & Loopback Testing

Virtual Serial Port Software

In development environments without physical serial ports, virtual serial port software can create port pairs:

  • com0com (Free, open source)
  • Virtual Serial Port Driver (Commercial software)
  • VSPD (Commercial software)

After installation, create virtual serial port pairs (e.g., COM3 ↔ COM4). Data sent to COM3 will appear in COM4's receive buffer.

Loopback Test Code

vb
Public Sub TestLoopback()
    Dim spTx As New cSerialPort
    Dim spRx As New cSerialPort

    ' Open both ports of the virtual pair
    spTx.PortName = "COM3"
    spRx.PortName = "COM4"

    Dim cfg As New cSerialConfig
    cfg.BaudRate = br9600
    Set spTx.Config = cfg
    Set spRx.Config = cfg.Clone()

    If Not spTx.OpenPort() Then
        Debug.Print "Failed to open COM3"
        Exit Sub
    End If
    If Not spRx.OpenPort() Then
        Debug.Print "Failed to open COM4"
        spTx.ClosePort
        Exit Sub
    End If

    ' Clear buffers
    spTx.PurgeAll
    spRx.PurgeAll

    ' Send
    Dim testStr As String
    testStr = "LOOPBACK_TEST_12345"
    spTx.WriteText testStr
    Debug.Print "Sent: " & testStr

    ' Wait for data arrival
    Dim t As Single
    t = Timer
    Do While spRx.InBufferCount < Len(testStr) And (Timer - t) < 2
        DoEvents
    Loop

    ' Receive
    Dim received As String
    received = spRx.ReadExisting()
    Debug.Print "Received: " & received

    If received = testStr Then
        Debug.Print "[PASS] Loopback test passed"
    Else
        Debug.Print "[FAIL] Loopback test failed"
    End If

    spTx.ClosePort
    spRx.ClosePort
End Sub

Dynamic Configuration Management

Runtime Configuration Changes

vb
sp.OpenPort "COM3"

' Change baud rate
sp.Config.BaudRate = br115200
sp.ApplyConfig          ' Manually apply to opened port

' Read actual configuration from port
sp.RefreshConfig
Debug.Print "Actual baud rate: " & sp.Config.BaudRate

Configuration Cloning

vb
Dim cfg1 As New cSerialConfig
cfg1.BaudRate = br115200
cfg1.Parity = ptEven

' Clone configuration (independent)
Dim cfg2 As cSerialConfig
Set cfg2 = cfg1.Clone()

cfg2.BaudRate = br9600
Debug.Print cfg1.BaudRate   ' Still 115200

Mode String

vb
Dim cfg As New cSerialConfig
cfg.FromModeString "baud=115200 parity=N data=8 stop=1"
Debug.Print cfg.ToModeString()   ' baud=115200 parity=N data=8 stop=1

Modbus RTU Practice

Complete Modbus RTU Master Request

vb
Public Function ModbusReadHoldingRegisters( _
    ByVal sp As cSerialPort, _
    ByVal SlaveAddr As Byte, _
    ByVal StartAddr As Integer, _
    ByVal Quantity As Integer) As Byte()

    ' Build request frame
    Dim frame(7) As Byte
    frame(0) = SlaveAddr
    frame(1) = &H03    ' Function code: read holding registers
    frame(2) = (StartAddr And &HFF00) \ &H100
    frame(3) = StartAddr And &HFF
    frame(4) = (Quantity And &HFF00) \ &H100
    frame(5) = Quantity And &HFF

    ' Calculate CRC16
    Dim crc As Long
    crc = CalcCRC16(frame, 6)
    frame(6) = crc And &HFF
    frame(7) = (crc And &HFF00) \ &H100

    ' Clear buffers and send
    sp.PurgeAll
    sp.WriteData frame

    ' Wait for response (1 second timeout)
    Dim t As Single
    t = Timer
    Do While sp.InBufferCount < 5 And (Timer - t) < 1
        DoEvents
    Loop

    ' Read response
    Dim buf() As Byte
    Dim n As Long
    n = sp.ReadData(buf)
    If n >= 5 Then
        ModbusReadHoldingRegisters = buf
    End If
End Function

Usage Example

vb
Dim sp As New cSerialPort
sp.PortName = "COM3"
sp.Config.BaudRate = br9600
sp.Config.DataBits = 8
sp.Config.Parity = ptEven      ' Modbus RTU typically uses even parity
sp.Config.StopBits = sb1
sp.OpenPort

' Read holding registers from slave 1, start address 0, count 10
Dim response() As Byte
response = ModbusReadHoldingRegisters(sp, 1, 0, 10)

If UBound(response) >= 0 Then
    ' Parse response data
    Dim byteCount As Byte
    byteCount = response(2)
    Debug.Print "Received " & byteCount & " bytes of data"
End If

sp.ClosePort

Last Updated: 2026-07-05

VB6 and LOGO copyright of Microsoft Corporation