Serial port polling and data processing

I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will accept more than 2000 measurements (each measurement is 7 bytes, all in hexadecimal format). And they shoot at the same time. I am currently polling 4 serial ports. In addition, I translated each dimension into String and added it to Stringbuilder. When I finish receiving the data, it will be output to a file. The problem is that processor consumption is very high, from 80% to 100%.

I went though some articles and put Thread.Sleep (100) at the end. This reduces processor time when there is no data. I also put Thread.Sleep at the end of each survey when BytesToRead is less than 100. This only helps to a certain extent.

Can someone suggest a serial polling solution and process the data I receive? Maybe adding every time I get something causes a problem?

//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox,     StringBuilder data)
    {
        textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));

        int bytesRead;
        int t;
        Byte[] dataIn;

        while (mySerialPort.IsOpen)
        {
            try
            {
                if (mySerialPort.BytesToRead != 0)
                {
                  //trying to read a fix number of bytes
                    bytesRead = 0;
                    t = 0;
                    dataIn = new Byte[bytesPerMeasurement];
                    t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
                    bytesRead += t;
                    while (bytesRead != bytesPerMeasurement)
                    {
                        t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
                        bytesRead += t;
                    }
                    //convert them into hex string
                    StringBuilder s = new StringBuilder();
                    foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
                    var line = s.ToString();

                                            var lineString = string.Format("{0}  ----          {2}",
                                                      line,
                                                    mySerialPort.BytesToRead);
                    data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.

                    ////use delegate to change UI thread...
                    textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));

                    if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
                }
            else{Thread.Sleep(100);}

            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.ToString());
            }
        }


    }
+5
source share
3 answers

This is not a good way to do this, it is much better to work with the DataReceived event.

basically with serial ports there is a 3-step process that works well.

  • Receive data from a serial port
  • Waiting until you have the appropriate piece of data.
  • Data interpretation

so something like

class DataCollector
{
    private readonly Action<List<byte>> _processMeasurement;
    private readonly string _port;
    private SerialPort _serialPort;
    private const int SizeOfMeasurement = 4;
    List<byte> Data = new List<byte>();

    public DataCollector(string port, Action<List<byte>> processMeasurement)
    {
        _processMeasurement = processMeasurement;
        _serialPort = new SerialPort(port);
        _serialPort.DataReceived +=SerialPortDataReceived;
    }

    private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while(_serialPort.BytesToRead > 0)
        {
           var count = _serialPort.BytesToRead;
           var bytes = new byte[count];
           _serialPort.Read(bytes, 0, count);
           AddBytes(bytes);
        }
    }

    private void AddBytes(byte[] bytes)
    {
        Data.AddRange(bytes);
        while(Data.Count > SizeOfMeasurement)            
        {
            var measurementData = Data.GetRange(0, SizeOfMeasurement);
            Data.RemoveRange(0, SizeOfMeasurement);
            if (_processMeasurement != null) _processMeasurement(measurementData);
        }

    }
}

. Bytes , , , .... 1 , 2 , , . , - , , 2.

-

var collector = new DataCollector("COM1", ProcessMeasurement);

  private void ProcessMeasurement(List<byte> bytes)
            {
                // this will get called for every measurement, so then
                // put stuff into a text box.... or do whatever
            }
+6
+2

, :

mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);

This eliminates the need to run a separate thread for each serial port you are listening to. In addition, each DataReceived handler will be called exactly when the data is available, and will consume as much processor time as necessary to process the data, and then go to the application / OS.

If this does not solve the problem of CPU usage, it means that you are processing too much. But if you do not have very fast serial ports, I cannot imagine that you have code that will have problems.

+1
source

All Articles