locked
serialport.datareceived event RRS feed

  • Question

  • Hi all,

    Coming to you again with a question on serial port (I am still a beginner)!
    here is an extract of the code :

    Private Sub serialport_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    Try
    Me.BeginInvoke(New StringSubPointer(AddressOf Display), SerialPort1.ReadExisting)
    Catch ex As Exception
    MsgBox(
    "Form_RS232 : Serialport_data_Received" & Environment.NewLine & ex.Message, MsgBoxStyle.Information) : End Try
    End Sub


    Private
    Sub Display(ByVal Buffer As String)
    Try
    Buffer = UCase(Buffer)
    If Buffer.Contains("ERROR") Then : MsgBox("Error") : End If
    If Buffer.StartsWith("S") Then
    Form_RFM_Registers.ComboBox_SEB.Text = Mid(Buffer, 5, 1) : Form_LFR_registers.ComboBox_SEB.Text = Mid(Buffer, 5, 1) End If
    If Form_Receive_LF_Data.RadioButton_LF_Reception_Status.Checked = True Then
    Form_Receive_LF_Data.TextBox_ReceivedFrame.AppendText(Buffer & Environment.NewLine) : End If

    Catch ex As Exception : MsgBox("Form_RS232 : Display" & Environment.NewLine & ex.Message, MsgBoxStyle.Information) : End Try

    End Sub


    My problem is that my buffer is always empty but connecting an hyper terminal window, my product answered correctly.
    Why is that ?
    What did I do wrong ?

    thanks for your support,

    Regards,

    Sof.

    Tuesday, January 6, 2009 2:19 PM

Answers

  • my serial port routines

    http://www.vbforums.com/showthread.php?t=530790
    Tuesday, January 6, 2009 6:12 PM
  • For continuous high-speed operation it may be better to use Invoke instead of BeginInvoke as I wrote previously.

    There are pro's and contra's with BeginInvoke and Invoke.

    If you have a simple ASCII protocol where each line is terminated with a given character, it is natural to use ReadLine or ReadTo. However, you should only do that with BeginInvoke since Invoke would lock the UI thread and the thread pool thread used for the DataReceived event together. This would block the UI thread until the entire telegram is received. If you have no speed problems, It is usually most appropriate to collect an entire telegram in the eventhandler for the DataReceived event and then marshal the result to the UI thread. This gives the maximum utilization of the multithreading since the two threads works in parallel.

    If you use a binary protocol at very high speed, use of BeginInvoke may overload the message queue if you just empty the receive buffer for each DataReceived event. If you instead use Invoke to "wake" the UI thread - for example by means of the MethodInvoker delegate (you have no data to transfer), you lock the UI thread and the eventhandler together. Because of a lock in SerialPort, the DataReceived event will not fire again until you have emtied the receive buffer (ReadExisting or a BytesToRead loop) from the UI thread. Note that to avoid blocking the UI thread, you should not use any blocking calls like ReadLine or ReadTo - just empty the receive buffer and add the data to any previous data and then return so that the DataReceived event can fire again. With this method, you cannot overload the system as long as the receive buffer (in the driver) is big enough. The number of messages is very limited, and if you use the standard delegate MethodInvoker, which cuts some corners internal in .Net, there is very little overhead.

    The disadvantage of Invoke is the deadlock situation, which may occur when you close the port. As described in the tutorial, this makes it necessary to close the port from another thread. It is not a big deal if you for examble use delegate.Invoke to grab a thread from the thread pool for that purpose, but it is of course more complicated than just to close the port as you can do with BeginInvoke.

    The reason why my tutorial has grown to the size it has is that it is extremely important to understand exactly what goes on internal in .Net and SerialPort if you want to utilize the system to the limits. It is my true belief that for your application, a proper Invoke solution is much faster than any busy loop and uses much less resourses. Just imagine if everything in .Net including keyboard and mouse reads was done by means of busy loops instead of events and interrupts. It would kill even the fastest computer.
    Everything should be made as simple as possible, but not simpler.
    Saturday, January 10, 2009 8:33 AM

