none
How to print out text from Serialport DataReceived Event RRS feed

  • Question

  • Hi! I'm trying to print out texts onto a richTextBox from the SerialPort.ReceivedDataEventHandler event. The problem is that the texts don't seem to be appearing onto the richtextbox. I have the functions use thread as. Here's the code.

    static SerialPort Arduino;
            static string DistanceToTravel;
            static bool _continue = true;
            //object demand;
            //string Com_number = "COM5";
    
                /*Arduino.BaudRate = 9600;
    
                Arduino.Parity = Parity.None;
                Arduino.DataBits = 8;
                Arduino.StopBits = StopBits.One;
    
                Arduino.ReadTimeout = 5000;
                Arduino.WriteTimeout = 5000;
                richTextBox1.Multiline = true;*/
    
            //ThreadStart thd = new ThreadStart(SetSerialParameters(COMPortSettings.arduinoCOM));
            //Thread thread = new Thread(thd);
    
            public Main()
            {
                InitializeComponent();
            }
    
            private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort comm = (SerialPort)sender;
                string incoming_Data = comm.ReadExisting();
                //richTextBox1.AppendText("Received Data: \n");
                //richTextBox1.AppendText(incoming_Data + "\n");
                //Console.WriteLine(incoming_Data + "\n");
                this.BeginInvoke((MethodInvoker)delegate()
                {
                        richTextBox1.AppendText(incoming_Data + "\n");
                });
            }
    
            public void SetSerialParameters(string ComPort)
            {
                //richTextBox1.AppendText(_arduinoCom);
                try
                {
                    Arduino = new SerialPort(ComPort);
                }
                catch (ArgumentNullException ex)
                {
                    richTextBox1.AppendText(ex.Message);
                }
                //richTextBox1.AppendText(ComPort);
    
                Arduino.BaudRate = 9600;
    
                Arduino.Parity = Parity.None;
                Arduino.DataBits = 8;
                Arduino.StopBits = StopBits.One;
    
                Arduino.ReadTimeout = 5000;
                Arduino.WriteTimeout = 5000;
                richTextBox1.Multiline = true;
            }
    
            public void ReadSerialReceivedData()
            {
                 while (_continue)
                 {
                     this.Invoke((MethodInvoker)delegate()
                     {
                         try
                         {
                             richTextBox1.AppendText(Arduino.ReadLine());
                         }
                         catch (TimeoutException ex)
                         {
                             richTextBox1.AppendText(ex.Message + "\n");
                             _continue = false;
                         }
                     });
                     Thread.Sleep(1000);
                 }
                 _continue = true;
             }
    
             void ArduinoMoveToDemandPosition(string ComPort, string Demand)
             {
                 try
                 {
                     Arduino = new SerialPort(ComPort);
                 }
                 catch (ArgumentNullException ex)
                 {
                     richTextBox1.AppendText(ex.Message);
                 }
                 //richTextBox1.AppendText(ComPort);
    
                 Arduino.BaudRate = 9600;
    
                 Arduino.Parity = Parity.None;
                 Arduino.DataBits = 8;
                 Arduino.StopBits = StopBits.One;
    
                 //Arduino.ReadTimeout = 5000;
                 //Arduino.WriteTimeout = 5000;
                 richTextBox1.Multiline = true;
                 
                 //richTextBox1.AppendText(DistanceToTravel);
                 //richTextBox1.AppendText("Please wait until all the lines appear.");
                 SetSerialParameters(COMPortSettings.arduinoCOM);
    
                 Arduino.Open();
                 Arduino.Write("5\n");
                 Arduino.Write(Demand);
    
                 //ReadSerialReceivedData();
    
                 Arduino.DataReceived += DataReceivedHandler;
    
                 Arduino.Close();
                 //tbDistanceCtrl.Clear();
                 //richTextBox1.AppendText(Demand);
            }
    
             void ArduinoDatum(string ComPort, string Demand)
             {
                 //SetSerialParameters(COMPortSettings.arduinoCOM);
                 try
                 {
                     Arduino = new SerialPort(ComPort, 9600, Parity.None, 8, StopBits.One);
                 }
                 catch (ArgumentNullException ex)
                 {
                     richTextBox1.AppendText(ex.Message);
                 }
    
                 /*Arduino.BaudRate = 9600;
                 Arduino.Parity = Parity.None;
                 Arduino.DataBits = 8;
                 Arduino.StopBits = StopBits.One;*/
                 
                 Arduino.DtrEnable = true;
                 Arduino.RtsEnable = true;
    
                 //Arduino.ReadTimeout = 5000;
                 //Arduino.WriteTimeout = 5000;
    
                 //Arduino.ReadTimeout = SerialPort.InfiniteTimeout;
                 richTextBox1.Multiline = true;
    
                 Arduino.DataReceived += DataReceivedHandler;
    
                 Arduino.Open();
    
                 //Arduino.DataReceived += DataReceivedHandler;
                 Arduino.Write("4\n");
    
                 //ReadSerialReceivedData();
    
                 Arduino.Close();
             }
    
            //Thread _thread = new Thread(() => ArduinoMoveToDemandPosition(COMPortSettings.arduinoCOM, DistanceToTravel));
    
            private void btnWrite_Click(object sender, EventArgs e)
            {
                SetSerialParameters(COMPortSettings.arduinoCOM);
                richTextBox1.AppendText("COM port opened.\n");
                richTextBox1.AppendText(COMPortSettings.arduinoCOM);
                Arduino.Close();
    
                SetSerialParameters(COMPortSettings.dpuCOM);
                richTextBox1.AppendText("COM port opened.\n");
                richTextBox1.AppendText(COMPortSettings.dpuCOM);
                Arduino.Close();
            }
            
            private void richTextBox1_TextChanged(object sender, EventArgs e)
            {
    
            }
    
            private void btnCalOp_Click(object sender, EventArgs e)
            {
                var CalOp = new Calibration();
                CalOp.Show();
            }
    
            private void textBox1_TextChanged(object sender, EventArgs e)
            {
                DistanceToTravel = tbDistanceCtrl.Text;
                //demand = (object)DistanceToTravel;
            }
    
            private void btnDatum_Click(object sender, EventArgs e)
            {
                /*SetSerialParameters(COMPortSettings.arduinoCOM);
    
                Arduino.Open();
                Arduino.Write("4\n");
    
                ReadSerialReceivedData();
    
                Arduino.Close();*/
                Thread _thread = new Thread(() => ArduinoDatum(COMPortSettings.arduinoCOM, DistanceToTravel));
                _thread.Start();
            }
    
            private void btnGo_Click(object sender, EventArgs e)
            {
                /*richTextBox1.AppendText(DistanceToTravel);
                richTextBox1.AppendText("Please wait until all the lines appear.");
                SetSerialParameters(COMPortSettings.arduinoCOM);
    
                Arduino.Open();
                Arduino.Write("5\n");
                Arduino.Write(DistanceToTravel);
    
                ReadSerialReceivedData();
    
                Arduino.Close();
                tbDistanceCtrl.Clear();
                richTextBox1.AppendText(DistanceToTravel);*/
                //ArduinoMoveToDemandPosition(COMPortSettings.arduinoCOM, DistanceToTravel);
                Thread _thread = new Thread(() => ArduinoMoveToDemandPosition(COMPortSettings.arduinoCOM, DistanceToTravel));
                _thread.Start();
            }
    
            private void btnClose_Click(object sender, EventArgs e)
            {
                Arduino.Close();
                richTextBox1.AppendText("COM Port Closed.\n");
            }
    
            private void btnCOMSet_Click(object sender, EventArgs e)
            {
                var COMSet = new COMPortSettings();
                COMSet.Show();
            }
        }
    }
    

    Tuesday, March 12, 2013 6:15 PM

