Fast Serial Communication For C# Real-Time Applications

While I’m working in AeroXtreme MAV Researching Project, i faced a serious problem in the serial communication in .NET C#. The standard C# serial component is too slow to handle fast serial communication for real-time applications. The MAV Main Computer send data over serial communication in high frequency and send a lot of data. When I used the standard C# serial library “Receiving Event” a huge lag of communication appeared and the buffers is jammed due to the high frequency of communication. After a lot of testing and debugging i found that the serial communication using the standard library will fail in such application so i tried to develop a Fast Serial Library to fulfill my requirement of real-time communication. My Library solve this problem and it can work with high frequency communication without any overhead on the processor due to the frequency control technique.

/* This AX-Fast Serial Library
   Developer: Ahmed Mubarak - RoofMan

   This Library Provide The Fastest & Efficient Serial Communication
   Over The Standard C# Serial Component
*/

using System;
using System.IO.Ports;
using System.Threading;
using Diagnostics = System.Diagnostics;

namespace RTSerialCom
{
    public class DataStreamEventArgs : EventArgs
    {
        #region Defines
        private byte[] _bytes;
        #endregion

        #region Constructors
        public DataStreamEventArgs(byte[] bytes)
        {
           _bytes = bytes;
        }
        #endregion

        #region Properties
        public byte[] Response
        {
            get { return _bytes; }
        }
        #endregion
    }

    public class SerialClient : IDisposable
    {
        #region Defines
        private string _port;
        private int _baudRate;
        private SerialPort _serialPort;
        private Thread serThread;
        private double _PacketsRate;
        private DateTime _lastReceive;
        /*The Critical Frequency of Communication to Avoid Any Lag*/
        private const int freqCriticalLimit = 20;
        #endregion

        #region Constructors
        public SerialClient(string port)
        {
            _port = port;
            _baudRate = 9600;
            _lastReceive = DateTime.MinValue;

            serThread = new Thread(new ThreadStart(SerialReceiving));
            serThread.Priority = ThreadPriority.Normal;
            serThread.Name = "SerialHandle" + serThread.ManagedThreadId;
        }
        public SerialClient(string Port, int baudRate)
            : this(Port)
        {
            _baudRate = baudRate;
        }
        #endregion

        #region Custom Events
        public event EventHandler<DataStreamEventArgs> OnReceiving;
        #endregion

        #region Properties
        public string Port
        {
            get { return _port; }
        }
        public int BaudRate
        {
            get { return _baudRate; }
        }
        public string ConnectionString
        {
            get
            {
                return String.Format("[Serial] Port: {0} | Baudrate: {1}",
                    _serialPort.PortName, _serialPort.BaudRate.ToString());
            }
        }
        #endregion

        #region Methods
        #region Port Control
        public bool OpenConn()
        {
            try
            {
                if (_serialPort == null)
                    _serialPort = new SerialPort(_port, _baudRate, Parity.None);

                if (!_serialPort.IsOpen)
                {
                    _serialPort.ReadTimeout = -1;
                    _serialPort.WriteTimeout = -1;

                    _serialPort.Open();

                    if (_serialPort.IsOpen)
                        serThread.Start(); /*Start The Communication Thread*/
                }
            }
            catch (Exception ex)
            {
                return false;
            }

            return true;
        }
        public bool OpenConn(string port, int baudRate)
        {
            _port = port;
            _baudRate = baudRate;

            return OpenConn();
        }
        public void CloseConn()
        {
            if (_serialPort != null && _serialPort.IsOpen)
            {
                serThread.Abort();

                if (serThread.ThreadState == ThreadState.Aborted)
                    _serialPort.Close();
            }
        }
        public bool ResetConn()
        {
            CloseConn();
            return OpenConn();
        }
        #endregion
        #region Transmit/Receive
        public void Transmit(byte[] packet)
        {
            _serialPort.Write(packet, 0, packet.Length);
        }
        public int Receive(byte[] bytes, int offset, int count)
        {
            int readBytes = 0;

            if (count > 0)
            {
                readBytes = _serialPort.Read(bytes, offset, count);
            }

            return readBytes;
        }
        #endregion
        #region IDisposable Methods
        public void Dispose()
        {
            CloseConn();

            if (_serialPort != null)
            {
                _serialPort.Dispose();
                _serialPort = null;
            }
        }
        #endregion
        #endregion

