C# waiting for the data on serial port

19,727

Solution 1

This code works perfectly for me:

port = new SerialPort(port, 9600, Parity.None, 8, StopBits.One);
port.Open();
port.DiscardOutBuffer();
port.DiscardInBuffer();
port.DataReceived += OnScan;

void OnScan(object sender, SerialDataReceivedEventArgs args)
{
    SerialPort port = sender as SerialPort;

    string line = port.ReadExisting();
// etc
}

Solution 2

Unfortunately waiting for a serial port data in C# is tricky, there is nothing like poll().

There is SerialPort.DataReceived which takes functions to be called on incoming data. So you assign there a function to trigger an arbitrary event. Your another function — the one to actually wait — should wait for this event.

Below is a simple example, it is commented, but in short: the TestFunc initializes and opens a serial port (in particular assigns the DataReceived). The Proxy() is a function that will be called every time a data arrived, it triggers an event. And WaitForAData() indeed waits for the event that will be triggered by Proxy() when a data appears. Note the lock(){}s, without them surrounding Monitor's functions it won't work properly.

It's just an example, you would probably want to remake WaitForAData() function to trigger an exception in case of timeout. And to add a boolean variable in case if the Proxy() was triggered before you began waiting, then serial port already have data. But I tested it (cause I need such a function now ☺), and it works.

namespace MyNamespace
{

class MySerial
{
    ///A condition variable that signals when serial has a data
    private System.Object SerialIncoming;

    public MySerial()
    {
        SerialIncoming = new Object();
    }

    /**
     * A proxy function that will be called every time a data arrived
     */
    private void Proxy(Object unused1, SerialDataReceivedEventArgs unused2)
    {
        Console.WriteLine("Data arrived!");
        lock (SerialIncoming)
        {
            Monitor.Pulse(SerialIncoming);
        }
    }

    /**
     * Waits for a data for the time interval Timeout
     * \param Timeout a timeout in milliseconds to wait for a data
     * \returns true in if a data did arrived, and false else
     */
    public bool WaitForAData(int Timeout)
    {
        lock (SerialIncoming)//waits N seconds for a condition variable
        {
            if (!Monitor.Wait(SerialIncoming, Timeout))
                {//if timeout
                    Console.WriteLine("Time out");
                    return false;
                }
            return true;
        }
    }

    /* Just a test function: opens a serial with speed, and waits
     * for a data for the «Timeout» milliseconds.
     */
    public void TestFunc(string serial, int speed, int Timeout)
    {
        SerialPort ser = new SerialPort(serial);
        ser.BaudRate = speed;
        ser.DataReceived += Proxy;
        ser.Open();
        if (WaitForAData(Timeout))
            Console.WriteLine("Okay in TestFunc");
        else
            Console.WriteLine("Time out in TestFunc");
    }
}

}

UPDATE: the problem wasted ½ of my day, so I hope I will save someone's time: the code above won't work in mono (but works in MS implementation) because serial port events are not supported as of writing these words.

Solution 3

If this is a Console application, you can use things like Console.ReadLine() etc. after calling the appropriate function of the COM Port component to start listening asynchronously. If this is a WinForms application. The message loop will of course keep showing your current form. In that case you can call asynchronous listening function in the Form_Load event or behind a button click.

The key point here is that you should call the asynchronous version of the listener function. There is no need to use delays or timers in that case.

Share:
19,727

Related videos on Youtube

sami manchnda
Author by

sami manchnda

Updated on September 14, 2022

Comments

  • sami manchnda
    sami manchnda over 1 year

    i am trying to get data from fingerprint scanner through c# application, but before the fingerprint can send, a my whole code executes.

    I tried using delay function and System.Threading.Thread.Sleep(1000), so it can get data before the next step executes, but it all seems futile.

    Could any one please provide any other option?

    I am using "SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)" to get data.

    • Kamil Budziewski
      Kamil Budziewski over 10 years
      Post code, so someone could help you.
  • sami manchnda
    sami manchnda over 10 years
    If some data is received and thread.sleep() is running then it will discard that data??
  • joe
    joe over 10 years
    Thread.Sleep is for the current thread. The DataReceived event is called from a different thread, so you WILL receive the data.
  • DaMachk
    DaMachk over 10 years
    That would be because you have to make an inter-thread value change, so find how to hanges values in one thread to another.
  • Hi-Angel
    Hi-Angel over 9 years
    It does not waits for a data, it is just reads an existing ones.
  • Offler
    Offler about 9 years
    @Hi-Angel I think code should work as expected. DataReceived will call the delegate OnScan only if data is received (and not immidiatly)
  • Hi-Angel
    Hi-Angel about 9 years
    @Offler yes. But what the asker expected is a code to wait on SerialPort. It was in my case too: suppose I have some kind of serial connection, and I want to wait for a data within a few seconds, and if a data didn't came, then return to suppose the connection is broken. It's not quite the code Servè wrote. Btw, here was my answer on the problem
  • Offler
    Offler about 9 years
    @Hi-Angel But if you really only want to do this it could be easier to forget everything with the events. I have done the following to Access an arduino "serial = new SerialPort(port, baudRate) { ReadTimeout = 2000 };" and afterwards "try { int returnValue = serial.ReadByte(); return returnValue;} catch (TimeoutException) //will occur after 2 seconds -> broken". Afterwards you don't Need the hole bunch of Monitors and so on. (okay the communication to arduino is one own thread at a hobby project - everything within this thread runs in order...)
  • Hi-Angel
    Hi-Angel about 9 years
    @Offler no, it's a pathos. I'm serious.
  • Hi-Angel
    Hi-Angel about 9 years
    @Offler out of curiosity I just tried to google again, armed with the «ReadTimeout» knowledge. Of answers where I found the timeout mentioned those where either one of +100500 answers, either just used it in code without even an attempt to explain what it does (It could be timeout to anything, right?) and with «DataReceived» to add a confuse. Since I already had a bad experience with C WinAPI and serial ports, no wonder that I found nothing. I'm wonder, how many people would find it like an easter egg.
  • Prashant Pimpale
    Prashant Pimpale about 5 years
    Can you help me with this:stackoverflow.com/questions/55026042/…
  • Hi-Angel
    Hi-Angel about 5 years
    @PrashantPimpale sorry, I didn't work with C# for some years, and hopefully I won't in future :) Additionally, I also don't have serial ports handy, nor I know if Mono (i.e. the only implementation on non-Windows systems since I don't have Windows handy) fixed serial port events. Basically, ATM I have no means at all to try to help you with your question.
  • Prashant Pimpale
    Prashant Pimpale about 5 years
    Ok, no problem! Thanks for the reply BTW!