Search code examples
c#winformsserial-portstatic-methodseventhandler

How to get data from a static event-handler for serial port


I'm currently write a method that read data consecutively from serial port in C# winform for .NET framework.

I write an event handler for it, but since its static, i cannot call variable from outside. Now i'm thinking of how to getting data from the static method to outside. Like share data between static method and normal method.

Ofc this won't work correctly. I want sp variable go into mySerialPort but i don't know how.

private void GetComPortData()
{
    SerialPort mySerialPort = new SerialPort("COM5");
    mySerialPort.BaudRate = 115200;
    mySerialPort.Parity = Parity.None;
    mySerialPort.StopBits = StopBits.One;
    mySerialPort.DataBits = 8;
    mySerialPort.Handshake = Handshake.None;
    mySerialPort.RtsEnable = true; //change to false if not need rts
    mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
    mySerialPort.Open();
    

    //how to make mySerialPort = sp?
    //string dataComPort = mySerialPort.ReadExisting();
    var data = dataComPort.Split(new[] { '/' },4);
    /*Do some work to show data in datagridview*/
    mySerialPort.Close();
}
//handle comport data
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort sp = (SerialPort)sender;
    string indata = sp.ReadExisting();
}

I also tried to change static to other reference type, but it doesn't work. Any help is much appreciated.


Solution

  • In addition to the excellent comment by Olivier about 'not closing' the serial port, there is also the likelihood of DataReceived being on a different thread. You may have to BeginInvoke to prevent a cross-threading exception if you plan to "do some work to show data in datagridview". I used to do this sort of thing quite a lot and here's an example of what worked for me for receiving the event and then locking a critical section while the handler loops to get "all" (may require some kind of throttling) of the data available in the buffer while displaying chunks <= 16 bytes in the DGV.

    data grid view


    Set up DataGridView

    public partial class MainForm : Form
    {
        public MainForm() =>InitializeComponent();
        BindingList<DataReceived> DataSource { get; } = new BindingList<DataReceived>();
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            dataGridView.DataSource = DataSource;
            dataGridView.AllowUserToAddRows = false; // Critical for this example.
            dataGridView.Columns[nameof(DataReceived.Timestamp)].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
            dataGridView.Columns[nameof(DataReceived.Data)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
    
            MockSerialPort mySerialPort = new MockSerialPort("COM5");
            mySerialPort.BaudRate = 115200;
            mySerialPort.Parity = Parity.None;
            mySerialPort.StopBits = StopBits.One;
            mySerialPort.DataBits = 8;
            mySerialPort.Handshake = Handshake.None;
            mySerialPort.RtsEnable = true;
            mySerialPort.DataReceived += DataReceivedHandler;
            mySerialPort.Open();
        }
        .
        .
        .
    

    Handle DataReceived

        .
        .
        .
        SemaphoreSlim _criticalSection = new SemaphoreSlim(1, 1);
        private async void DataReceivedHandler(object sender, MockSerialDataReceivedEventArgs e)
        {
            await _criticalSection.WaitAsync();
            if(!IsDisposed) BeginInvoke((MethodInvoker)delegate 
            {
                try
                {
                    if (sender is MockSerialPort port)
                    {
                        while (port.BytesToRead > 0)
                        {
                            byte[] buffer = new byte[16];
                            int success = port.Read(buffer, 0, buffer.Length);
                            string display = BitConverter.ToString(buffer, 0, success).Replace("-", " ");
                            var data = new DataReceived { Data = display };
                            DataSource.Add(data);
                        }
                    }
                }
                finally
                {                    
                    _criticalSection.Release();
                }
            });
        }
    }
    

    class DataReceived
    {
        public string Timestamp { get; } = DateTime.Now.ToString(@"hh\:mm\:ss\.fff tt");
        public string? Data { get; set; }
    }
    

    I used a MockSerialPort to test this. Full Code