        #region Threading Loops
        private void SerialReceiving()
        {
            while (true)
            {
                int count = _serialPort.BytesToRead;

                /*Get Sleep Inteval*/
                TimeSpan tmpInterval = (DateTime.Now - _lastReceive);

                /*Form The Packet in The Buffer*/
                byte[] buf = new byte[count];
                int readBytes = Receive(buf, 0, count);

                if (readBytes > 0)
                {
                    OnSerialReceiving(buf);
                }

                #region Frequency Control
                _PacketsRate = ((_PacketsRate + readBytes) / 2);

                _lastReceive = DateTime.Now;

                if ((double)(readBytes + _serialPort.BytesToRead) / 2 <= _PacketsRate)
                {
                    if (tmpInterval.Milliseconds > 0)
                        Thread.Sleep(tmpInterval.Milliseconds > freqCriticalLimit ? freqCriticalLimit : tmpInterval.Milliseconds);

                    /*Testing Threading Model*/
                    Diagnostics.Debug.Write(tmpInterval.Milliseconds.ToString());
                    Diagnostics.Debug.Write(" - ");
                    Diagnostics.Debug.Write(readBytes.ToString());
                    Diagnostics.Debug.Write("\r\n");
                }
                #endregion
            }

        }
        #endregion

        #region Custom Events Invoke Functions
        private void OnSerialReceiving(byte[] res)
        {
            if (OnReceiving != null)
            {
                OnReceiving(this, new DataStreamEventArgs(res));
            }
        }
        #endregion
    }
}

I wrote a sample project that will help you understand how to use it (Download Sample)