All replies

  • my serial port routines

    http://www.vbforums.com/showthread.php?t=530790
    Tuesday, January 6, 2009 6:12 PM
  • I think that your problem is that you have not defined a transmission protocol. You just call ReadExisting, but this call may return only one character in the buffer. You need to collect an entier telegram before you marshal it to the UI thread by means of the BeginInvoke call. Since you are using an ASCII protocol, I presume that all telegrams are terminated with a given character like LF or CR. If this is the case, just replace ReadExisting with ReadLine and set the NewLine character to the termination character if it is not LF (default).

    I have writen a very big SerialPort tutorial, which also includes a small sample program, which is easy to modify to your needs. The URL is: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm

     


    Everything should be made as simple as possible, but not simpler.
    Wednesday, January 7, 2009 8:27 AM
  • dbasnett said:

    my serial port routines

    http://www.vbforums.com/showthread.php?t=530790


    This is the best way to do it; best solution for serial port programming by far. Also made it a link :)

    Stephen J Whiteley
    Wednesday, January 7, 2009 2:23 PM
  • That depends on whether you prefer an event driven solutions or a busy loop, but I know that you (SJW) and I will never agree on that.

    The solution with BeginInvoke or Invoke is 100% event driven like the rest of .Net.

    The solution from dbasnett is based on the following busy loop:

        Do While SerialPort1.IsOpen OrElse zSPNumBytesInQ() > 0

            .......

            Application.DoEvents() 'needed so event handler(timer) can fire
        Loop

    which transfers the data from the receive buffer to the UI thread.

    However, to call Application.DoEvents() in a loop like this is just to do what the message pump on the UI thread is already doing in a better way.

    Besides, the comment "needed so event handler(timer) can fire" is misleading. Application.DoEvents() empties the message queue. It has nothing to do with events, which are just subroutines called by means of the invocation list of a delegate, and especially it has nothing to do with the DataReceived event, which is raised on a different thread (a thread pool thread). You cannot block an event, but you can block the thread, which raise (execute) the event or you can remove the eventhandler from the invocation list (RemoveHandler). In the above case, ApplicationDoEvents() is nessesary to avoid that the busy loop is blocking the UI thread.

    I agree that BeginInvoke is slow due to the big overhead with TME objects etc., but if you empty the receive buffer before every call there is usually no problems. I use BeginInvoke without problems up to 921.6 Kbit/s with a 16C950 UART. However, for continuous high speed operation, it is better to use Invoke as this will block the event handler for the DataReceived event until the UI thread is ready, so if the receive buffer is big enough it is very difficult to overload the system. Read chapter "SerialPort Receive" of my tutorial where this is described in details.

    -------

    Please SJW. I know that you don't like me, but shouldn't we keep it straight technical. Everytime I make a comment, you say that it is a bad solution no matter what. You have even tried to convince Cheyenne and this society that it is possible to burn of a motherboard by means of a current of 0.3 mA - at least 30 times less than the protection diodes of almost every IC can handle - just because I suggested a hardware solution for controlling a teddy bear by means of the serial port. Last time it was about Sleep(), but if you don't like Sleep() why havn't you commented this little detail from your "best solution for serial port programming by far":

        Do While InDataReceivedEv 'in case we were already in data rcvd handler
            Thread.Sleep(10)
        Loop

    Have you even read it before you claim that it is far better than my much, much simpler, event driven solution?
     


    Everything should be made as simple as possible, but not simpler.
    Thursday, January 8, 2009 7:43 AM
  • Hi,

    Thanks for your answer.
    I first read your tutorial to write my initial code.
    I also tried with REadline function and results are the same : no answer at all.
    I do not understand why.
    I know that something is sent as I saw answers with Hyper Terminal window.
    Did I misunderstand something in your tutorial ?

    regards,
    Sof.
    Thursday, January 8, 2009 1:37 PM
  • Hi all,

    I also tried my product with SJW routines and it works.
    but now, how shall I import this routines to my code ?

    Regards,

    Sof.
    Thursday, January 8, 2009 2:26 PM
  • i feel honored carsten that you actually downloaded my code.  the loop with the thread sleep occurs in the close for the serial port and is there to co-ordinate being in the receive handler and wanting to close the port.  i'll work on removing it.

    i'll also try to remember to change the comment on the .DoEvents.



    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Thursday, January 8, 2009 4:03 PM
  • dbasnett

    Of course I downloaded your code. I have only one intention with my tutorial: To present the best ways to use the serial port. If somebody has done something more clever than I, I will change my code and/or tutorial immediately. However, I must admit that I don't like busy loops as they lower the total efficiency of the computer (more programs may run simultaneously), so I will stick to BeginInvoke and Invoke.

    Personally, I have nothing against Sleep() if you just use it in a way where it does not destroy the user experience - for example a short Sleep() after an Application.DoEvents() statement. I do this myself when I change the COM port to allow some time for the background thread to close down before it is reopened (flaw in SerialPort). It is just SJW, Peter Ritchie, ReneeC and others, who have the fanatic rule newer, ever to use Sleep() - no matter if it is actually the most proper solution for that particular problem.


    sof31

    Try to download the program from my tutorial and see if it works. It is a very small .exe file, which does not install anything on your computer. If it works, just modify it to your needs and you have an event driven solution.

    Everything should be made as simple as possible, but not simpler.
    Friday, January 9, 2009 8:37 AM
  • if the serial port use is limited with low data rates, then begininvoke is fine, along with several other methods.

    if the serial port is central to the application, and the bandwidth is high, then why not a loop?  if properly coded it does not have to be a resource hog unless it needs to be. 

    i am working on adding a begin invoke to the examples to be fair.

    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Friday, January 9, 2009 5:27 PM
  • For continuous high-speed operation it may be better to use Invoke instead of BeginInvoke as I wrote previously.

    There are pro's and contra's with BeginInvoke and Invoke.

    If you have a simple ASCII protocol where each line is terminated with a given character, it is natural to use ReadLine or ReadTo. However, you should only do that with BeginInvoke since Invoke would lock the UI thread and the thread pool thread used for the DataReceived event together. This would block the UI thread until the entire telegram is received. If you have no speed problems, It is usually most appropriate to collect an entire telegram in the eventhandler for the DataReceived event and then marshal the result to the UI thread. This gives the maximum utilization of the multithreading since the two threads works in parallel.

    If you use a binary protocol at very high speed, use of BeginInvoke may overload the message queue if you just empty the receive buffer for each DataReceived event. If you instead use Invoke to "wake" the UI thread - for example by means of the MethodInvoker delegate (you have no data to transfer), you lock the UI thread and the eventhandler together. Because of a lock in SerialPort, the DataReceived event will not fire again until you have emtied the receive buffer (ReadExisting or a BytesToRead loop) from the UI thread. Note that to avoid blocking the UI thread, you should not use any blocking calls like ReadLine or ReadTo - just empty the receive buffer and add the data to any previous data and then return so that the DataReceived event can fire again. With this method, you cannot overload the system as long as the receive buffer (in the driver) is big enough. The number of messages is very limited, and if you use the standard delegate MethodInvoker, which cuts some corners internal in .Net, there is very little overhead.

    The disadvantage of Invoke is the deadlock situation, which may occur when you close the port. As described in the tutorial, this makes it necessary to close the port from another thread. It is not a big deal if you for examble use delegate.Invoke to grab a thread from the thread pool for that purpose, but it is of course more complicated than just to close the port as you can do with BeginInvoke.

    The reason why my tutorial has grown to the size it has is that it is extremely important to understand exactly what goes on internal in .Net and SerialPort if you want to utilize the system to the limits. It is my true belief that for your application, a proper Invoke solution is much faster than any busy loop and uses much less resourses. Just imagine if everything in .Net including keyboard and mouse reads was done by means of busy loops instead of events and interrupts. It would kill even the fastest computer.
    Everything should be made as simple as possible, but not simpler.
    Saturday, January 10, 2009 8:33 AM
  • Hello Carsten Kanstrup,

    I tried with your program from your tutorial, connect my product, configure the port (COM1, 38400 b/s).
    I then sent some data and expected answers.
    The only answer I got was TX.
    Is it normal ?

    Thanks

    Regards,

    Sof31.
    Tuesday, January 13, 2009 10:50 AM
  • No, not if the hardware is working and you are using .Net 2.0 (or maybe .Net 3.5 SP1), but .Net 3.5 RTM does not work with SerialPort. Which version do you use?

    TX is just put into the receive buffer each time some data is send to make it easier to separate the received telegrams (TX is a shortening for transmitter).

    The receiver is completely asynchronous and just displays everything, which is received, in hexadecimal.
    Everything should be made as simple as possible, but not simpler.
    Tuesday, January 13, 2009 1:27 PM
  • I am using the Visual Basic 2008 EXpress Edition. What version of .NET is it ?
    Or how can I determine it ?

    Thanks for your support,

    Sof31
    Tuesday, January 13, 2009 4:09 PM
  •         Debug.WriteLine(Environment.Version.Major) 
            Debug.WriteLine(Environment.Version.MajorRevision) 
            Debug.WriteLine(Environment.Version.Minor) 
            Debug.WriteLine(Environment.Version.Build) 
            Debug.WriteLine(Environment.Version.MinorRevision) 
            Debug.WriteLine(Environment.Version.Revision) 
            'or 
            Debug.WriteLine(Environment.Version) 
     


    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Tuesday, January 13, 2009 9:54 PM
  • Sof31
     
    VS2008 by default use .Net 3.5. Unless you have updated to SP1, that version is useless for SerialPort applications. However, when you build your code, you can just select .Net 2.0. Try this.
    Everything should be made as simple as possible, but not simpler.
    Tuesday, January 13, 2009 10:43 PM
  • i have not had a problem with SP1
    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Wednesday, January 14, 2009 12:01 AM
  • That's what I mean. Sorry that it may be possible to misunderstand my post.

    Just to clarify: 2.0 Works, 3.5 RTM is useless, but I have heard that 3.5 SP1 works. I have not tested my program with that version (3.5 SP1) myself. Due to the many problems with 3.5 RTM, which may even require a reinstall of Windows, I have not been in a hurry to download VS2008, but I guess it should be safe now that we have SP1.
    Everything should be made as simple as possible, but not simpler.
    Wednesday, January 14, 2009 8:28 AM
  • Here are the information :
    Version 2.0.0
    Build 50727
    Rev Minor : 1433
    And it still not work.

    I know my product is responding as connecting an HyperTerminal Window, it is working.

    It is also working with SJW code but I did not know how to implement it correctly in my code.

    Thanks again and again for your support,

    Sof31.
    Thursday, January 15, 2009 1:28 PM
  • That is really a mystery. I have never seen problems with .Net 2.0 and there are approximately 1000 downloads of that program per month. On the other hand, it looks exactly like the "usual" .net 3.5 RTM problems, where the DataReceived event does not fire.  Hyperterminal is not using .Net and the so-called SJW code is not event driven but based on a busy loop. This is probably the reason why these programs works.

    It seems like a DataReceived problem. Try to place a breakpoint in the beginning of the eventhandler for the DataReceived event and see if it fires. If you have two serial ports on the computer, it could also be helpful to connect the two ports and use hyperterminal for one of these so that you can find out if the problem is in the receiver or in the transmitter. Of course an oscilloscope would also be helpful for this purpose.

    PS. Please double check that you don't use .Net 3.5 RTM.

    Everything should be made as simple as possible, but not simpler.
    Thursday, January 15, 2009 4:53 PM
  • the sjw routines are mine.  they do in fact use a busy loop, but it is only used to empty a queue that is filled with the firing of the datareceived event.  i have no problems, that i am aware of, using 3.5 SP1.
    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Friday, January 16, 2009 12:54 AM
  • @carsten - i am trying to double check differences between 2.0, 3.5, 3.5SP1.  how do you force an application to use a particular one?  
    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Friday, January 16, 2009 3:01 PM
  • You can select the .Net version in Advanced Compilation options.
    Everything should be made as simple as possible, but not simpler.
    Saturday, January 17, 2009 4:09 PM
  • @carsten - if you have a chance will you take a look at this

    http://www.vbforums.com/showthread.php?p=3427555#post3427555

    i am getting blue screens of death after it runs for a few minutes.  it may be my modem/driver.




    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Thursday, January 22, 2009 8:37 PM
  • Sorry, but I am still using VS2005, so I cannot open your solution file (generated in VS2008).

    However, since your system runs for a few minutes before it crash, there is probably no doubt that you eat up some resource on your computer. Check for reentrance, which may lead to stack overflow. I don't think it is your driver.


    Everything should be made as simple as possible, but not simpler.
    Friday, January 23, 2009 8:27 AM
  • there is no re-entrance.
    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Friday, January 23, 2009 2:07 PM
  • Re-entrance is just one of many possibilities for resource eating. Another possibility is overrun in the driver buffer, receiver array etc. Try to see if your application crashes if there is no input data (remove the plug if necessary). If it does not do that, try to reduce the speed to the half and see if it runs better. If it for example takes the double amount of time before it crashed at the half speed or does not crash at all, it is probably an overrun problem.
    Everything should be made as simple as possible, but not simpler.
    Friday, January 23, 2009 2:48 PM
  • found a newer driver and it has ran for over 2 hours.  previous record was 10 minutes.
    Looking for work - Zip 65101 http://www.vbforums.com/showthread.php?t=552774
    Friday, January 23, 2009 4:55 PM