0

I have designed an application that communicates with an external device on my development box through a USB->serial adapter (Windows 7 64bit, VS2013, targeting .Net Framework 3.5 SP1). I also have a test box setup with a native serial port with Windows XP SP3 and .Net Framework 3.5 SP1. The external device communicates at 19200, N, 8, 1 and supports hardware flow control. I know the flow control works because when I initiate a report on the device, set flow control to none and manually create a checkbox to turn DTR on and off the device will pause when DTR is toggled off.

I'm having trouble making the hardware flow control work automatically through my Windows desktop application, however (Handshake.RequestToSend). If I leave handshaking set to None, communication is "usually" successful. Sometimes on the slower test box with the native serial port I believe the receive buffer is being overrun when requesting large amounts of data from the device, even though I've set a very large buffer and made the DataReceived routine as efficient as possible. I've tried various flow control settings in the driver itself with no effect. If I set handshaking to RequestToSend in my application communication appears to stall and the device never responds to my initial status request. I've tried various combinations of True/False with DtrEnable and RtsEnable to no avail. I've searched the internet for many hours and haven't seen anything that has so far helped. Here's some of my code:

  Private sbReceive_Extract As New StringBuilder 'Receive buffer

  Private WithEvents comPort As SerialPort

  Private bReady as Boolean = False 'data ready flag

  Private bExtract as Boolean = False 'type of data request

  Public Sub Status_Request()

    sbReceive_Extract.Length = 0 'clear buffer

    If Open_Port() = True

      comPort.DiscardInBuffer()
      comPort.DiscardOutBuffer()

      'Send status request to external serial device
      comPort.Write(Chr(27) & "?" & Chr(13))

      bExtract = False 'Just get status header this time    

      'Loop until device responds to request
      bReady = False
      Do Until bReady = True
        Application.DoEvents()
        'update an onscreen status label as buffer fills
        lblStatus.Text = "Buffer read: " & sbReceive_Extract.Length.ToString
      Loop

    End If

  End Sub


  Public Function Open_Port() As Boolean

      'return code for port open or closed
    Dim bReturnResult As Boolean = False

    If comPort Is Nothing Then
      comPort = New SerialPort
    End If

    Try
      With comPort
        .PortName = "COM1"       'comm port 1
        .BaudRate = 19200  'transfer speed
        .DataBits = 8       '8 bits
        .StopBits = StopBits.One
        .Parity = Parity.None
        .ReadBufferSize = 32768 '32768=size of buffer in bytes
        .ReadTimeout = SerialPort.InfiniteTimeout
        .Handshake = Handshake.RequestToSend 'works when set to none
        .DtrEnable = True 'tried both true/false
        .RtsEnable = True 'tried both true/false
        .Encoding = Encoding.Default
        .NewLine = Chr(13) '13=carriage return
        .ReceivedBytesThreshold = 1
      End With

      If comPort.IsOpen = False Then
        comPort.Open()
      End If

      If comPort.IsOpen = True Then
        bReturnResult = True
      Else
        bReturnResult = False
      End If

    Catch Ex As Exception
      bReturnResult = False
    End Try

    Return bReturnResult

  End Function

Private Sub comVoteExt_DataReceived(ByVal sender As SerialPort, ByVal eArgs As SerialDataReceivedEventArgs) Handles comVoteExt.DataReceived

Dim sData As Integer = 0 'integer to hold serial data

Try
  Do
    sData = sender.ReadChar 'take one char off the stack
    sbReceive_Extract.Append(Chr(sData))
    If bExtract = True Then
      'Check for data
      If iLength = 0 Then
          'hex 04 is end of data on tally stream
        If Chr(sData) = Chr("&H04") Then
          iLength = sbReceive_Extract.Length
        End If
      Else
           '6 bytes reserved for checksum and carriage return
        If sbReceive_Extract.Length >= iLength + 6 Then
            'Meets checksum length. Ready for next stage.
          bReady = True
        End If
      End If
    Else
        '20 bytes of header information 
      If sbReceive_Extract.Length > 20 Then
          'Data is ready for next stage. Minimum header length met.
        bReady = True
      End If
    End If
  Loop Until sender.BytesToRead = 0
