c# SerialPort DataReceivedHandler not being called for every event. - c#

I am developing a desktop application which interacts with Arduino via SerialPort.
I want my application to respond every time arduino writes something on the serial port.
But DataReceivedHandler does not get triggered for all the events.
Here is the connection code -
public static bool connectToArduino()
{
foreach (string name in SerialPort.GetPortNames())
{
SerialPort serialPort = new SerialPort(name);
Console.WriteLine(name);
if (serialPort.IsOpen == false)
{
serialPort.PortName = name;
try
{
Console.WriteLine("Openning serial port.");
serialPort.WriteTimeout = 5000;
serialPort.ReadTimeout = 5000;
serialPort.BaudRate = 115200;
serialPort.Open();
serialPort.Write(Constants.CONNECT_APP_STRING);
Console.WriteLine("Written to serial port.");
string reply = serialPort.ReadLine();
//string reply = serialPort.ReadTo("\n");
Console.WriteLine("Reply is: " + reply);
Console.WriteLine("Read from serial port.");
if (reply == Constants.CONNECT_ACK)
{
Console.WriteLine(name);
Console.WriteLine("Connected with arduino controller");
//serialPort.DataReceived += DataReceivedHandler;
serialController = serialPort;
return true;
}
else if (reply+"\n" == Constants.CONNECT_ARDUINO_STRING) {
serialPort.WriteLine(Constants.CONNECT_ACK);
serialController = serialPort;
MessageBox.Show("Connected with arduino controller");
return true;
}
else
{
serialPort.Close();
}
}
catch (TimeoutException)
{
Console.WriteLine("Timeout occured.");
serialPort.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "ERROR");
Console.WriteLine(ex);
}
}
}
MessageBox.Show("Connection with Arduino failed!");
return false;
}
After this I set the data received handler -
SerialComm.serialController.DataReceived += DataReceivedHandler;
Now, the problem is that sometimes DataReceivedHandler is not being triggered.
I am not able to find a pattern in this. It randomly just skips some events.
Any idea what is going wrong?

Looking at the documentation you can find in the remarks:
The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.
If the event is in your case too unreliable to be trusted catch every incoming information from your device, then you could use an extra thread which would run in the background constantly checking the BytesToRead property and/or constantly reading with ReadExisting which is non-blocking.

Related

Serial COM just echoes what I send instead of data stream. C#

