Serial Communication gets back wrong answer when something is running in the background (like browsing the hard drive)

StackOverflow https://stackoverflow.com/questions/17678887

Question

I have some big trouble with serial requests.

Description from what i want:

  • establish a serial connection, send serial requests to 6 temperature sensors one by one (this is done every 0,5 second in a loop)

  • the question and answer-destination is stored in a List array

  • every request is started in a separate thread so the gui does not bug while the programme waits for the sensor-hardware to answer

My problem:

The connection and the request is working fine, but if I am browsing data at the local hard drive the answer from the sensor-unit gets destroyed (negative algebraic sign or value from other sensor or simply wrong value). How does this happen or how can I solve this?

Where I guess the problem might be:

  • In the private void ReceiveThread() of class SerialCommunication

Here is my code:


Class CommunicationArray:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hardwarecommunication
{
    public class CommunicationArray
    {
        public string request { get; set; }
        public object myObject { get; set; }
        public string objectType { get; set; }
    }
}

Class SerialCommunication

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;

namespace Hardwarecommunication
{
    class SerialCommunication
    {
        Thread t2;
        Thread t;
        private SerialPort serialPort = new SerialPort("COM2", 115200, Parity.Even, 8, StopBits.One);
        string serialAnswer = "";
        private volatile bool _shouldStop;
        private int counter;

        List<CommunicationArray> ar = new List<CommunicationArray>();

        object[] o = new object[3];


        public void addListener(string request, object myObject, string objectType)
        {
            CommunicationArray sa = new CommunicationArray();

            sa.request = request;
            sa.myObject = myObject;
            sa.objectType = objectType;
            ar.Add(sa);
        }

        public void startListen()
        {
            t2 = new Thread(() => writeSerialPortThread());
            t2.Start();
        }



        public void startSerialPort2()
        {

            try
            {
                serialPort.Open();
                //MessageBox.Show("Connection opend!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        public void stopSerialPort2()
        {
            try
            {
                if (serialPort.IsOpen == true)
                    // Connection  closed
                    serialPort.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        private void writeSerialPortThread()
        {
            string request = "";

            for (int i = 0; i < ar.Count(); i++)
            {
                request = ar[i].request;
                //request = ((object[])ar[0])[0].ToString();
                //if (!t.IsAlive)
                //{
                try
                {
                    t = new Thread(ReceiveThread);
                    _shouldStop = false;
                    //MessageBox.Show("start thread");
                    t.Start();
                    serialPort.Write(request);
                    Thread.Sleep(50);
                    _shouldStop = true;
                    t.Join();
                }
                catch
                {
                }
                Label tmpLabelObject = (Label)ar[i].myObject;
                serialAnswer = serialAnswer.Replace("=", "");
                if (tmpLabelObject.InvokeRequired)
                {
                    MethodInvoker UpdateLabel = delegate
                    {
                        tmpLabelObject.Text = serialAnswer;
                    };
                    try
                    {
                        tmpLabelObject.Invoke(UpdateLabel);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private void ReceiveThread()
        {
            //MessageBox.Show("in thread");
            while (!_shouldStop)
            {
                serialAnswer = "";
                try           
                {
                    //MessageBox.Show("in thread");
                    serialAnswer = serialPort.ReadTo("\r");
                    if (serialAnswer != "")
                    {
                    }
                    return;
                }
                catch (TimeoutException) { }
            }
        }
    }
}

Class Form1 //to establish the connection and to start the Sensor request

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

namespace Hardwarecommunication
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private SerialCommunication serialCommunication1 = new SerialCommunication();

        private void Form1_Load(object sender, EventArgs e)
        {
            //start up serial connection
            serialCommunication1.startSerialPort2();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            timerRecord.Enabled = true;
            if (this.buttonStart.Text == "Start")
                this.buttonStart.Text = "Stop";
            else
                this.buttonStart.Text = "Start";            
        }

        private void timerRecord_Tick(object sender, EventArgs e)
        {
            if (this.buttonStart.Text == "Stop")
            {
                this.serialCommunication1.startListen();
            }
        }

        private void buttonFillRequestArray_Click(object sender, EventArgs e)
        {         
            this.serialCommunication1.addListener("$0BR00\r" + "\r", this.labelResult0, "label0"); //request to the hardware
            this.serialCommunication1.addListener("$0BR01\r" + "\r", this.labelResult1, "label1");
            this.serialCommunication1.addListener("$01R00\r" + "\r", this.labelResult2, "label2");
            this.serialCommunication1.addListener("$01R01\r" + "\r", this.labelResult3, "label3");
            this.serialCommunication1.addListener("$01R02\r" + "\r", this.labelResult4, "label4");          
        }
    }
}

I woud be happy about any try to fix the problem. I coud also upload the solution as .zip but you can't test it at all because you do not have the sensor hardware.

Was it helpful?

Solution

Note: serialPort.Write(string) is a non-blocking store into the output buffer.

That means the following won't guarantee you've even finished writing your request before you stop listening for a response:

serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;

You could add:

while( serialPort.BytesToWrite > 0 ) Thread.Sleep(1); // force blocking

but it's ill advised.

One thing I'm wondering. There is only a single serial port here. Why do you want many different threads to work with it when you could manage the entire serial port interaction with a single thread? (Or at worse, 1 thread for input 1 thread for output)

To me it makes a lot more sense to store up requests into a queue of some kind and then peel them off one at a time for processing in a single thread. Responses could be similarly queued up or fired as events back to the caller.

EDIT: If you don't mind one read/write cycle at a time you could try:

string response;
lock(serialPort) {
    // serialPort.DiscardInBuffer(); // only if garbage in buffer.
    serialPort.Write(request);
    response = serialPort.ReadTo("\r"); // this call will block till \r is read.
                                        // be sure \r ends response (only 1)
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top