1

I need event, that will call my function after full line received, not just one byte.

SerialPort object in .NET has 3 events: DataReceived, ErrorReceived, PinChanged.

When im using DataReceived - event is "firing" after 1 byte, or after "x" bytes defined in "ReceiveByteThreshold" property. Line length may vary, so i cant predict "x".

Can someone give me a hint?

I have to create some buffer, which will collect bytes until LF/CRLF, or there is better approach to problem?

Kamil
  • 13,363
  • 24
  • 88
  • 183

4 Answers4

7

You cannot get this, the only option is SerialPort.ReceivedBytesThreshold to delay the DataReceived event handler call and that's useless for a variable length response.

The workaround is very simple, just call ReadLine() in your DataReceived event handler. That will block on a worker thread, not affecting anything else going on in your program. No danger either of additional events firing while the ReadLine() call is blocking, it is interlocked inside the SerialPort class. Use the ReadTimeout property if necessary if the communication isn't reliable enough so ReadLine() will not block forever. Set it to ten times the expected delay in receiving the longest possible response.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I was thinking about it, but i worried about that multiple events firing. Thank you for that very valuable answer. – Kamil Oct 01 '12 at 14:25
4

You'll have to do it yourself. Use DataReceived and check each byte. Collect the bytes in a buffer until you get a newline and then handle the buffer as a line at that point.

Corey Ogburn
  • 24,072
  • 31
  • 113
  • 188
  • Thank you. I almost accepted your reply, but there is better way. Check accepted answer. – Kamil Oct 01 '12 at 14:29
  • I would have picked that answer, too. My experience with SerialPort is a little limited. – Corey Ogburn Oct 01 '12 at 14:33
  • I need to support dynamically choosing which newline character so ReadLine() even with the set line char doesn't work for me :) This one was the answer for me – Marc Magon Nov 09 '20 at 22:45
0

The hint:

The SerialPort class has a property NewLine to set the value used to interpret the end of a call to the ReadLine method.

Serge
  • 6,088
  • 17
  • 27
0

Here is my quickly implemented, non blocking, same thread solution. It is a very basic state machine that waits for '\r' and '\n' and then sends all the buffered characters for parsing. You can alter it to whatever line-break value you want by changing the state machine itself. In this approach you can register for the OnNewLineReceived event and process the data from the SerialStringMessgae eventhandler. No try/catch overhead. No deadlocks.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace NonBlockingSerialPortReadLine
{
    public partial class Form1 : Form
    {
    System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
    public event EventHandler OnNewLineReceived;
    System.Windows.Forms.Timer NewDataTimer = new System.Windows.Forms.Timer();
    int StateMachine = 0;
    StringBuilder stringBuffer = new StringBuilder();

    public Form1()
    {
        InitializeComponent();
        InitTimer();
        InitOnNewLineReceived();
    }

    private void InitTimer()
    {
        NewDataTimer.Interval = 50;
        NewDataTimer.Tick += NewDataTimer_Tick;
    }

    private void InitOnNewLineReceived()
    {
        OnNewLineReceived += Form1_OnNewLineReceived;
    }

    void Form1_OnNewLineReceived(object sender, EventArgs e)
    {
        SerialStringMessgae STM = e as SerialStringMessgae;
        string messgae = STM.message;

        // PARSE YOU MESSAGE HERE - the debug line below is not mandatory

        System.Diagnostics.Debug.WriteLine(messgae);
    }

    class SerialStringMessgae : EventArgs
    {
        public string message;
    }

    private void StartListeningButton_Click(object sender, EventArgs e)
    {
        StartListeningButton.Enabled = false;
        sp = new System.IO.Ports.SerialPort("COM4",57600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
        try
        {
            sp.Open();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
            return;
        }
        if (sp.IsOpen)
        {
            NewDataTimer.Enabled = true;
        }

    }

    void NewDataTimer_Tick(object sender, EventArgs e)
    {
        string newData = sp.ReadExisting();
        foreach (char c in newData)
        {
            switch (StateMachine)
            {
                case 0:
                    // waiting for '\r'
                    if (c == '\r')
                    {
                        StateMachine = 1;
                    }
                    else
                    {
                        stringBuffer.Append(c);
                    }
                    break;
                case 1:
                    // waiting for '\n'
                    if (c == '\n')
                    {
                        if (OnNewLineReceived != null)
                        {
                            SerialStringMessgae STM = new SerialStringMessgae();
                            STM.message = stringBuffer.ToString();
                            OnNewLineReceived(this, STM);
                        }
                    }
                    // after parsing the message we reset the state machine
                    stringBuffer = new StringBuilder();
                    StateMachine = 0;
                    break;
            }
        }
    }

}
}
Hadar Ben David
  • 189
  • 1
  • 8