Synchronizing Serial Port Reading/Writing [duplicate] - c#

This question already has answers here:
Problem with serial port data receive in C#
(6 answers)
Closed 5 years ago.
I'm attempting to write a c# program in Visual Studio 2010 that communicates with a micro controller via a serial connection. I can read and write to the port just fine, I am just unsure of how to have the send method wait until all of the data from the previous send command has been received before it executes. I have implemented the data received event handler such that it determines when the proper amount of data that had been requested has been received on the serial port. I just need to know how to cause that to tell the send method the port is free.
I had planned on using a mutex, but I believe the problem is not due to multi-threading. The same thread is sending strings out on the serial port one after another and the data being received in response to the first request is conflicting with the second request.
Also, if the communication is being done by one thread, would having that thread wait cause the data received event handler to not execute?
(both methods are in the same class)
My send data method:
//method for sending data
public void send(String s)
{
sp.Write(s + "\r");
}
My data received event handler:
//data received event handler
private void dataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string tmp;
tmp = sp.ReadExisting();
//put all received data into the read buffer
readBuffer += tmp;
//add various flag checks to determine end of transmission
if (firmwareFlag)
{
if (readBuffer.EndsWith("\n"))
{
firmwareFlag = false;
//trigger event to tell object owner data is ready
dataReady(this, new CustomEventArgs(readBuffer, "firmware"));
readBuffer = "";
}
}
else if (parameterFlag)
{
if (System.Text.RegularExpressions.Regex.IsMatch(readBuffer, "K90", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
parameterFlag = false;
dataReady(this, new CustomEventArgs(readBuffer, "parameters"));
readBuffer = "";
}
}
else
{
dataReady(this, new CustomEventArgs(readBuffer, null));
readBuffer = "";
}
}

I would use just a bool variable in the class, and a timer. When timer ticks, you check that bool and send the data if it is allowed.
boolean isAllowed;
DispatcherTimer tmr1;
//...etc

I have the same problem on the past and I solved this way:
I added SendAndWaitResponse method which receives the buffer to be sent by the port, the expected lenght of the response, a timeout in seconds(just in case), and a callback, when the expected response has been received:
//System.IO.Ports.SerialPort port;
//port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
void SendAndWaitResponse(String buffer, int expectedLenght, int timeoutInSeconds, Action<String> callback)
{
TimeSpan _timeout = new TimeSpan(0, 0, 0, timeoutInSeconds);
//may not be necesary
if (!string.IsNullOrEmpty(buffer) && buffer != "" && port != null)
{
//Remove current dataReceived event handler, as we will manually manage it for now, will restore later!
this.port.DataReceived -= port_DataReceived;
if (!this.port.IsOpen) this.port.Open(); // open the port if it was closed
this.send(buffer); // send buffer, changed port.write(buffer) so it now usses your own send
DateTime startToWait = DateTime.Now; //start timeout
bool isTimeout = false;
int totalRead = 0;
String read = "";
while (!(isTimeout) && totalRead < expectedLenght)
{
do
{
if (port.BytesToRead > 0)
{
read += (char)this.port.ReadByte();
totalRead++;
//you can add a System.Threading.Thread.Sleep(int); if all bytes come in parts
}
} while (port.BytesToRead > 0);
isTimeout = (DateTime.Now.Subtract(_timeout) >= startToWait);
}
this.port.DataReceived += port_DataReceived; // restore listener
callback(read); //invoke callback!!!
}
}

If you want to check if there are bytes with out send to the device (bytes in write buffer), so you can read BytesToWrite property.
SerialPort.BytesToWrite Property

Related

COM: Device only sends data when things are changing. How to deal with that?