All replies

  • the following works for me ...
        public partial class Form1 : Form {
            static SerialPort Arduino;
            static string DistanceToTravel;
            static bool _continue = true;
    
            private delegate void displayDelegate(string x);
            displayDelegate displayFunction;
    
            public Form1()
            {
                InitializeComponent();
                displayFunction = displayText;
            }
    
            private void button1_Click(object sender, EventArgs e) {
                Arduino = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
                Arduino.DataReceived += DataReceivedHandler;
                Arduino.Open();
                Arduino.WriteLine("VER\r");
            }
    
    
            private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
                SerialPort comm = (SerialPort)sender;
                string incoming_Data = comm.ReadExisting();
                //richTextBox1.AppendText("Received Data: \n");
                //richTextBox1.AppendText(incoming_Data + "\n");
                //Console.WriteLine(incoming_Data + "\n");
                displayFunction(incoming_Data + "\n");
            }
    
    
    
    
            void displayText(string x) {
                if (richTextBox1.InvokeRequired)
                    richTextBox1.Invoke(displayFunction, new object[] {x});
                else
                    richTextBox1.Text += x;
            }// end function 'DisplayText'
        }
    }
    

    Tuesday, March 12, 2013 6:54 PM
  • Exactly where is your code failing? Also it would be helpful if you cleaned it up and removed the commented out code so that it is more readable.
    Tuesday, March 12, 2013 7:29 PM
  • Thanks for the reply! I jus tnoticed, when I add Arduino.Close() at the end of the button_click function, the DataReceived Handler does not printout anymore. How can I implement Arduino.Close() at the end of that function without hindering the print out from DataReceived Handler.
    Wednesday, March 13, 2013 5:08 PM
  • Thanks for the reply! I jus tnoticed, when I add Arduino.Close() at the end of the button_click function, the DataReceived Handler does not printout anymore. How can I implement Arduino.Close() at the end of that function without hindering the print out from DataReceived Handler.

    You cannot.  Once you close the port, the buffers are emptied and communication stops.  Connecting to the microcontroller and communicating with it are two independant operations.  First you have to connect, then perform all of your communication, and then disconnect.

    You should create and configure your SerialPort component, and add the required event handlers, before you open the port.  When you are sure that you are finished receiving data, you can then close the port.

    Depending on how much data is returned, you may or may not receive the entire repsonse in one event.  Typically you need to continue to wait for DataReceived events until you determine that you have received all of the expected data.  Then it is safe to close the connection.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Wednesday, March 13, 2013 6:45 PM
    Moderator
  • Thanks for the reply again. I have 2 different functions that communicates withe same COM port. If I should not close the COM port, then how do I keep the COM port open without getting any errors or exceptions, such as the UnauthorizedAccessException, and still received and print out the data real time?
    Wednesday, March 13, 2013 7:19 PM
  • Why should you receive any errors like UnauthorizedAccessExceptions?

    Open the port when you start the application and don't close it again before you terminate the application. It is bad practice to open and close the port all the time and completely unnecessary.

    Note that the handler for the DataReceived event runs on a thread pool thread. Because of this, you need to use one of the four thread safe methods (Invoke, BeginInvoke, EndInvoke and CreateGraphics) to communicate to the UI thread. If you don't do that, you may experience errors, but this is not coursed by SerialPort, but by bad programming.

    Constructions like the one from Lincoln_MA above is NOT allowed !!!

    private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
       SerialPort comm = (SerialPort)sender;
       string incoming_Data = comm.ReadExisting();
       displayFunction(incoming_Data + "\n");
    }

    Note that Lincoln_MA calls displayFunction directly. A much better method is to Invoke displayFunction by means of the MethodInvoker delegate and an Invoke call and then check for BytesToReceive before terminating the handler for the DataReceived event.

    Note that if this method is used, it is necessary to close the port from a different thread to avoid a deadlock as described in chapter "SerialPort Receive" of my SerialPort tutorial.


    Everything should be made as simple as possible, but not simpler. Any fool can write code that a computer can understand. Good programmers write code that humans understand.


    Thursday, March 14, 2013 9:42 AM
  • Carsten,

    Did you read the rest of the function?  The order might be a little odd, but his displayFunction calls DisplayText which then contains the call to Invoke the AppendText method.  So the example is ok, if not to your taste.

    The "so-called" tutorial you link to (lol sorry I couldn't help it, you seem to like that phrase a lot) is quite dated and probably contains far too deep a dive into areas that the average programmer should not be concerned with.  For all the information provided in the article, one of the most important things appears to be missing and too much concern may be given to underlying framework mechanics.  I appreciate the effort you put into it, and I'm sure it was quite beneficial for folks back when it was first written, but today things are better explained in the MSDN documentation and some processes have been enchanced or simplified by new additions to the framework.  Plus there are so many new would-be programmers who won't take the time to read a wall of text that deep-dives areas they don't understand and don't want to know (thats why they picked VB and not C++!).  So its not that I'm trying to put down your effort, its just that I'm not sure it should still be advertised to today's beginner crowd.

    Probably the most important to point out about consuming the DataReceived event is to not block the event handler.  The handler should return as soon as possible.  So the data received handler may not be a good place to analyze the received data to determine if a particular message frame is complete.  In many cases it may be preferrable to blindly receive bytes in the event handler and store them in a buffer to be processed later by another thread (not the thread which raised the DataReceived event).  Once this other thread (which may or may not also be the UI thread) has analyzed the buffered data and determined that a complete frame is received, it can remove those bytes from the buffer and then hand them off for processing.  The processing can then take place on the same thread as the analysis (which again, may or may not be the UI thread) or on yet a different thread (which may be the UI thread this time if the analysis was not on the UI thread already, or may be another background thread).  Finally the processing routine can then update the UI, using Invoke if required (depending on whether the final processing took place on a background thread or the UI thread).

    As far as the inner-workings of the related Windows messages go, the average .Net programmer should accept that this is what the framework is designed to do and if you follow the documentation and samples you won't need to worry about those underpinnings.  Many good tools did not exist when that tutorial was written, such as the generic Action and Func delegates.  Users should not generally fear the cost of calling Invoke(New Action(AddressOf someMethod)).  Sure, you can still wind up blocking a thread if you invoke calls to it too rapidly for too long a time but it won't generally hurt anything to use a invocation like that for a simple cross-thread alert (possibly passing no data at all).  Also, using BeginInvoke adds another layer of complexity to the design through async patterns.  Users have to be aware of how this could affect the order of processing and complicate their ability to know what code should be executing at any given time while the program is running.  So there may be little performance benefit to be had by BeginInvoke compared to the complexity it could add over using a synchronous Invoke.

    The basic usage of the serial port is really quite simple with the only complication really being that you have to understand how to build a complete message based on the protocol of the data that you are receiving.  If you use a thread-safe collection such as ConcurrentQueue to buffer your received data and then use a seperate Task to montior the buffer, determine a complete frame and then process the frame and clean up the buffer, you can support any amount of required analysis and processing without disrupting the SerialPort or overcomplicating the program design.  This simple design works well for the most common scenarios such as sending simple configuration commands to and reading status data from any number of microcontroller based hardware devices (which seems to be the most common use of a SerialPort in these forums today).


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Thursday, March 14, 2013 3:37 PM
    Moderator
  • Thanks for the reply. Well, I just started programming in C# 3 weeks ago, and I'm not very accustomed to Java style of programming yet, and C# is quite different from the previous languages that I've learned, so there are so many questions I have not yet grasped a lot of the concepts yet. If I only have to open the COM port once, where could I open the COM port from? How do I access the already opened comport without having to get the UnauthorizedAccess Exception? Is Invoke delegates always unnecessary for printing out texts onto textbox in a thread? Why does the textbox need delegate to be able to print a text from a thread?
    Thursday, March 14, 2013 3:57 PM
  • @ StarbornWaylon

    I still don't understand where your UnauthorizedAccess Exception comes from. Describe a little more when it happens.

    "Is Invoke delegates always unnecessary for printing out texts onto textbox in a thread?"

    Don't you mean necessary? In practice, you need to use either Invoke or BeginInvoke to communicate between threads - or to make an awful lot of synchronization yourself.

    "Why does the textbox need delegate to be able to print a text from a thread?"

    Because the method you want to call was not known when Microsoft wrote the thread safe methods BeginInvoke and Invoke. Therefore, you need to build a delegate, which can hold the address of the method(s), you want to call. This delegate can also contain the data to transfer like in the above example (String x), but it doesn't need to. The standard delegate MethodInvoker (or EventHandler) may be used if you just want to "wake" a method, which can then grasp the data from the buffer itself.

    It is a very long story and very confusing. In principle, SerialPort is very simple, but since it uses multithreading, you need to understand how this works. It is all described in my tutorial. I know it is big and that all examples are in VB, but I have tried to make it understandable for beginners and if you think that you can find better information elsewhere and in the MSDN documentation as Reed Kimble writes, just go ahead and good luck ;-)


    Everything should be made as simple as possible, but not simpler. Any fool can write code that a computer can understand. Good programmers write code that humans understand.

    Thursday, March 14, 2013 8:26 PM
  • Thanks for the reply. My UnauthorizedAccess Exception comes when I try to write the second time after the COM port has been written the first time.
    Friday, March 15, 2013 6:11 PM
  • Do you write from the same method or from different methods?

    Maybe you make the clasical mistake to open and close the port all the time, so that the port is not open when you try to access it the second time?


    Everything should be made as simple as possible, but not simpler. Any fool can write code that a computer can understand. Good programmers write code that humans understand.

    Saturday, March 16, 2013 11:16 AM