I have a thermometer that has an RS232 connection, so I bought U-Port adapter to USB, per the manual you send it the string "Tcrlf" and it starts outputting data. Tried on PuTTy and it works like a charm.
I'm trying to automate some software with the data stream I am getting, however I am having problems communicating it, I have tried a few snippets from various tutorials around the webs but when I send it the same string via my app it just echoes it back and doesnt stream the data.
This is my Connect Button (after selecting COM port)
private void probeConnectBtn_Click(object sender, EventArgs e)
{
//Connect to the NIST-Reference Probe, using Omega HH42 settings:
if (refProbeCOMPort.SelectedIndex == -1)
{
MessageBox.Show("No COM Port selected for NIST-Reference Probe");
return;
}
if (!connectedreferenceProbePort.IsOpen)
{
connectedreferenceProbePort.DataBits = HH42DataBits;
connectedreferenceProbePort.BaudRate = HH42BaudRate;
connectedreferenceProbePort.PortName = HH42PortName;
connectedreferenceProbePort.Parity = Parity.None;
connectedreferenceProbePort.ReadTimeout = 600;
connectedreferenceProbePort.WriteTimeout = 800;
connectedreferenceProbePort.StopBits = StopBits.One;
connectedreferenceProbePort.DataReceived += new SerialDataReceivedEventHandler(probeDataReceived);
}
try
{
Console.WriteLine("Attempting to open port");
if (!connectedreferenceProbePort.IsOpen)
{
connectedreferenceProbePort.Open();
if (connectedreferenceProbePort.IsOpen)
{
Console.WriteLine("Port Opened, sending RTS");
connectedreferenceProbePort.RtsEnable = true;
connectedreferenceProbePort.WriteLine("Tcrl");
}
}
else
{
Console.WriteLine("Port is already open");
}
}
catch (Exception ex)
{
MessageBox.Show("Error opening/writing to Serial Port:: " + ex.Message, "Fatal Error!");
}
}
That's the connect and "attempting" to start the stream, then I have the datareceived part:
(Per the HH42 manual, after receiving the RTS signal, it sends a ">" character meaning that it's ready to listen).
private void probeDataReceived(object sender, EventArgs e)
{
string dataReceived = "";
Console.WriteLine("Data Incoming");
connectedreferenceProbePort.DiscardOutBuffer();
try
{
dataReceived = connectedreferenceProbePort.ReadExisting();
Console.WriteLine("Recevied :" + dataReceived);
} catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
if (dataReceived.Contains(">"))
{
Console.WriteLine("> Detected, attempting to write Tcrl");
connectedreferenceProbePort.Write("Tcrl");
}
}
This is my output from the console and a screenshot of PuttY:
Attempting to open port
Port Opened, sending RTS
Data Incoming
Recevied :
> Tcrl
> Detected, attempting to write Tcrl
Data Incoming
Recevied :Tc
Data Incoming
Recevied :rl
When you type "Tcrl" and press return in PuTTY, what PuTTY is actually sending are the bytes "T", "c", "r", "l", followed by a carriage return (CR) and a linefeed (NL).
I believe the manual is telling you to send "T", CR, LF, which in C# terms would be the string "T\r\n".

Closing a USB serial port leaves the port unavailable

My app uses USB based serial ports to connect to physical hardware devices. I can open any valid USB port and communicate with the external devices. However, when I close the connection, the USB port is left in some sort of indeterminate state for some time, and during that time further attempts to reconnect result in the "Access to port "COM--" is denied" error. However, after some few seconds, attempting to reconnect is successful. How can I determine WHEN the USB port will again support a new connection?
The code looks like this:
private void Setup(string Port)
{
bool ValidPort = false;
int CloseSleep = 10;
_PortName = Port;
_PortType = this;
string[] AvailablePorts = SerialPort.GetPortNames();
foreach(string aPort in AvailablePorts)
{
if (aPort == _PortName)
{
// The required port is listed in the list of available ports)
ValidPort = true;
break;
}
}
if (ValidPort)
{
try
{
if (_ThePort != null)
{
_ThePort.Close();
_ThePort.DataReceived -= ReceivedDataEventHandler;
while(CloseSleep-- > 0)
System.Threading.Thread.Sleep(100);
_ThePort.Dispose();
_ThePort = null;
}
}
catch (Exception ex)
{
EMS_Config_Tool.ModalDialog md = new EMS_Config_Tool.ModalDialog("Closing Port: " + ex.Message, "System Exception");
md.ShowDialog();
}
System.IO.Ports.SerialPort TheNewPort = new System.IO.Ports.SerialPort(Port, 38400);
// Setup the event handlers from Tx and Rx
Handler.DataOutEvent += CommsSender;
TheNewPort.DataReceived += ReceivedDataEventHandler;
TheNewPort.DataBits = 8;
TheNewPort.Parity = Parity.None;
TheNewPort.Handshake = System.IO.Ports.Handshake.None;
TheNewPort.StopBits = System.IO.Ports.StopBits.One;
// We will try 3 times to open the port, and report an error if we fail to open the port
try
{
TheNewPort.Open();
}
catch (Exception)
{
System.Threading.Thread.Sleep(1000);
try
{
TheNewPort.Open();
}
catch (Exception)
{
System.Threading.Thread.Sleep(1000);
try
{
TheNewPort.Open();
}
catch (Exception ex)
{
EMS_Config_Tool.ModalDialog md = new EMS_Config_Tool.ModalDialog("Opening Port: " + ex.Message, "System Exception");
return;
}
}
}
The final catch statement is where the error about Access being denied is issued. Note my attempt to retry opening the port 3 times doesn't really help. If I leave the port alone for about 5 to 10 seconds and retry calling the Setup method it succeeds immediately.
As #Neil said, there are many issues. The best thing to do, in my point of view, is to put the search in a loop, and as soon as the port can be opened, it will be.
I used to do like this :
public Task WaitingPort()
{
while (port is null)
{
port = CheckPort();
}
}
private SerialPort CheckPort()
{
string[] listPort = SerialPort.GetPortNames();
foreach(string namePort in listPort)
{
SerialPort port = new SerialPort(namePort, 9600);
if (!port.IsOpen)
{
try
{
port.Open();
port.ReadTimeout = 1500;
string data = port.Readline();
// I programmed my device to send an "A" until it receives
// "777" to be able to recognize it once opened
if (data.Substring(0, 1) == "A")
{
port.ReadTimeout = 200;
port.Write("777"); // to make it stop sending "A"
return port;
}
else
{
port.Close();
}
}
catch (Exception e1)
{
port.Close();
}
}
}
return null;
}
Of course, this is just some kind of a template which you have to reshape to your use
I have amended my code to use a constrained loop to give it a better chance to work, which it usually does. I was hoping that there was a better way to do it, as I tend to have pretty impatient users who will be posting defect reports if they have to wait 5 or 10 seconds to make a connection....
// We will try several times to open the port, upto 10 times over 5 seconds, and report an error if we finally fail to open the port
try
{
TheNewPort.Open();
}
catch (Exception ex)
{
RetryOpenTimer.Interval = 500;
RetryCount = 10;
RetryOpenTimer.Elapsed += new System.Timers.ElapsedEventHandler(RetryOpenTimer_Elapsed);
WaitForOpen = true;
RetryOpenTimer.Start();
while (WaitForOpen && RetryCount > 0)
{
System.Threading.Thread.Sleep(500);
}
if (WaitForOpen)
{
EMS_Config_Tool.ModalDialog md = new EMS_Config_Tool.ModalDialog("Opening Port: " + ex.Message, "System Exception");
return;
}
}
...
void RetryOpenTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
RetryOpenTimer.Stop();
RetryOpenTimer.Elapsed -= RetryOpenTimer_Elapsed;
try
{
if (RetryCount-- > 0)
{
TheNewPort.Open();
WaitForOpen = false;
}
else
return;
}
catch (Exception)
{
RetryOpenTimer.Start();
RetryOpenTimer.Elapsed += RetryOpenTimer_Elapsed;
}
}

Send Data During Serial Port Data received Event

I am using COM4 Serial Port to both send and receive data. I have a demand request that comes in - I verify that the request is of mydatastring = myrequeststring - this works fine. I am trying to reply data through the com port during this event handler but of course Access is denied. How can I accomplish sending of data after receiving my request..??
void scScale_OnReceiving(object sender, DataStreamEventArgs e)
{
if( e.Response == myrequeststring )
{
scScale.Transmit(this.data);
}
}
EDIT
relevant parts of scScale - it is a SerialPort Client class - basically encapsulates the SerialPort details.
Relevant code:
public bool OpenConn()
{
try
{
if (_serialPort == null)
_serialPort = new SerialPort(_port, _baudRate, Parity.None);
if (!_serialPort.IsOpen)
{
_serialPort.ReadTimeout = -1;
_serialPort.WriteTimeout = -1;
_serialPort.Open();
if (_serialPort.IsOpen)
{
serThread.Start(); /*Start The Communication Thread*/
IsOpen = true;
}
else
{
IsOpen = false;
}
}
}
catch (Exception ex)
{
return false;
}
return true;
}
public void Transmit(string packet)
{
_serialPort.Write(packet); // 0, packet.Length);
}
public void Transmit(byte[] packet)
{
_serialPort.Write(packet, 0, packet.Length);
}
public int Receive(byte[] bytes, int offset, int count)
{
int readBytes = 0;
if (count > 0)
{
readBytes = _serialPort.Read(bytes, offset, count);
}
return readBytes;
}
private void OnSerialReceiving(byte[] res)
{
if (OnReceiving != null)
{
OnReceiving(this, new DataStreamEventArgs(res));
}
}
SCSCALE is this code at link..
https://roofman.wordpress.com/2012/09/13/fast-serial-communication-for-c-real-time-applications/
I do not know why this works but it does; when the Serial Port was instantiated - I created the event handler and that did fire the event, but I was unable to send a reply through the port during handling of that event.
However if I subscribe to the event after the port is open, the event will fire and I will have access to the port to send a reply.
While I did not post my complete code - I do verify that the port is open before transmitting, and if not I open the port.
Now instead of immediately subscribing to the receive event - I open the port and then subscribe to the OnReceive event.
The reason I changed the code to subscribe after the Port was open is because I was reading on SO and some other sites about strange behaviours caused by subscribing before the port was open.
So in short Subscribe to the receive event after the port is open.

Cannot read data from Arduino bluetooth serial data in WinRT

I am using Arduino UNO connected with bluetooth module. I have this below code in Arduino, which listen to specific input and glow the LED.
int LED= 13;
char input;
void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println(">> START<<");
Serial.flush();
}
void loop()
{
Serial.flush();
if(Serial.available()>0)
{
input= Serial.read();
if(input=='1')
{
Serial.write(1);
Serial.println('a');
digitalWrite(LED, HIGH);
}
else if(input=='0')
{
Serial.println("OFF");
digitalWrite(LED, LOW);
}
else
{
Serial.println("NO INPUT");
Serial.println(input);
}
}
}
From Windows 8.1 (XAML/C#) application I am sending data through bluetooth. And it works perfectly as expected. But I am also trying to read data from Arduino. For that I have below code in C#.
socket = new StreamSocket();
connectAction = socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName, SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
Task.Run(() =>
{
ListenForMessagesAsync();
});
The ListenForMessagesAsync method supposed to keep listening the dataReader. But it just waiting for infinite time and never returns.
private async Task ListenForMessagesAsync()
{
while (reader != null)
{
try
{
uint sizeFieldCount = await reader.LoadAsync(1);// taskLoadLength.GetResults();
if (sizeFieldCount != 1)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message.
uint messageLength = reader.ReadByte();
uint actualMessageLength = await reader.LoadAsync(messageLength);
if (messageLength != actualMessageLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message and process it.
string message = reader.ReadString(actualMessageLength);
}
catch (Exception ex)
{
}
}
}
What am I doing wrong here?
You need a return after a successful ReadString and of course you need to do something with the message.

Detect Arduino port in C#

I'm trying to open each port and send <mccon> serially, for which my microcontroller will respond <connected>\n after which the C# code must exit the for each loop.
I'm having a problem at the serialPort.PortName = str; line. After two iterations, it does not continue further.
I tried doing this manually too. I made a drop down and selected ports one by one. After the second port, it does not allow to change the serial Port. But in case I select within two tries, it works fine.
I know OOP in C++. But I'm new to C#. I'm not sure why the loop fails.
public Form1()
{
InitializeComponent();
send_button.Enabled = false;
//Availabe COM ports
SerialPort tmp;
foreach(string str in SerialPort.GetPortNames())
{
tmp = new SerialPort(str);
if (tmp.IsOpen == false)
{
serialPort.PortName = str;
try
{
//Open serial port
serialPort.Open();
serialPort.BaudRate = 9600;
serialPort.WriteTimeout = 10;
serialPort.ReadTimeout = 10;
serialPort.Write("<mccon>");
readtxt.Text = serialPort.ReadTo("\n");
if (readtxt.Text == "<connected>")
{
send_button.Enabled = true;
port_combobox.Enabled = false;
break;
}
else
{
serialPort.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
I don't have multiple serial ports, but when I compiled and executed your code, I noticed that you are not closing the serial port if it errors during the read. I suggest you modify your code as follows:
SerialPort tmp;
foreach (string str in SerialPort.GetPortNames())
{
tmp = new SerialPort(str);
if (tmp.IsOpen == false)
{
serialPort.PortName = str;
try
{
//open serial port
serialPort.Open();
serialPort.BaudRate = 9600;
serialPort.WriteTimeout = 10;
serialPort.ReadTimeout = 10;
serialPort.Write("<mccon>");
String s = serialPort.ReadTo("\n");
if (s == "<connected>")
{
break;
}
else
{
serialPort.Close();
}
}
catch (TimeoutException)
{
serialPort.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
I'm not sure the effect on changing the port name while it's open, but it could well cause the issues you are seeing.
Could you execute this code and return what it shows? It might show some information about the Arduino port which you can then use for the serialport.
Add a reference to System.Management and also add the using, and then try the code:
using System.Management;
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PnPEntity");
foreach (ManagementObject queryObj in searcher.Get())
{
if (queryObj["Caption"].ToString().ToUpper().Contains("ARDUINO"))
{
Console.WriteLine(queryObj["Caption"]);
foreach (PropertyData pd in queryObj.Properties) { Console.WriteLine(pd.Name + " : " + pd.Value); }
}
}
}
catch (ManagementException e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();

Categories

Resources