So I want to connect to a device via Serial that only sends data when things are changing with the settings on the device (a measuring device).
I use C# and .Net's SerialPort.
I am able to read data and it looks kind of good. But there are a few problems I encountered.
I realized my reading- method (ReadExistingData()) so that it will constantly use the SerialDataReceivedEventHandler when there's new data.
Unfortunately, when I read it like that (probably because of varying package sizes) it will read very chaotically and thus I need to "catch" the first initiating byte (here it's 0xA5).
That means, I always check whether the byte I just received is a 0xA5 and if it is, I read the rest.
But I feel like that way I am missing some commands my device sends me and thats why I cant display the data consistently right, only from time to time.
On a side note: The device sends the device time and a value. The value is delayed and kind of unaccurate, but the time is always right on spot. The other parameters it sends are always weirded out and dont seem to be recognized and thus changed at all.
To display data I use the console for testing purposes, and the whole construct seems to be very reactive to Console outputs.
Here's a little code snippet:
class Device
{
private int stdMessageLengthInBytes = 5;
public DeviceData processedData;
byte[] dataBuffer;
int receivedMessages = 0;
public Device()
{
// Initialize BaseClass (SerialPort derivative)
this.port = new System.IO.Ports.SerialPort();
// Initialize Device
this.data = new byte[stdMessageLengthInBytes];
this.processedData = new P8005_Data();
dataBuffer = new byte[stdMessageLengthInBytes];
}
// Is supposed to read the data from serial port
protected override void ReadExistingData()
{
// Check whether buffer is empty -> Try to catch a 0xA5
if (dataBuffer[0] == 0x00)
{
port.Read(dataBuffer, 0, 1);
}
// If no 0xA5 was catched, restart
if (dataBuffer[0] != 0xA5)
{
dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
return;
}
// Read next byte -> Command byte
port.Read(dataBuffer, 1, 1);
// If command was empty, restart
if (dataBuffer[1] == 0x00)
{
dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
return;
}
// If its time that is communicated: Read 3 bytes
if (dataBuffer[1] == 0x06)
{
// 4 ms delay seems to be needed otherwise it wont function correctly somehow
System.Threading.Thread.Sleep(5);
port.Read(dataBuffer, 2, 3);
// Write buffer to actual raw data byte array
this.data = dataBuffer;
dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
}
// Otherwise: Just read 2 bytes
System.Threading.Thread.Sleep(5); // Needed delay
port.Read(dataBuffer, 2, 2);
// Write buffer to actual raw data byte array
this.data = dataBuffer;
dataBuffer = new byte[stdMessageLengthInBytes]; // Reset buffer
}
// Method called by SerialDataReceivedEventHandler
protected override void DataReceivedOnComPort(object sender, SerialDataReceivedEventArgs e)
{
bool valid = false;
ReadExistingData(); // Read data from COM- Port
lock (processedData)
{
switch (data[1]) // Check command byte
{
// Time (3 btyes)
case (0x06):
processedData.currentTime = String.Format("{0:D2}:{1:D2}:{2:D2}", DecodeBcd(data[2]), DecodeBcd(data[3]), DecodeBcd(data[4]));
valid = true;
receivedMessages++;
break;
// Value (2 bytes)
case (0x0D):
double val = 0;
val += DecodeBcd(data[2]) * 100;
val += DecodeBcd(data[3]);
val /= 10;
processedData.currentValue = val;
valid = true;
receivedMessages++;
break;
// ... here are various other hex- code that represent a command from the device (2 btyes)
default:
valid = false;
break;
}
}
// only to check when
if (valid)
{
Console.WriteLine("Received Valid Messages: {0}", receivedMessages);
ConsoleOutput();
}
}
}
On a note: The initialization of the port happens in another method from the base class and works fine.
Is there anything I am missing? How to deal with something like that? Are there any improvements that would help improving my performance? I thought about threading with locks, but I dont think that is the solution somehow... Or maybe everything is just a console problem?
EDIT:
I know changed my code (as #jdweng suggested) so that I put everything in a buffer (basically List<byte> mainBuffer. Then, I take all bytes in the buffer whenever its possbile and work with them, skimming it for 0xA5. When one is found, I read the command and determine how long the "message" has to be according to it (Time -> +3 bytes, Data -> +2 bytes, Other -> +1 byte). Then I can work off those messages (I put them into a List<byte[]>) and determine my output to my screen.
However, even after outsourcing the chopping up into messages and processing the messages, I still seem to either miss some messages, since some action are just not registered and have a big delay, or my processing is wrong. What I can think of is that because I lock my mainBuffer maybe some data isnt written to it.
Is this really this time critical? There is a software that comes with the device and it doesnt seem to have such big problems with delay and slightly wrong values...
Since you don't have the exact specs and/or an unreliable connection (which with serial data has to be expected) you need to sync to the 0xa5 at every message. I would just run every single byte you receive through a parser while keeping the state of the currently received message.
Make sure you validate your input since there are a bunch of things that can go wrong if you get messed up serial data. For example if there is an 0xa5 in the other message types, you might miss your next message. To prevent that I strongly recommend to either get to the specs if possible or code more logic based on data observations.
private const int MESSAGE_LENGTH = 5;
private const int VALUE_COMMAND = 0x0D;
private const int VALUE_SIZE = 4;
private const int TIME_COMMAND = 0x06;
private const int TIME_SIZE = 5;
private byte[] _message = new byte[MESSAGE_LENGTH];
private int _messagePos = 0;
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var data = new byte[_serialPort.BytesToRead];
_serialPort.Read(data, 0, data.Length);
foreach (var b in data)
{
_message[_messagePos] = b;
if (_messagePos == 0 && b != 0xa5)
continue;
++_messagePos;
if (_messagePos > 2) // if command byte present, process command of any size
ProcessCommand(_message[1]);
}
}
private void ProcessCommand(byte command)
{
if (_messagePos == VALUE_SIZE && command == VALUE_COMMAND)
{
// parse value...
_messagePos = 0;
}
else if (_messagePos == TIME_SIZE && _message[1] == TIME_COMMAND)
{
// parse time...
_messagePos = 0;
}
}

C# - Example for Handshaking with SerialPort class

I'm currently working on a project which requires me to communicate with an MBED through serial ports.
I've read the msdn site but I don't understand.
Can anyone teach me or show examples of how to use SerialPort.Handshake (XonXoff or RequestToSend)?
My main request how to set: If C# reads a certain string, then send data.
First of all i think that Handshaking is not what you need. Handshaking is a low level communication, to control when to send data or not. more about handshaking
My main request how to set: If C# reads a certain string, then send
data.
What you need is a continuous data reading or an event, and after certain string is read - to send some data.
Let me find some examples.
void connect(){
_port = new SerialPort();
// set port parameters
_port.DataReceived += portDataReceived; //set the data received event
_port.Open();
}
void portDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _port.ReadExisting();
if(data.Contains("data-start")) //check for the start data
{
CreateReply(); // your reply
}
}
You can read the data in a while loop, but take a note that an infinite loop executed in the main thread will block your program from responding.
void connectAndRead(){
_port = new SerialPort();
// set port parameters
_port.Open();
_stopReading = false;
var dataBuffer = "";
while(!_stopReading)
{
dataBuffer += _serialPort.ReadLine();
if(!dataBuffer.Contains("data-start")) //check for the start data
continue;
CreateReply(); // your reply
_stopReading = true; // exit loop
}
}

