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!
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
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With