Catch exc As Exception
End Try

End Sub

Does anything look like it could be causing my handshaking problem? Is there some way to "wake up" the device once the port is open so it starts communicating? I've tried setting DTREnable to true after the port is open and it doesn't seem to matter. I'm quite stumped.

  • Your code is buggy. DataReceived is fired on a worker thread. Which means that Application.DoEvents() does nothing and testing a Boolean inside a loop can fail to see the value change at all when you build and run the release build. Don't use DataReceived at all when you write your code like this, just read directly after you write. And implement the ErrorReceived event to *know* that you got an overrun. – Hans Passant Jan 18 '14 at 18:15
  • I have two Application.DoEvents() - one in my calling loop and another in the DataReceived event. I will eliminate the one in the DataReceived event but the one in the calling routine is needed so that I can update a status label on my form (not shown in code for simplicity). The boolean (bReady) is declared at the class level (not shown) and definitely works just fine when updated by the event. I can certainly implement ErrorReceived, but I'm still hopeful someone can help me with flow control, which was the reason for my question. – Bernie Hirsch Jan 18 '14 at 20:23
  • When I attempt to use Handshake.RequestToSend my communication doesn't even start. – Bernie Hirsch Jan 18 '14 at 20:32
  • Is your serial connection's cable wired correctly for hardware flow control? – Mark Hall Jan 18 '14 at 20:36
  • It's a custom cable but my understanding is that yes, it is wired correctly for hardware flow control. I have a 9-pin female on one end and an RJ-45 on the other. Also, as previously stated I have verified that I can start and stop the device from transmitting by manually changing the DTREnable value in realtime. I wouldn't mind manually checking the pins with a continuity tester, though. – Bernie Hirsch Jan 18 '14 at 20:44
  • Your cable isn't going to work correctly for hardware flow control as you need at least 5 pins to carry the minimum items of the flow control signals. IIRC, you really need 7 pins to function minimally for hardware flow control. You will probably need to set RTS to true and DTR to true on the PC side to even get data to flow. If you need to manually control the flow, set CTS to false to stop data coming from your instrument. – Adam Zuckerman Jan 19 '14 at 04:53
  • @HansPassant is correct about the flow of your code too. You will want to have one place only that can handle inbound data. Move that data around as necessary, but do NOT try to read the input buffer outside of the event handler comVoteExt_DataReceived. You can cause buffer over-runs very easily. – Adam Zuckerman Jan 19 '14 at 04:55
  • @AdamZuckerman an RJ-45 connector has 8 pins and a 9 pin female connector has 9 pins, so if serial flow control requires 7 pins at a minimum I guess I'm missing your logic. Seems like your answer is missing a few pins. – Bernie Hirsch Jan 19 '14 at 16:21
  • @AdamZuckerman exactly where in my code am I reading any input buffer data outside of the event handler? I have a loop checking a boolean value indicating that the transmission is complete and only after that point am I doing anything with the buffer. – Bernie Hirsch Jan 19 '14 at 16:23
  • Here's a diagram of the serial interface. https://www.dropbox.com/s/nondrv4sprf407u/SerialInterface.jpg – Bernie Hirsch Jan 19 '14 at 16:36
  • Let me approach this question from another direction. I know I can pause the device by merely changing the DTREnable value. How can I manually test the remaining system receive buffer during parsing and determine that I'm getting close to a buffer overrun? I know how to test the total buffer size, just not the remaining space available. – Bernie Hirsch Jan 19 '14 at 16:54
  • @BernieHirsch, it has been a while since I made cables. I am probably mistaken in remembering the RJ-45 as a 6 pin connector. While you don't explicitly perform any direct querying of the receive buffer in the code above, it can be _*extremely*_ tempting to do so. I would strongly recommend one sub/function for sending data out and using the event handler for data coming in. And never check the comm port outside either of those two subs. – Adam Zuckerman Jan 21 '14 at 01:51

0 Answers0