Delay in processing command from socket listener

I have a special socket listener, that works in it's thread. It's job to get commands from external program that updates database. When command comes on socket i am calling special method, that updates my application cache from database.
I have a problem, that is delay between sending command from external program and processing that command in my app (ASP .NET Application). Every day my app restarting at 4 a.m. and by the end of the day i have delay about 1-2 hours.
How i can reduce this delay?
You can find code of my listener below.
Thanks.
public delegate void OnECIGetCommand( string command );
public class ECIMain
{
protected Socket socket;
protected string ip;
protected int port;
private static ECIMain INSTANCE = null;
const int receivedDataSize = 250;
protected static byte[] buffer = new byte[ receivedDataSize ];
protected static StringBuilder sb;
protected static DoWorkEventHandler onCommand;
private ECIMain()
{
socket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
sb = new StringBuilder();
}
private void StartSocket()
{
sb.Clear();
socket.Listen(1);
socket.BeginAccept(null, receivedDataSize,
new AsyncCallback(AcceptReceiveDataCallback), socket);
}
private static void AcceptReceiveDataCallback(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
// End the operation and display the received data on the console.
byte[] Buffer;
int bytesTransferred;
Socket handler = listener.EndAccept(out Buffer,
out bytesTransferred, ar);
HandleBuff(bytesTransferred, Buffer);
// Create the state object for the asynchronous receive.
handler.BeginReceive(buffer, 0, receivedDataSize,
SocketFlags.None, new AsyncCallback(ReadCallback), handler);
}
private static void HandleBuff(int size, byte[] buff )
{
if (size > 0)
{
// There might be more data, so store the data received so far.
sb.Append(Encoding.ASCII.GetString(buff, 0, size));
// Check for end-of-file tag. If it is not there, read more data.
var content = sb.ToString();
int pos = -1;
if ((pos = content.IndexOf("</cmd>")) > -1)
{
// All the data has been read from the
// client.
pos += 6;
if( pos < content.Length )
content = content.Remove(pos);
var startPos = content.LastIndexOf("<cmd>");
if( startPos > -1 )
{
if (startPos > 0)
content = content.Remove(0, startPos);
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += onCommand;
worker.RunWorkerAsync(content);
}
sb.Remove(0, pos);
}
}
}
private static void ReadCallback(IAsyncResult ar)
{
// Retrieve the state object and the handler socket
// from the asynchronous state object.
Socket handler = (Socket)ar.AsyncState;
SocketError error;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar, out error );
if (error == SocketError.Success)
{
if (bytesRead > 0)
{
HandleBuff(bytesRead, buffer);
handler.BeginReceive(buffer, 0, receivedDataSize,
SocketFlags.None, new AsyncCallback(ReadCallback), handler);
}
else
{
handler.Disconnect(true);
INSTANCE.StartSocket();
}
}
else if (error == SocketError.Shutdown || error == SocketError.ConnectionReset)
{
INSTANCE.StartSocket();
}
}
public static string InitECI(int port, DoWorkEventHandler commandHandler)
{
if (INSTANCE == null)
{
INSTANCE = new ECIMain();
INSTANCE.port = port;
onCommand += commandHandler;
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList
.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
INSTANCE.ip = ipAddress.ToString();
try
{
INSTANCE.socket.Bind(localEndPoint);
}
catch (System.Net.Sockets.SocketException e)
{
if (e.SocketErrorCode == System.Net.Sockets
.SocketError.AddressAlreadyInUse)
{
//INSTANCE.socket.Bind(localEndPoint);
}
throw e;
}
INSTANCE.StartSocket();
}
return INSTANCE.ip;
}
public static void ShutDownECI()
{
if( INSTANCE.socket.Connected )
INSTANCE.socket.Disconnect(false);
INSTANCE.socket.Close();
}
}
When using the TCP Stack (either sending or receiving) one must look at the stack as it's own system, which is bullet proof and works very well... Most of these types of issues deal with the fact that the application layer is easily able to fire off as many async actions as it wants, but that doesn't mean the TCP Stack is going to be faster, especially if it's overwhelmed. What it means instead is that it will queue up and process tasks as it's able.
One of the symptoms of the stack being overwhelmed is the presence of many half session states visualized via the Netstat command. Anything that has the word "Wait" is an indicator of a half state. This occurs when one side posts a packet but the other doesn't respond right away. From there it's all downhill because TCP kicks in and attempts to keep the sessions alive by re-sending the same packet. Multiply this by the number of active sessions and the fact that TCP retries up to 10 times for each packet before timing out, you can see this is the last thing an overwhelmed stack needs.
Your situation could be that the network traffic is exceeding the capacity of the single Network adapter. This is usually solved by adding more network cards and using other means to do load balancing. One way is what they call DNS round robin and it's the least expensive. The other way is an F5 device.
Bottom line, it sounds like your network adapter is being overwhelmed.
Here's few things to check at both side of the wire.
Are all of the sockets being fully closed when the gig is up for each session?
Are you able to run a network monitor to review the overall loads... You typically want 60% or less utilization on any single network adapter.
If your loads are too high then you can talk to DNS folks about using round robin, and put in another card into the same server (it will have different address)
Sometimes it's due to lack of compression of the data being sent on the wire. You can investigate compression techniques too.
Sometimes switches go bad which give MAC-To-MAC connect-ability.
Improper router configuration can allow for re-tranmissions on the wire from different access points.
Incorrectly configured servers can also broadcast too much noise (total junk)
Wireless Access Points are notorious for going flaky they could be the source of noise too.
There's a lot to getting to the root of this type of issue, but hope that some of these ideas will help you.