58 Comments on “Fast Serial Communication For C# Real-Time Applications

  1. hi Ahmed
    i have an application with the following:
    baudrate 115200
    messages are 400bytes long 100ms apart from each other
    and i need to acknowledge each message i receive,
    will it works?
    how does the frequency control will help me here??
    i actually try it but did not receive the whole bytes of the message!!

    Like

    • Use This Config
      Frequency Control = 1000Hz = 1ms -> Baudrate = 115200 Baud – Packet Len = 400 Byte

      but the Library i shared used to improve the speed or reception and transmission, and the frequency control used to reduce the CPU overhead only but sometime you can use it to have more processor time to make the communication smoother but that’s mean processor overhead.

      I prefer to develop upper layer (Protocol layer) to handle the received bytes and to make sure the message is complete and correct, and in this layer too you have to develop a technique of handshaking to use it in acknowledgement.

      Simple Architecture
      ———————

      • App Layer -> Your Application
      • Protocol Layer -> Process The Messages and Form it and do some CRC on it if possible, to make sure the message is correct and complete
      • Serial Layer -> Provide Real-time Communication with the other clients with very small delay for real-time applications

      Like

  2. Can you show me how to use the data in the main thread?
    Im getting bogus data after copyto() the byte[] to main thread.

    Like

    • You have 2 methods to this:
      Timers like i done in the example or using Delegates to update the GUI.

      If you can’t solve the problem send me on my private email a sample from your problem to send you the solution in the same sample you sent 🙂

      Like

  3. Would you recommend if I used this library to handle received data from serial? I’m building an electronic drum and I’m using a software app as the “brain” Can you suggest any other thing to make it work?

    Like

  4. Hello RoofMan
    many thanks to share this great library !!
    I am trying to receive from serial using this
    however the data over 115200 baud is missing sometimes.
    What is your maximum data transfer rate ?

    Like

      • Hello Roofman

        It is really thanks for the test.
        However would you let me know what was your maximum speed when you use for your project?

        Like

  5. Hi,

    Thanks a lot for the code shared. I am quite new to C#, so sorry if I sound a bit silly.
    As of now I am using serialPort1.ReadLine() in the System.IO.Ports namespace. The serial will be printing one set of data is one line and I have to get the line into the program as string. Can you tell me which function to call in your class for this.

    Thanks
    Harish

    Like

  6. It’s fake… I’m searching for really fast serial communication to delete lags after receiving data.
    This solution is not faster at all. Lag is 15ms, still… You changed way to receive data bytes.

    Like

  7. Very nice job Ahmed, thank you!!! I works fine on VS2012 but when I run it on VS2010, do not read from serial port.

    Like

      • hi Ahmed, thanks for the reply. I´m using .NET 4 Client Profile in both but on VS2010 do not run. I tried to change the differents Target frameworks but still does not run

        Like

  8. Hi Ahmed!

    I’m using VS2010 and .net framewotk 4.0. and reception is working. But I can’t figure out how to send data with your library: Would you help me please ?

    Regards !

    Mladen

    Like

      • i add this code to reciveHandler
        str = System.Text.Encoding.Default.GetString(e.Response);
        and in timer add this
        File.AppendAllText(@”J:\test1.txt”, str);

        for test i send data ( 1 to 12000)from ARM
        but in my tes1 file my data repeated
        for example
        12341234567891056789105678910111213……12000
        1234
        repeated
        and
        5678910

        Like

      • Debug it. The Lib don’t return duplication.

        Only new data from the buffer. Check your handling code.

        I’m sure it’s your code. Because alot of people use it like me without any problem.

        If you want extra support. Send me your code privately on my mail

        Roofman2008@gmail.com

        Like

  9. I have to acheive the speed of 921,600bps with serial interface in c#. So, I have a question. What is the maximum speed of serial communication if above source code is applied?

    Like

  10. Hey:
    First of all, thank you very much for this library, I am planning to use this to make a GUI to one project in which I need to have a great receiving velocity, this must be very silly question but this is my first time with threading.
    I am having a hard time to traduce my project from the Serial Class library to yours. I am sending from my MCU 258 bytes to the PC, and I am waiting for this 258 bytes to get into the serial port, and then proceed with the analysis. I am currently using the property of e.Respose.Length to get the number of bytes in my serial buffer, and only if e.Respose.Length >= 258, the program read the data with your serialclient.Receive(Array, offset, count).
    But the problem here is that I cannot receive the 258 bytes of data, and the event doesn’t occur anymore…
    The question here is how can I wait for data chunk to get to my serial port?

    Like

    • Create a global Byte list and when your receive bytes append to the list and when list count >=256 process the data.

      Like

      • First of all, thanks for the advice. I did what you recommend me yesterday and it worked quite good, but one thing that seems strange to me, is that: the time between I send the command to the MCU to send me the data until I receive the data (reaction time) is greater than the time from the serial class library.
        How can I know the reaction time of serial events? I am using the stopwatch library, from the moment I send the data request to the moment I receive all the data I am needing.
        In fact the serial class has an average time of reaction of 25 ms, but your library in the way I am using it is giving me an average time of reaction of 40 ms.
        this is the code of the receive handler:
        private void receiveHandler(object sender, DataStreamEventArgs e)
        {
        readBuf.AddRange(e.Response.ToList());
        if (readBuf.Count >= readBuffer.Length)
        {
        timer_count2 = swatch.ElapsedMilliseconds;
        this.Invoke(new EventHandler(ChangeToArr));
        }
        }
        As you can see is simple and it works in my code, and although the reaction time is almost invariable in compared to the time of reaction of the serial class, the thing is that the time of reaction is slow.
        I don’t know if I am missing something, I would appreciate any advice that you can bring me.
        Martin

        Like

  11. When transmitting if the receiving application is not successfully picking up – the SerialClient locks up. Placing a real timeout value fixes the issue. I added properties for IsOpen and also for transmit and receive text.

    Like

  12. Just a heads up running “Diagnostics.Debug…” within SerialReceiving will eat your CPU. On my MAC +- 10% constantly.

    Like

  13. I realize this has been our there for awhile, and thank-you for the nice library!!! This has helped me a ton. I do have a question, it seems that the receive buffer only receives 512 bytes before you need to read it, is there away to increase that? Maybe to 1000?

    Thanks again for the awesome library!
    Brandon

    Like

  14. your library saved my job reading video timecode from 24 up to 60 frames per second. the standard c# library is far too slow to do this without timecode gaps. thank you very much!

    Like

  15. Hello RoofMan,
    If the serial connection is interrupted, the program crashes. Is there a way to catch this error?
    Thank you for support, Micha

    Like

  16. iam using serialport control in application and sending 24 bytes for every 2.5 milliseconds to board and reciveing 104 bytes from board using reciving handler in gui but unable receive total bytes in gui.iam miising bytes in gui.

    Like

Leave a comment