Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# wait for an echo on serial port, check it, and have a timout on the wait

Tags:

c#

Hello and thanks for considering this query: I am sending a command from a PC over a virtual serial port to an embedded system which echos back the command when the embedded system has completed the command.

I can send the command fine and see the echo, when the command is completed by the embedded sytem, but I am having trouble finding a suitable way to wait or delay the program until the echoed command is received, so that I may proceed and send the next command. I suppose its a type of "high level" flow control that I'm trying to implement. The code is in C#. I'd like to wait fro the echo and have a timeout as well in case communication is lost between the PC and embedded system, so that the program does not freeze. Any wizz kids out there that can suggest a neat way to do this? I am not a great c# programmer, just learning.

This is the receive function that I have:

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        // If the com port has been closed, do nothing
        if (!comport.IsOpen) return;

        // This method will be called when there is data waiting in the port's buffer

        // Determain which mode (string or binary) the user is in
        if (CurrentDataMode == DataMode.Text)
        {
            // Read all the data waiting in the buffer
            string data = comport.ReadExisting();

            // Display the text to the user in the terminal
            Log(LogMsgType.Incoming, data);
        }
        else
        {
            // Obtain the number of bytes waiting in the port's buffer
            int bytes = comport.BytesToRead;

            // Create a byte array buffer to hold the incoming data
            byte[] buffer = new byte[bytes];

            // Read the data from the port and store it in our buffer
            comport.Read(buffer, 0, bytes);

            // Show the user the incoming data in hex format
            Log(LogMsgType.Incoming, ByteArrayToHexString(buffer));
            }
    }

This is an example of a call to Transmitt a command:

     text = "AR" + 50 + "\r";            //transmit a command to move
     this.comport.Write(text);

Currently I'm using a time Delay [Thread.Sleep(TIMEGAP)] and assuming that the message is executed and that the echo back from the embedded system is fine BUT I do not check it and also wait a very long time to ensure that it is completed:

     text = "AR" + 50 + "\r";            //transmit a command to move
     this.comport.Write(text);
     Thread.Sleep(TIMEGAP);              //Timegap = 10000ms

I really would like to replace the time delay call [Thread.Sleep(TIMEGAP)] with a function/method that monitors the response on the serial port, checks to see that it is the same as the one sent and then allows the program code to proceed to the next command, AND if the correct echo [AR50\r in the above example] is not received in say for example 5 seconds, then the program reports an error.

Any suggestions?

Thank you!

like image 314
user2281033 Avatar asked Apr 15 '13 03:04

user2281033


1 Answers

The easiest way is not to use the DataReceived event, but by setting a ReadTimeout and using the Read method.

And since you're dealing with ASCII, you should check out the ReadLine method.

Both will throw a TimeoutException if ReadTimeout has elapsed without incoming data.

If, however, the embedded system can send unsolicited messages, the you'll need an other approach. Then you could put the echo you're expecting in a global string variable, and have the receive event set a ManualResetEvent when the echo has been received. Then you can wait for the ManualResetEvent with a timeout. This will also involve thread synchronization using the lock statement.

If GC is not an issue, I would probably start with something like this:

using System.Text;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication2
{
    class Program
    {
        static string serialBuffer = "";
        static string expectedEcho = null;
        static object expectedEchoLock = new object();
        static ManualResetEvent expectedEchoReceived = new ManualResetEvent(false);
        static SerialPort port = new SerialPort("COM1", 19200, Parity.None, 8, StopBits.One);

        static void Main(string[] args)
        {
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
        }

        static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            while (port.BytesToRead > 0)
            {
                byte[] buffer = new byte[port.BytesToRead];
                int bytesRead = port.Read(buffer, 0, buffer.Length);
                if (bytesRead <= 0) return;
                serialBuffer += Encoding.UTF8.GetString(buffer, 0, bytesRead);
                string[] lines = serialBuffer.Split('\r', '\n');
                // Don't process the last part, because it's not terminated yet
                for (int i = 0; i < (lines.Length - 1); i++)
                {
                    if (lines[i].Length > 0)
                        ProcessLine(lines[i]);
                }
                serialBuffer = lines[lines.Length - 1]; // keep last part
            }
        }

        static void ProcessLine(string line)
        {
            bool unsolicitedMessageReceived = false;
            lock (expectedEchoLock)
            {
                if (line == expectedEcho)
                {
                    expectedEchoReceived.Set();
                }
                else
                {
                    unsolicitedMessageReceived = true;
                }
            }
            if (unsolicitedMessageReceived)
            {
               // Process unsolicited/unexpected messages
            }
        }

        /// <summary>
        /// Send a command and wait for echo
        /// </summary>
        /// <param name="command">The command to send</param>
        /// <returns>True when echo has been received, false on timeout.</returns>
        static bool SendCommand(string command)
        {
            lock (expectedEchoLock)
            {
                expectedEchoReceived.Reset();
                expectedEcho = command;
            }
            port.Write(command);
            return expectedEchoReceived.WaitOne(5000); // timeout after 5 seconds
        }
    }
}
like image 139
huysentruitw Avatar answered Nov 19 '22 11:11

huysentruitw