C# how to return data from a eventhandler to a thread

The following functions runs as thread in my application. My function requests data from a serial device.
I use this code for my task:
private void request(ClsComSettingMain clsComSettingMain, CancellationToken token) //class for async data requests
{
Console.WriteLine("Debug: Hello from serialTask.");
string comPort = clsComSettingMain.comport;
int baudRate = clsComSettingMain.baudRate;
if (comPort != null && baudRate != 0)
{
SerialPort serialPort = new SerialPort(comPort, baudRate);
serialPort.ReadTimeout = 200;
serialPort.Open();
serialPort.DataReceived += new SerialDataReceivedEventHandler(readSerialIn); //register for SerialDataReceivedEvent
while (true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Debug: Serialthread cancelled.");
serialPort.Dispose(); // free all ressources for GC
return;
}
else
{
if (!pfcDataRequested) //check if data is already requested
{
Console.WriteLine("Debug: Requesting data from serial, send raw: 0xF0, 0x02, 0x0D");
serialPort.Write(adv_request, 0, 3); // Write byte array to serial port, with no offset, all 3 bytes
pfcDataRequested = true;
}
}
}
}
}
The eventhandler should read the serial data when available and returns it to the function request. And after that the request should request new data. But I don't understand how to make the thread wait for the eventhandler do finish and set the bool request back to false so a new request is send.
According to your provided code fragment, pfcDataRequested is a member of the class. So in your case you would "just" need to modify the readSerialIn callback method in a way, that at the end of the method the member pfcDataRequested is set to false again.
Like pfcDataRequested = false.

