locked
How to use the DataReceived event which was defined in a module.

    Question

  •  

    Hello, everyont:

          I met a problem that I didn't thought it was a trouble one,but in fact it troubled me for almost a hole day:

    I defined a SerialPort Class in a module. Please look at the following codes:

     

         Module Module1

         Dim rdata() as byte

         Public WithEvents comport As New System.IO.Port.SerialPort("COM1",9600,IO.Ports.Parity.None,8,IO.Ports.StopBits.One)    ''''''''''''''''''the SerialPort Class

        

         Sub SendData(Byval data as byte())          ''''''''''''''''''''''write bytes

         comport.DiscardInBuffer()

         comport.Write(data,0,data.Length)

         End Sub

     

         Public Sub ReceiveData(Byval sender As Object,Byval e as System.IO.Ports.SerialDataReceivedEventArgs) Handles comport.DataReceived                          '''''''''''''''''''''''''the DataReceived event

         comport.ReceivedBytesThreshold = 3

         comport.read(rdata,0,3)

         End Sub

         End Module

     

    Now the problem comes:when I wanted to read the serialport in my Form1 by using the method:comport.Read(array,0,array.Length) ,it reads correctly; but when I changed it to the ReceiveData() event,the program was dead.

     

    My Problem:How can I use the DataReceived event to get the bytes from my serialport COM1 ?

                       The most important thing is that I have many forms which are all need to receive data from COM1.

     

    Tuesday, October 16, 2007 5:10 AM

Answers

  • It is important to understand that DataReceived does not necessary fire for each byte. All data, which are receiced by the UART, are stored in the receive buffer in the UART driver. This buffer has a minimum size of 4096 bytes. This means that at 19200 bit/s it will take 2.1 seconds to fill this buffer (remember start and stop bit), so DataReceived must just not be slower than that. I am using SerialPort up to 921.6 kbit/s myself with a 16C950 UART.

     

    Your so-called data loss may be coursed by your wrong setting of the receiver threshold in combination with a misunderstanding of how Read(buffer, offset, count) works. Qoute from chapter "Read Method" in my description:

     

    "Because the help files does not describe how Read actually works, it is logical to assume that the method:

       BytesReceived = YourCOMPort.Read(buffer, offset, count)
       

    is a blocking method, which does not return before "count" number of bytes are received. It is not!. If there are bytes available on the serial port, Read returns up to "count" bytes, but will not block (wait) for the remaining bytes. If there are no bytes available on the serial port, Read will block until at least one byte is available on the port, up until the ReadTimeout milliseconds have elapsed, at which time a TimeoutException will be thrown."

     

    Because your threshold setting does not work, Read() may return when only one byte is received. The next time you call Read(), you will get the next bytes. I think that this is your "data loss".

     

    I will try to find the time to make a test program where I put SerialPort in a module to see what is necessary to change. In the meantime it is perhaps a good idea for you to read more of my description. In this description I have tried to gather all my present knowledge about SerialPort plus an answer to all the questions I have been asked about SerialPort.

    Tuesday, October 23, 2007 5:02 AM
  • Of course  Read() works the way I have described it - it is actually a quote from Kim Hamilton. If you use Read(), you should always check the actual number of received bytes (the returned value).

     

    As I wrote before (see my above posts), the receiver threshold is checked in the thread pool thread BEFORE it calls your eventhandler for the DataReceived event, so it doesn't work to specify the threshold level in the eventhandler as you do! You need to specify the threshold level outside the eventhandler - typically the same place where you set other parameters for SerialPort like ReadBufferSize, BaudRate, Parity etc.

    Tuesday, October 23, 2007 6:43 AM

All replies

  • VickyLoe,

     

    According to your question on using System.IO.Ports.SerialPort.DataReceived event to get the bytes, I would like to provide you the suggestions as follows:

     

    1. Serial Communications

     

    Usually when I do serial communications, I set up an event handler for the comm port's data received event.  Then I get the data whenever it comes in and usually will resend my output until I get a response or send it too many times, at which point I timeout, close the port, and raise a timeout event.  The code could look something like this:

     

    Code Snippet

        Dim WithEvents port As New SerialPort("COM1")

        Private Sub SendData(ByVal data As String)

            If Not port.IsOpen Then port.Open()

            port.Write(data)

        End Sub

     

        Private Sub port_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles port.DataReceived

            If e.EventType <> SerialData.Chars Then Exit Sub

     

            Dim inData As String = port.ReadExisting

            'process incoming data here

        End Sub

     

     

    2. How does SerialPort handle DataReceived

     

    Please see the answer by nobugz: The call to your DataReceived event may well be delayed, depending on system load.  By the time it runs and you use the BytesToRead property, you may be seeing bytes received for which SerialPort has already queued new thread pool worker items.  That means that you may well see zero bytes (because they were read by the previous invocation of the event).  Also, the the number of bytes may change right after you call it in the middle of your DataReceived event's execution when another byte was received a few microseconds later.

     

    3. Example from code project: Communication on a serial port in NET 2.0

     

    Code Block

    Imports System

    Imports System.IO.Ports

     

    Public Class Form1

        Dim WithEvents port As SerialPort = New _

         System.IO.Ports.SerialPort("COM1", 9600, Parity.None, 8, StopBits.One)

     

        Private Sub Form1_Load(ByVal sender As Object, ByVal e As _

                               System.EventArgs) Handles Me.Load

            CheckForIllegalCrossThreadCalls = False

            If port.IsOpen = False Then port.Open()

        End Sub

     

        Private Sub port_DataReceived(ByVal sender As Object, ByVal e As _

           System.IO.Ports.SerialDataReceivedEventArgs) Handles port.DataReceived

            TextBox1.Text = (port.ReadTo("!%"))

            If port.ReadExisting.Length = 0 Then

                ListBox1.Items.Add(TextBox1.Text)

                TextBox1.Text = ""

            End If

        End Sub

    End Class

     

     

    Hope that can help you.
    Friday, October 19, 2007 6:20 AM
  • Bruno Yu - MSFT:

    Thanks very much for your kind reply,but I still have a question about the SerialPort control: I'd like to know if I set the SerialPort.ReceivedBytesThreshold = 3, and I do a SerialPort.read(rdata,0,3) operation, resume that there are still many bytes data in the InBuffer, does it means after the SerialPort.read(rdata,0,3) operation, system will clear the three bytes' space whice I just read,and the rest bytes will step up to the space,then fire another DataReceived Event???

     

    Is my understanding right?

     

    if it is right, I have another question: How fast can the DataReceived Event be fired? 2mS a time? or 1mS a time?

     

    In my application, the DataReceived Event need to fire 1.5mS a time,but I found that it can only fire at a frequency of 2mS a time! Where is the Problem????

     

    Hoping for your kind reply again!

     

    yours

    VickyJoe

    Monday, October 22, 2007 4:49 AM
  • Bruno Yu and VickyJoe

     

    I don't think that the answer from nobugz, which Bruno Yu quote, is correct (#2).

     

    The short story:

    DataReceived fires when the number of received data exceeds the threshold level. Any further events from SerialPort are disabled (by means of a lock on the underlying SerialStream object) until the eventhandler returns, so there will never be more instances running at the same time. Therefore, you will never receive zero bytes (bytes received by a previous instance).

     

    The long story:

    Read at least the chapter "Serial Port Events" of this description: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm . This describes the exact receive sequence according to Kim Hamilton, who is the one, who has programmed SerialPort.

     

    VickyJoe

     

    You cannot expect the DataReceived event to fire faster than 50 mS! The event is handled on a thread pool thread with lower priority than the UI thread. Why do you need it so fast? Anything above 115.2 kbit/s requires a better UART, but below that speed, you will have no problems if you do it right.
     
    I know that when you put SerialPort in its own class (or into a module) you need to do something slightly different to make the DataReceived event to fire. I think it has something to do with making a new SerialPortEventHandler delegate in the class, but I have never used SerialPort that way so I cannot give you a precise guidance, but maybe Bruno Yu know what to change when you put some code in a module.

    Monday, October 22, 2007 6:59 AM
  • VickyJoe

     

    As the threshold level is checked in the thread pool thread, which is used for the DataReceived event, before the event handler is called (see long story) it is not a good idea to set the threshold level in the eventhandler. Do it outside.

     

    As I wrote, I have never used SerialPort in a module, but have you tried something like this:

     

       ... Handles Me.comport.DataReceived

     

    Note the extra "Me."

    Monday, October 22, 2007 10:16 AM
  • Thanks for your reply, according to your short story and Kim's long story, I have a new understanding of the SerialPort Class.

    But I still have a question about the DataReceived Event's fire-time:I use a baudrate of 19200,and it is much less than 115200bit/s,why can't it received all the data I send to it? I'm sure that the lost data are still in the InPutBuffer,because when I send some data to it again, it received the data which I sent before. What a strange phenomenon!!!

     

    So another problem comes:whether the SerialPort clear the space it just read when it fired again???

     

    Although I use the loop require method to solve the problems above,but I still want to know why the event cannot fire fast enough? How does the SerialPort Control run when it reads the InPutBuffer time and time again?

     

    Thanks you all very much,sincerely! 

    Tuesday, October 23, 2007 2:42 AM
  • Carsten Kanstrup :

               I think it is my fault to have not explaint my problem clearly:

    I can write the DataReceived Event in a module in the form of : 

    ..........Handles comport.DataReceived (suppose the name of the SerialPort Class is comport)

    but my problem is that when I put it in a module , how can I call it to be active?

     

    Campared with define a DataReceived Event in a module,suppose I use a SerialPort Control in the main form whose name is Form1, I can define the Event in the code space of Form1 like:

    ......Handles SerialPort1.DataRecieved  (suppose the name of the SerialPort Control is SerialPort1)

    And this time it can fire correctly when it receives enough bytes from COMx according to my setting, because it is always active.

     

    So, what I asked was how can I make a DataReceived Event to be active if I define it in a module?

     

    Thanks for your attention!

    Tuesday, October 23, 2007 3:01 AM
  • It is important to understand that DataReceived does not necessary fire for each byte. All data, which are receiced by the UART, are stored in the receive buffer in the UART driver. This buffer has a minimum size of 4096 bytes. This means that at 19200 bit/s it will take 2.1 seconds to fill this buffer (remember start and stop bit), so DataReceived must just not be slower than that. I am using SerialPort up to 921.6 kbit/s myself with a 16C950 UART.

     

    Your so-called data loss may be coursed by your wrong setting of the receiver threshold in combination with a misunderstanding of how Read(buffer, offset, count) works. Qoute from chapter "Read Method" in my description:

     

    "Because the help files does not describe how Read actually works, it is logical to assume that the method:

       BytesReceived = YourCOMPort.Read(buffer, offset, count)
       

    is a blocking method, which does not return before "count" number of bytes are received. It is not!. If there are bytes available on the serial port, Read returns up to "count" bytes, but will not block (wait) for the remaining bytes. If there are no bytes available on the serial port, Read will block until at least one byte is available on the port, up until the ReadTimeout milliseconds have elapsed, at which time a TimeoutException will be thrown."

     

    Because your threshold setting does not work, Read() may return when only one byte is received. The next time you call Read(), you will get the next bytes. I think that this is your "data loss".

     

    I will try to find the time to make a test program where I put SerialPort in a module to see what is necessary to change. In the meantime it is perhaps a good idea for you to read more of my description. In this description I have tried to gather all my present knowledge about SerialPort plus an answer to all the questions I have been asked about SerialPort.

    Tuesday, October 23, 2007 5:02 AM
  •  

    So!!!That is the problem!

    Why did it "will not block (wait) for the remaining bytes"??

    It is define to read the "count" number of bytes,and further more, I had already set the ReceiveThreshold to "count", Why did it still be fired when there were one or two or three bytes available in the inputbuffer if I set the ReceiveThreshold equal to 6??????

    Is the Read method's problem? Or it just works in the way you just posted above??

    Tuesday, October 23, 2007 6:11 AM
  • Of course  Read() works the way I have described it - it is actually a quote from Kim Hamilton. If you use Read(), you should always check the actual number of received bytes (the returned value).

     

    As I wrote before (see my above posts), the receiver threshold is checked in the thread pool thread BEFORE it calls your eventhandler for the DataReceived event, so it doesn't work to specify the threshold level in the eventhandler as you do! You need to specify the threshold level outside the eventhandler - typically the same place where you set other parameters for SerialPort like ReadBufferSize, BaudRate, Parity etc.

    Tuesday, October 23, 2007 6:43 AM
  •  

    OK,I will try it in the way you told.Thanks very much! But actually I specify the Threshold in the right place as you said. I think there still someplace I didn't do them right,I will checkout them and reply this post later.

     

    At last,I feel so happy that you helped me so much,Thank you!

    Wednesday, October 24, 2007 3:03 AM
  • Sorry, but unless you have changed your code, you set the threshold in the eventhandler.

     

        Public Sub ReceiveData(Byval sender As Object,Byval e as System.IO.Ports.SerialDataReceivedEventArgs) Handles comport.DataReceived                          '''''''''''''''''''''''''the DataReceived event

         comport.ReceivedBytesThreshold = 3    ' Here you set the threshold in the eventhandler - this is wrong!

         comport.read(rdata,0,3)

         End Sub

         End Module

     

    Wednesday, October 24, 2007 6:14 AM
  •  

    Oh,yes,I forgot to say that comport.ReceivedBytesThreshold = 3 is an example of my code,but actually I define it with the PortName,Baudrate,StopBits,Parity...and so on,which is outside the DataReceived Event.In Fact they are initialized in the Form1_Load event.I'm so sorry about the mistake which made you misunderstand.

     

    But even I do so, the DataReceived Event can be fired when there were not 3 bytes in the receivebuffer.I use a Loop to do the DataReceive instead of the DataReceived Event,and that works smoothly.

     

    Thanks for your kind help,sincerely!!! 

    Wednesday, October 24, 2007 6:25 AM
  • I am not sure if this case is solved?

     

    Have you solved the module problem and made a program, which works?

    Wednesday, October 24, 2007 7:37 AM