How to Read and Write from the Serial Port

I just started learning how to send and receive data from my hardware through the C# GUI.

Can anyone please write a detail how to read data from the serial port?


SerialPort (RS-232 Serial COM Port) in C# .NET
This article explains how to use the SerialPort class in .NET to read and write data, determine what serial ports are available on your machine, and how to send files. It even covers the pin assignments on the port itself.

Example Code:

using System;
using System.IO.Ports;
using System.Windows.Forms;

namespace SerialPortExample
{
  class SerialPortProgram
  {
    // Create the serial port with basic settings
    private SerialPort port = new SerialPort("COM1",
      9600, Parity.None, 8, StopBits.One);

    [STAThread]
    static void Main(string[] args)
    { 
      // Instatiate this class
      new SerialPortProgram();
    }

    private SerialPortProgram()
    {
      Console.WriteLine("Incoming Data:");

      // Attach a method to be called when there
      // is data waiting in the port's buffer
      port.DataReceived += new 
        SerialDataReceivedEventHandler(port_DataReceived);

      // Begin communications
      port.Open();

      // Enter an application loop to keep this thread alive
      Application.Run();
    }

    private void port_DataReceived(object sender,
      SerialDataReceivedEventArgs e)
    {
      // Show all the incoming data in the port's buffer
      Console.WriteLine(port.ReadExisting());
    }
  }
}

I spent a lot of time to use SerialPort class and has concluded to use SerialPort.BaseStream class instead. You can see source code: SerialPort-source and SerialPort.BaseStream-source for deep understanding. I created and use code that shown below.

  • The core function public int Recv(byte[] buffer, int maxLen) has name and works like "well known" socket's recv().

  • It means that

    • in one hand it has timeout for no any data and throws TimeoutException.
    • In other hand, when any data has received,
      • it receives data either until maxLen bytes
      • or short timeout (theoretical 6 ms) in UART data flow

.

public class Uart : SerialPort

    {
        private int _receiveTimeout;

        public int ReceiveTimeout { get => _receiveTimeout; set => _receiveTimeout = value; }

        static private string ComPortName = "";

        /// <summary>
        /// It builds PortName using ComPortNum parameter and opens SerialPort.
        /// </summary>
        /// <param name="ComPortNum"></param>
        public Uart(int ComPortNum) : base()
        {
            base.BaudRate = 115200; // default value           
            _receiveTimeout = 2000;
            ComPortName = "COM" + ComPortNum;

            try
            {
                base.PortName = ComPortName;
                base.Open();
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine("Error: Port {0} is in use", ComPortName);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Uart exception: " + ex);
            }
        } //Uart()

        /// <summary>
        /// Private property returning positive only Environment.TickCount
        /// </summary>
        private int _tickCount { get => Environment.TickCount & Int32.MaxValue; }


        /// <summary>
        /// It uses SerialPort.BaseStream rather SerialPort functionality .
        /// It Receives up to maxLen number bytes of data, 
        /// Or throws TimeoutException if no any data arrived during ReceiveTimeout. 
        /// It works likes socket-recv routine (explanation in body).
        /// Returns:
        ///    totalReceived - bytes, 
        ///    TimeoutException,
        ///    -1 in non-ComPortNum Exception  
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="maxLen"></param>
        /// <returns></returns>
        public int Recv(byte[] buffer, int maxLen)
        {
            /// The routine works in "pseudo-blocking" mode. It cycles up to first 
            /// data received using BaseStream.ReadTimeout = TimeOutSpan (2 ms).
            /// If no any message received during ReceiveTimeout property, 
            /// the routine throws TimeoutException 
            /// In other hand, if any data has received, first no-data cycle
            /// causes to exit from routine.

            int TimeOutSpan = 2;
            // counts delay in TimeOutSpan-s after end of data to break receive
            int EndOfDataCnt;
            // pseudo-blocking timeout counter
            int TimeOutCnt = _tickCount + _receiveTimeout; 
            //number of currently received data bytes
            int justReceived = 0;
            //number of total received data bytes
            int totalReceived = 0;

            BaseStream.ReadTimeout = TimeOutSpan;
            //causes (2+1)*TimeOutSpan delay after end of data in UART stream
            EndOfDataCnt = 2;
            while (_tickCount < TimeOutCnt && EndOfDataCnt > 0)
            {
                try
                {
                    justReceived = 0; 
                    justReceived = base.BaseStream.Read(buffer, totalReceived, maxLen - totalReceived);
                    totalReceived += justReceived;

                    if (totalReceived >= maxLen)
                        break;
                }
                catch (TimeoutException)
                {
                    if (totalReceived > 0) 
                        EndOfDataCnt--;
                }
                catch (Exception ex)
                {                   
                    totalReceived = -1;
                    base.Close();
                    Console.WriteLine("Recv exception: " + ex);
                    break;
                }

            } //while
            if (totalReceived == 0)
            {              
                throw new TimeoutException();
            }
            else
            {               
                return totalReceived;
            }
        } // Recv()            
    } // Uart

Note that usage of a SerialPort.DataReceived event is optional. You can set proper timeout using SerialPort.ReadTimeout and continuously call SerialPort.Read() after you wrote something to a port until you get a full response.

Moreover you can use SerialPort.BaseStream property to extract an underlying Stream instance. The benefit of using a Stream is that you can easily utilize various decorators with it:

var port = new SerialPort();
// LoggingStream inherits Stream, implements IDisposable, needen abstract methods and 
// overrides needen virtual methods. 
Stream portStream = new LoggingStream(port.BaseStream);
portStream.Write(...); // Logs write buffer.
portStream.Read(...); // Logs read buffer.

For more information check:

  • Top 5 SerialPort Tips article by Kim Hamilton, BCL Team Blog
  • C# await event and timeout in serial port communication discussion on StackOverflow