Serial port reading

I am trying to read 3 temperature devices using WinForms and the Modbus 485 protocol.
Basically I have to periodically write a command to each device, wait for response and when I get the response, process it. Each device has a unique communication adress. In order to periodically send the command I am using a timer.Timer1.interval=100;
This is how I am sending the command and where I am processing the response:
private void ProcessTimer_Tick(object sender, EventArgs e)
{
switch (tempState)
{
case TempTimerState.sendCommDevice1:
if (!tempSerial.IsOpen)
{
tempSerial.Open();
}
tempSerial.DiscardInBuffer();
communication.tempCommand[0] = 0x01; //device adress
communication.tempCommand[6] = 0xA5; //CRC
communication.tempCommand[7] = 0xC2; //CRC
tempSerial.Write(communication.tempCommand, 0, 8);
tempState = TempTimerState.recievedDevice1;
communication.waitTime = 0; //time to wait before throw a timeout exception
communication.dataRecievedTemp = false; //flag for response recieved
break;
case TempTimerState.recievedDevice1:
communication.waitTime++;
if (communication.dataRecievedTemp)
{
communication.waitTime = 0;
if(CheckCRC(communication.tempResponse)) //CRC checking
{
//process response
}
else
{
//handle CRC Failure error
}
}
if(commcommunication.waitTime>=maxWaitTime)
{
//handle Timeout exception
}
tempState=TempTimerState.sendCommDevice2;
break;
}
}
and so on for each device. This is my serialport data recieved event:
private void tempSerial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
sp.Read(communication.tempResponse, 0, sp.BytesToRead);
communication.dataRecievedTemp = true; //flag for data recieved
}
So my communication should be :
send command device1
recieve response device1
send command device2
recieve command device2
send command device3
recieve command device3
and then send command device1 again. The problem is that I get sometimes get communication timeout error and I know for sure that all the devices are responding very quick and every time. Since I had preset the sp.ReceivedBytesThreshold=8I started to get CRC errors too. My response should always be 8 bytes long.
I think the problem is in the serial port data recieved event, but I can't see what's the problem.
P.S. I have also tried to set the timer interval to 1000 miliseconds, but that didn't solve my problem
Relying on ReceivedBytesThreshold is very brittle, the show is over when you get out of sync once. Your code is also very vulnerable to other reasons that DataReceived may fire, you are not checking the e.EventType property. Which certainly can be SerialData.Eof for a binary protocol.
Just write robust code that doesn't depend on the EventType nor the number of available bytes. Like this:
private byte[] rcveBuf = new byte[8];
private int rcveLen;
private void tempSerial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
rcveLen += sp.Read(rcvebuf, rcveLen, rcveBuf.Length - rcveLen);
if (rcveLen == rcveBuf.Length) {
Array.Copy(rcveBuf, communication.tempResponse, rcveBuf.Length);
communication.dataRecievedTemp = true;
rcveLen = 0;
}
}
And reset rcveLen back to zero on a timeout. And make sure that the timeout isn't too low, you can lose many seconds if your program got swapped out, use 10 seconds to be safe.

Categories