locked
Data Received (delay problem)

    Question

  • I received data from the serial port and display the data on a real time graph format. However I discover serious delay in the data received. When the value on the device has changed, the value shown on the graph doesn’t not change until few minutes later. I wonder if the data received event fire as long as there’s data coming in? so I put the thread to sleep every second. I only need to acquire a data every single second.

    In the form load, I enable the timer and and set timer interval to 1000.

    Private Sub OnDataReceived(ByVal sender As Object, _

    ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _

    Handles SerialPort1.DataReceived

    Try

    TextBox1.Invoke(New myDelegate(AddressOf UpdateTextBox), New Object() {})

    Catch ex As Exception

    MsgBox(ex.Message)

    End Try

    End Sub

     

    Public Delegate Sub myDelegate()

     

     

     

    Public Sub UpdateTextBox()

    Dim result As String = ""

    Dim strInData As String = ""

    Dim final As String = ""

     

     

    While SerialPort1.BytesToRead > 0

    strInData &= Chr(SerialPort1.ReadByte())

    End While

     

    If strInData.Length >= 3 Then

    result &= strInData.Substring(1, 1)

    Dim bytes() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(result.ToCharArray)

     

    For Each b As Byte In bytes

    final &= (b - 48) * 0.5

     

    Dim d As Decimal = final

    d = Decimal.Round(d, 0, MidpointRounding.AwayFromZero)

     

    buffer.Add(final)

    Next

    System.Threading.Thread.Sleep(1000)

    End If

    End Sub

     

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

    If i = buffer.Count Then

    i = -1

    End If

    i += 1

    yValues.Add(buffer(i))

     

    End Sub

     

    yValues contains what to be plotted on the graph..

     

     

    Wednesday, March 28, 2007 12:25 PM

Answers

  • There has just been a similar question about the DataReceived event in the .NET base Class forum - see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1379397&SiteID=1

     

    Personally, I think that the DataReceived event fires each time the number of received bytes reaches the threshold limit, that it, it fires when the a number of characters corresponding to the threshold level are received, but then it doesn't fire again before the buffer has been below the threshold level and the threshold level is reached again. At least this is how the interrupt system in the UART works, and the UART is the first one to discover that a byte has been received. The default threshold level is 1 byte, so I think that the DataReceived event will fire when the first character is received and then it will not fire again before the buffer has been empty and a new character is received.

     

    You use the DataReceived event to invoke the UpdateTextBox routine, but this will only put this routine on the message queue so it may easily take 50 mS before the routine gets up and running. Besides, you make a 1 sec. delay in this routine, which makes the situation even worse. Because you use Invoke instead of BeginInvoke your event handler OnDataReceived will not be able to handle new events before this 1 sec delay has gone and this is probably the problem. When you empty the buffer the DataReceived event may fire again, but because of the delay, it cannot activate the same instance of OnDataReceived again - it is still busy. Therefore, the system graps a new thread from the thread pool and uses this for yet another instance of OnDataReceived until the thread pool gets empty (see nobugz answer in the above forum thread). When there are no more threads in the pool, new events will be put in queue for a thread.

     

    This is simply not the way to do it. Try to empty the receive buffer in the event handler and send the telegram to UpdateTextBox by means of BeginInvoke. Do not use Invoke as this will block OnDataReceived until the display operation is finished and if DataReceived fires during this period you probably force the system to use a new thread for a new instance of OnDataReceived.

     

    The description of our serial port program has just been considerably updated and now describes multithreading, events, delegates, invokation etc. in a language, which even beginners should have a chance of understanding.

     

    The URL is: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm

     

     It is extremely hard to get precise under-the-hood informations about .NET (especially because I am black-listed by some of the big guys in this forum). The information is believed to be true, but if anybody discover an error in the description please let me know.

     

    Wednesday, March 28, 2007 2:05 PM

All replies

  • There has just been a similar question about the DataReceived event in the .NET base Class forum - see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1379397&SiteID=1

     

    Personally, I think that the DataReceived event fires each time the number of received bytes reaches the threshold limit, that it, it fires when the a number of characters corresponding to the threshold level are received, but then it doesn't fire again before the buffer has been below the threshold level and the threshold level is reached again. At least this is how the interrupt system in the UART works, and the UART is the first one to discover that a byte has been received. The default threshold level is 1 byte, so I think that the DataReceived event will fire when the first character is received and then it will not fire again before the buffer has been empty and a new character is received.

     

    You use the DataReceived event to invoke the UpdateTextBox routine, but this will only put this routine on the message queue so it may easily take 50 mS before the routine gets up and running. Besides, you make a 1 sec. delay in this routine, which makes the situation even worse. Because you use Invoke instead of BeginInvoke your event handler OnDataReceived will not be able to handle new events before this 1 sec delay has gone and this is probably the problem. When you empty the buffer the DataReceived event may fire again, but because of the delay, it cannot activate the same instance of OnDataReceived again - it is still busy. Therefore, the system graps a new thread from the thread pool and uses this for yet another instance of OnDataReceived until the thread pool gets empty (see nobugz answer in the above forum thread). When there are no more threads in the pool, new events will be put in queue for a thread.

     

    This is simply not the way to do it. Try to empty the receive buffer in the event handler and send the telegram to UpdateTextBox by means of BeginInvoke. Do not use Invoke as this will block OnDataReceived until the display operation is finished and if DataReceived fires during this period you probably force the system to use a new thread for a new instance of OnDataReceived.

     

    The description of our serial port program has just been considerably updated and now describes multithreading, events, delegates, invokation etc. in a language, which even beginners should have a chance of understanding.

     

    The URL is: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm

     

     It is extremely hard to get precise under-the-hood informations about .NET (especially because I am black-listed by some of the big guys in this forum). The information is believed to be true, but if anybody discover an error in the description please let me know.

     

    Wednesday, March 28, 2007 2:05 PM
  •  

    Thanks for the help..I'll try to understand the whole thing more clearly...I made changes to the datareceived event in such a way, but havent tested to see how it works..

     

     

    Private Sub OnDataReceived(ByVal sender As Object, _

    ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _

    Handles SerialPort1.DataReceived

    Try

    buffer.Clear()

    TextBox1.BeginInvoke(New myDelegate(AddressOf UpdateTextBox), New Object() {})

    Catch ex As Exception

    MsgBox(ex.Message)

    End Try

    End Sub

    Wednesday, March 28, 2007 3:33 PM
  • Sorry, I didn't ment that you should clear the buffer. I ment that you should put your "While SerialPort1.BytesToRead > 0" loop in OnDataReceived and let it empty the buffer.
    Wednesday, March 28, 2007 4:13 PM
  •  I have

     

    While SerialPort1.BytesToRead > 0

    strInData &= Chr(SerialPort1.ReadByte())

    End While

     

    Inside the data received event, and the loop can then be exluded from the update textbox sub?

    Wednesday, March 28, 2007 4:26 PM
  • As OnDataReceived uses a thread pool thread you need a BeginInvoke statement to invoke UpdateTextBox on the UI thread, but read the above link. It describes everything with examples.
    Wednesday, March 28, 2007 4:34 PM