We are trying to read data written by an external device (weighing scale in this case) connected to serial port using .Net serial port class.
First we initialize the serial port as below:
InitializeSerialPort()
{
if ((serialPort != null) && (serialPort.IsOpen))
{
serialPort.Close();
serialPort.Dispose();
serialPort = null;
}
serialPort = new SerialPort("COM2", 9600, Parity.None, 8,
StopBits.One) { Handshake = Handshake.None };
serialPort.DataReceived += serialPort_DataReceived;
serialPort.NewLine = "\r";
}
We are using background worker thread to poll the device on continuous interval by sending a command(understood by the weighing scale) on the serial port. As soon as we send the command the device connected to serial port reacts with a response output. We call ReadLine API of SerialPort class to get the data present on the serial port written by the device in the DataReceived event as shown in the code snippet below :
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
data = serialPort.ReadLine();
}
catch(System.IO.IOException ex)
{
//since serial port reading threw an error so there is no value to be parsed hence exit the function.
return;
}
//if no error then parse the data received
}
I'm using System.IO.Ports.SerialPort class of .Net framework 4.0. I can see a number of people posting this issue on other forums but with no specific resolution. Some of them terming .Net Serial port class as buggy which has not been fixed by Microsoft till date. One of the forums where this error is mentioned is here
I also tried the solution posted here but of no help. I need some input if any one else has come across this issue or its resolution.
We were able to solve this problem by locking the code inside serialPort_DataReceived method.
Object lockObject = new Object();
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock(lockObject)
{
try
{
data = serialPort.ReadLine();
}
catch(System.IO.IOException ex)
{
//since serial port reading threw an error so there is no value to be parsed hence exit the function.
return;
}
}
//if no error then parse the data received
}
We had set the polling interval to poll the device connected on serial port as 10 seconds. Possibly the entire code present inside serialPort_DataReceived method was sometimes taking more than 10 seconds. We were not able to exactly establish this fact as it was not happening every time may be.
So we locked the entire piece of code inside serialPort_DataReceived method using lock keyword in C# to ensure that the new execution for new data received from serial port doesn't start unless the older reading hasn't finished. The issue got resolved after implementing this code on trial and error basis. Hope this helps others as well if they come across such an issue.
Related
I have an issue with opening an STMicro USB virtual COM port.
When I plug the device into my PC, the COM port appears as it should, and the Windows Device Manager indicates it is working properly.
I have a C# program on the PC which selects and opens this port.
However, in about 1 in 10 attempts, the PC program sticks on the port.open() command, and after about half a minute, returns with the error "The semaphore timeout period has expired".
I have written a tiny C# program that does nothing more than open the port. This still gives the behaviour noted.
public partial class Form1 : Form
{
SerialPort port = new SerialPort();
string portName = "COM1"; // Give it a default to start with
public Form1()
{
InitializeComponent();
// Populate the COM port selector combobox with available port names
cmbPortSelect.Items.Clear();
string[] activePorts = SerialPort.GetPortNames();
foreach (string availablePort in activePorts)
{
cmbPortSelect.Items.Add(availablePort);
}
// Declare the serial port
port = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One);
port.ReadTimeout = 100;
}
private void cmbPortSelect_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbPortSelect.SelectedIndex != -1)
{ // It will get set to -1 (i.e. no selection) in the catch below - we don’t want this selected item change to do anything
if (port.IsOpen) port.Close();
port.PortName = (string)cmbPortSelect.SelectedItem;
System.Threading.Thread.Sleep(50);
try
{
port.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
cmbPortSelect.SelectedIndex = -1; // Clear the selected item box
}
}
}
}
If instead of using my C# program to open the port, I use the communications program PuTTY, it works every time.
In addition, if I plug in a device with an FDTI USB virtual COM port, it also works every time.
I'm using Windows 7, with the STMicro VCP drivers ver 1.3.1, but the same behaviour occurs with Windows 10 and the generic Microsoft drivers, which STMicro recommend we use.
There is a version 1.5.1 drivers for Windows 7, but when I installed them, it reported that they had installed correctly, but the Device Manager still reported ver 1.3.1.
Has anyone noted any similar behaviour?
That is seemed to be a timing issue. Try to increase your delay from 50 to, say, 200 ms and check the difference. As the doc says: The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly., sadly, there is no actual time specified.
I have been struggling with commincations speeds with some code.
So i want to increase the baud rate for both the code & Arduino. But if i leave the 9600 baud rate, the data stops sending & reciving properly.
So i set up a simple test program.
Arduino Code:
void setup()
{
Serial.begin(9600);
Serial.setTimeout(10);
}
void loop()
{
if (Serial.available())
{
String Data = Serial.readStringUntil('#');
if (Data == "Test")
{
Serial.println("Recived");
}
}
delay(1);
}
c# Code:
SerialPort Port = new SerialPort("COM4", 9600);
Port.Open();
if (Port.IsOpen)
{
Port.Write("Test#");
System.Threading.Thread.Sleep(1000);
String Read = Port.ReadExisting();
Port.Close();
}
So running that String Read comes back with "Recived\r\n".
Change the baud rate to 19200 and it comes back with "".
Any ideas why this is occuring?
Edit: If I use the Arduino IDE's Serial Monitor Program, this works just fine regardless of baudrate used. Its as soon as i use c# that it that this issue occurs. Which rules out hardware issues I believe.
Try sending a character at a time from the PC and use Serial.read() to read a character into a buffer in the arduino. Sometimes sending the whole text from PC at high baud rate is too much for the arduino to handle.
Thankyou for you inputs.
Think i have found a solution, although not to clear about why.
I think it was due to the Serial.Avalible() Command. Appears i needed to send though some data first to make it register the port is open.
So modifying my C# code to this: Works
SerialPort Port = new SerialPort("COM4", 9600);
Port.Open();
if (Port.IsOpen)
{
Port.Write("#");
Port.Write("Test#");
System.Threading.Thread.Sleep(1000);
String Read = Port.ReadExisting();
Port.Close();
}
Thanks a lot
i am trying to get data from fingerprint scanner through c# application, but before the fingerprint can send, a my whole code executes.
I tried using delay function and System.Threading.Thread.Sleep(1000), so it can get data before the next step executes, but it all seems futile.
Could any one please provide any other option?
I am using "SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)" to get data.
This code works perfectly for me:
port = new SerialPort(port, 9600, Parity.None, 8, StopBits.One);
port.Open();
port.DiscardOutBuffer();
port.DiscardInBuffer();
port.DataReceived += OnScan;
void OnScan(object sender, SerialDataReceivedEventArgs args)
{
SerialPort port = sender as SerialPort;
string line = port.ReadExisting();
// etc
}
Unfortunately waiting for a serial port data in C# is tricky, there is nothing like poll().
There is SerialPort.DataReceived which takes functions to be called on incoming data. So you assign there a function to trigger an arbitrary event. Your another function — the one to actually wait — should wait for this event.
Below is a simple example, it is commented, but in short: the TestFunc initializes and opens a serial port (in particular assigns the DataReceived). The Proxy() is a function that will be called every time a data arrived, it triggers an event. And WaitForAData() indeed waits for the event that will be triggered by Proxy() when a data appears. Note the lock(){}s, without them surrounding Monitor's functions it won't work properly.
It's just an example, you would probably want to remake WaitForAData() function to trigger an exception in case of timeout. And to add a boolean variable in case if the Proxy() was triggered before you began waiting, then serial port already have data. But I tested it (cause I need such a function now ☺), and it works.
namespace MyNamespace
{
class MySerial
{
///A condition variable that signals when serial has a data
private System.Object SerialIncoming;
public MySerial()
{
SerialIncoming = new Object();
}
/**
* A proxy function that will be called every time a data arrived
*/
private void Proxy(Object unused1, SerialDataReceivedEventArgs unused2)
{
Console.WriteLine("Data arrived!");
lock (SerialIncoming)
{
Monitor.Pulse(SerialIncoming);
}
}
/**
* Waits for a data for the time interval Timeout
* \param Timeout a timeout in milliseconds to wait for a data
* \returns true in if a data did arrived, and false else
*/
public bool WaitForAData(int Timeout)
{
lock (SerialIncoming)//waits N seconds for a condition variable
{
if (!Monitor.Wait(SerialIncoming, Timeout))
{//if timeout
Console.WriteLine("Time out");
return false;
}
return true;
}
}
/* Just a test function: opens a serial with speed, and waits
* for a data for the «Timeout» milliseconds.
*/
public void TestFunc(string serial, int speed, int Timeout)
{
SerialPort ser = new SerialPort(serial);
ser.BaudRate = speed;
ser.DataReceived += Proxy;
ser.Open();
if (WaitForAData(Timeout))
Console.WriteLine("Okay in TestFunc");
else
Console.WriteLine("Time out in TestFunc");
}
}
}
UPDATE: the problem wasted ½ of my day, so I hope I will save someone's time: the code above won't work in mono (but works in MS implementation) because serial port events are not supported as of writing these words.
If this is a Console application, you can use things like Console.ReadLine() etc. after calling the appropriate function of the COM Port component to start listening asynchronously. If this is a WinForms application. The message loop will of course keep showing your current form. In that case you can call asynchronous listening function in the Form_Load event or behind a button click.
The key point here is that you should call the asynchronous version of the listener function. There is no need to use delays or timers in that case.
Why not make a global marker (bool), that marks if you received anything and make a while(!marker) {} loop and you change the marker in the SerialPort_datareceived subrutine?
The thread.sleep might make you miss the SerialPort data sending?
The serial port is working in a separate thread. Therefore the serialPort_DataReceived event is fired from this thread.
So if your program only starts the serial port and then your main exits, you never receive the event. This is true if you have a console application.
When using a forms application, it keeps the form and the main thread alive until the user closes it.
To start I am coding in C#. I am writing data of varying sizes to a device through a socket. After writing the data I want to read from the socket because the device will write back an error code/completion message once it has finished processing all of the data. Currently I have something like this:
byte[] resultErrorCode = new byte[1];
resultErrorCode[0] = 255;
while (resultErrorCode[0] == 255)
{
try
{
ReadFromSocket(ref resultErrorCode);
}
catch (Exception)
{
}
}
Console.WriteLine(ErrorList[resultErrorCode[0] - 48]);
I use ReadFromSocket in other places, so I know that it is working correctly. What ends up happening is that the port I am connecting from (on my machine) changes to random ports. I think that this causes the firmware on the other side to have a bad connection. So when I write data on the other side, it tries to write data to the original port that I connected through, but after trying to read several times, the connection port changes on my side.
How can I read from the socket continuously until I receive a completion command? If I know that something is wrong with the loop because for my smallest test file it takes 1 min and 13 seconds pretty consistently. I have tested the code by removing the loop and putting the code to sleep for 1 min and 15 seconds. When it resumes, it successfully reads the completion command that I am expecting. Does anyone have any advice?
What you should have is a separate thread which will act like a driver of your external hardware. This thread will receive all data, parse it and transmit the appropriate messages to the rest of your application. This portion of code will give you an idea of how receive and parse data from your hardware.
public void ContinuousReceive(){
byte[] buffer = new byte[1024];
bool terminationCodeReceived = false;
while(!terminationCodeReceived){
try{
if(server.Receive(buffer)>0){
// We got something
// Parse the received data and check if the termination code
// is received or not
}
}catch (SocketException e){
Console.WriteLine("Oops! Something bad happened:" + e.Message);
}
}
}
Notes:
If you want to open a specific port on your machine (some external hardware are configured to talk to a predefined port) then you should specify that when you create your socket
Never close your socket until you want to stop your application or the external hardware API requires that. Keeping your socket open will resolve the random port change
using Thread.Sleep when dealing with external hardware is not a good idea. When possible, you should either use events (in case of RS232 connections) or blocking calls on separate threads as it is the case in the code above.
Pretty simple question this time around. I have an application that communicates with another copy of the application on another machines. One application sends a pretty constant stream of data, the other receives it.
The code to send data looks like this (where serialPort is an instance of the System.IO.Ports.SerialPorts class in C# .Net 2.0):
private void bgDataWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e){
try{
string tempStr = Convert.ToString(String.Format("{0:0.000000}", data));
serialPort.Write(tempStr); // Write "data" out to 6 decimal places
}
catch (TimeoutException){ }
catch (InvalidOperationException err){ // Port is obstructed or closed
this.Invoke((MethodInvoker)delegate{
MessageBox.Show(this, "Couldn't send wireless data:\n\n" +
err.ToString(), "NanoMETER - Wireless Error (Data)",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Global.remoteEna = false;
serialPort.Close();
usingBT = false;
});
}
}
It's called on a timer. The receive code is even more straightforward:
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) {
string buffer = serialPort.ReadExisting();
HandleInput(buffer);
}
Data gets sent and handled and it's all fine and dandy, but there's some unwanted choppiness where it's either not reliably sending data at a constant rate, or it's not picking up everything. I'm not sure if this can be fixed in my code, or if it's just the nature of having a few slow machines and a possibly shakey bluetooth connection. Any suggestions?
It's not uncommon for interns to be assigned to converting old code to a newer platform.
There are a few improvements you can make.
1) The following strategy is good when the bytes sent through the port is meant to be interpreted in blocks, such as commands. Do you have some sort of protocol? Something that dictates the format of the message you are sending. For instance, a specific delimiter to indicate the beginning and the length of the upcoming command. This allows you to quickly determine if the command was only half sent, or if there were missing bytes. Even better is to add a CRC at the end.
2) Instead of reading on a timer, base yourself on the events flagged by your serialport object. Here's an example of what i use:
//OnReceive event will only fire when at least 9 bytes are in the buffer.
serialPort.ReceivedBytesThreshold = 9;
//register the event handlers
serialPort.DataReceived += new SerialDataReceivedEventHandler(OnReceive);
serialPort.PinChanged += new SerialPinChangedEventHandler(OnPinChanged);
In the code above, i set a threshhold of 9, you should change that to whatever fits your context. Also, the Pinchanged event is something good to monitor, it will allow you to quickly identify if the cable has been disconnected. There is more on this, regarding CTSChanged but you can look it up if you are interested.
Lastly, if this doesn't help you get a little further, show an example of the problem that occured so the peolpe here can give you more help.