Here is my situation, when I check the checkbox, my application freezes but still works. Which means that it is still able to recognize data sent through the serial port; for testing purposes it just exits the application.
If I comment out line 45 ("pipe = arduino.ReadLine();" see screenshot below) meaning that it no longer has to "ReadLine()", I am able to un-check the box. However now when I try to re-check the box, I get an error message saying "Access to the port 'COM5' is denied"
I assume that the code cannot continue because it is trying to "ReadLine()" when nothing has been sent through yet. However I do not have an explanation for being denied access to the COM port; rather than me trying to open the port when its already opened.
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
SerialPort arduino = new SerialPort();
arduino.BaudRate = 9600;
arduino.PortName = comboBox1.Text;
string pipe;
if (checkBox1.Checked)
{
checkBox1.Text = "Listening...";
arduino.Open();
pipe = arduino.ReadLine();
if (pipe == "S\r")
{
//System.Diagnostics.Process.Start("shutdown", "/f /r /t 0");
System.Windows.Forms.Application.Exit();
}
else
{
checkBox1.Text = "Start";
}
}
}
The SerialPort class manages system resources and, when such sensitive objects are involved, the class usually implements the IDisposable interface to allow those system resource to be released immediately to the system.
Your code forgets to close the SerialPort, so, the next time your user action cause a call to this event handler the port is in use by your own first action.
Fortunately, there is an easy way to ensure proper closing and disposing of such objects and it is the using statement
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
checkBox1.Text = "Listening...";
using(SerialPort arduino = new SerialPort())
{
arduino.BaudRate = 9600;
arduino.PortName = comboBox1.Text;
string pipe;
arduino.Open();
pipe = arduino.ReadLine();
if (pipe == "S\r")
{
//System.Diagnostics.Process.Start("shutdown", "/f /r /t 0");
//System.Windows.Forms.Application.Exit();
}
} // Here the port will be closed and disposed.
}
else
{
checkBox1.Text = "Start";
}
}
Related
I am building a program in C# to be used in one of my course at a college to demonstrate how Asynchronous connections work using RS-232 and two computers connected together. My course is not about programming, but data networks, so the connectivity is what I am looking for.
picture 1 - sample layout of GUI using Visual Studio 2015
One of the features I want to implement in my program is to show how a Master-slave, simplex connection works (i.e. the program can choose between been a master to send input from the keyboard; or slave to only receive information and print it on a textbox).
What I have already is the capability of initializing the serial port with specific characteristics (baud rate, data bits, stop bits, etc). This features are selected using combo boxes from the GUI, and assigned to the port when the user clicks a button to "open the port".
What I don't know is how to create the "slave" part of the program. My idea of what I could do is, after you choose the program to be "slave", you open the port waiting for some sort of flag or event to trigger when the input buffer has data stored.
I've been reading several forums and I can't find anything similar to what I need. I have, however, tested multiple alternatives that I believed would bring me closer to what I need with little to no result. I come to ask for an idea of what I could be doing wrong, or suggestions on how to tackle this problem. The problematic lines are bolded (or 2 stars ( * ) ):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
static SerialPort _PuertoSerial;
public frmSerialComm()
{
InitializeComponent();
getAvailablePorts();
}
//---------------------------------my functions--------------------------------------
void getAvailablePorts()
{
string[] ports = SerialPort.GetPortNames();
cmbPortList.Items.AddRange(ports);
}
void activatePort()
{
//Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
{
_PuertoSerial.PortName = cmbPortList.Text;
_PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
_PuertoSerial.RtsEnable = true;
_PuertoSerial.DtrEnable = true;
_PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);
if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
else { _PuertoSerial.Parity = Parity.None; }
if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
else { _PuertoSerial.StopBits = StopBits.One; }
if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
else { _PuertoSerial.Handshake = Handshake.None; }
_PuertoSerial.ReadTimeout = 500;
_PuertoSerial.WriteTimeout = 500;
_PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
**_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
}
else
{
txtRecieve.Text = "Input selection missing 1 or more characteristics";
}
}
**
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort testing = (SerialPort)sender;
txtRecieve.AppendText(testing.ReadExisting()); //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
}
**
void enableDisableGUI(bool[] input)
{
grpConnection.Enabled = input[0];
grpCharacteristics.Enabled = input[1];
btnOpenPort.Enabled = input[2];
btnClosePort.Enabled = input[3];
txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
}
//----------------------------C# objects / functions--------------------------------------
private void btnOpenPort_Click(object sender, EventArgs e)
{
try
{
_PuertoSerial = new SerialPort();
activatePort();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
bool[] format = { false, false, false, true};
enableDisableGUI(format);
}
private void btnClosePort_Click(object sender, EventArgs e)
{
_PuertoSerial.Close();
bool[] format = { true, true, true, false};
enableDisableGUI(format);
}
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
_PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
}
private void btnClearTxts_Click(object sender, EventArgs e)
{
txtRecieve.Clear();
txtSend.Clear();
}
} //class closes
} //program closes
I am not an experienced programmer, I just want to create something useful for my students. Any constructive criticism will be highly appreciated.
I don't have any definitive answers for you. You code looks like it should provide what you need once you get past the two possible glitches.
I think you should attach your SerialDataReceivedEventHandler BEFORE
you call _PuertoSerial.Open().
It may have no effect since event handlers can normally be enabled/disabled dynamically, but I base the advice on the following comment taken from the .Net source code for SerialPort on MSDN.
// all the magic happens in the call to the instance's .Open() method.
// Internally, the SerialStream constructor opens the file handle, sets the device control block and associated Win32 structures, and begins the event-watching cycle.
The "object reference" error might be resolved by removing the
static modifier from your DataReceivedHandler. If not, or if that
static modifier is necessary for some reason, then perhaps the
txtRecieve control has a private modifier which needs to be changed
to internal or public. You should be able to use Visual Studio in
debug mode to step into the InitializeComponent() method and see
where txtRecieve is being instantiated.
Well, I believe that I needed to read more. This is how I solved the problem (if this is not the real solution, at least is working for now):
I moved the "SerialDataReceivedEventHandler" line before the _PuertoSerial.open();
I followed the suggestions from this article:
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true
So my funtions (one existings + a new one) look like this:
void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
printReceivedText(_PuertoSerial.ReadExisting());
}
private void printReceivedText(string text)
{
if (this.txtSend.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(printReceivedText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtRecieve.AppendText(text);
_PuertoSerial.DiscardInBuffer();
}
}
For now seems to be working fine. The final testing will come when I connect another terminal and see the program interacting with each other.
This question already has an answer here:
Reading from serial port asynchronously using Async await method
(1 answer)
Closed 6 years ago.
I have a program which relies on data being sent through a serial port. I would like to monitor when data is sent through with SerialPort.ReadLine(). My problem is that I use check boxes which freeze when checked because the SerialPort.ReadLine() method blocks until it receives some data. I have also tried using ReadTimeout() but it did not work; perhaps because I did not use it correctly.
I've posted my code for this event below. What I would like this method to do is to monitor my serial port when the checkbox is checked, and then stop monitoring the serial port when I un-check the box. My current situation with my code is that it freezes upon checking the box. All help is appreciated.
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked)
{
checkBox1.Text = "Listening...";
using (SerialPort arduino = new SerialPort())
{
arduino.BaudRate = 9600;
arduino.PortName = comboBox1.Text;
arduino.Open();
string pipe = arduino.ReadLine(); //loop here
if (pipe == "S")
{
//System.Diagnostics.Process.Start("shutdown", "/f /r /t 0");
System.Windows.Forms.Application.Exit();
}
else
{
checkBox1.Text = "Start";
}
}
}
}
Consider using the SerialPort.DataReceived event instead of blocking.
I use this code to initialize a serial port in C# :
serialPort.PortName = cboCOMPort.Text;
serialPort.BaudRate = Convert.ToInt32(cboBaudRate.Text);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Handshake = Handshake.None;
serialPort.WriteTimeout = 500;
serialPort.ReadTimeout = 500;
serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceived);
serialPort.Open();
then use this code to read data from serial port :
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
}
and write :
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
My problem : when a device (example a switch) connects to the serial port and executes a command I have written, my program has to wait for the switch finishes executing this command before writing a new command to the serial port. How to check if the switch is finished? (when finished, the switch will send some message contain the keywords like 'completed', 'finished',...). I have tried to use this code but not work :
while(true)
{
if(serialPort.ReadLine().Contains("completed"))
serialDataSend(nextCommand);
}
Sorry for not clearly explaining my problem.
**Example my problem : **
I connect to the switch using serial port and use my program to read/write data. I want to copy a large file from server to the switch using this command : cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz. I use my program to write this command to the serial port and the switch executes this command. The file is very large so the program need to wait the file copied completely before sending the next command. When finished, the switch show message "Completed". That is my problem : how to check the copy process completed to write the new command.
I will try to approach your problem. Probably will need to rephrase as more information might come from you :)
As I understand it you have more than 1 command that you are sending to the device. Something like a command-list.
As you already have the event for data reception you could also use it to verify whether the completed keyword has been send and set a flag.
EDIT: The flag you can use for the while-wait-loop as you already do.
Your program will wait there until your device confirms the completion and jump out of the loop. Then you need to reset the flag for the next waiting loop.
public class DeviceCommunication
{
bool FlagToProceed = false;
private void MainJob()
{
// send command 1...
SerialDataSend("cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz.");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
// send command 2 ...
SerialDataSend("WhatEverComesNext");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
}
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
// check whether affirmation has been received and open the gates
if(strDataReceived.Contains("completed"))
{
FlagToProceed = true;
}
}
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
}
This waiting technique is not a very good one! It is just the closest to your posted code. If you want to use it, I would suggest to have a timer running and break out of the while - loop if timeout is reached. Otherwise you might keep stuck in the while loop if the device decides not to confirm with "complete" or data just gets lost during transmission.
Another possibility to solve your problem could be to apply an asynch / await approach.
EDIT: be ware of case sensitivity! "Complete" & "complete" is not the same for the Contains method!
You need to use a state machine and delegates to achieve what you are trying to do. See the code below, I recommend doing all this in a separate thread other then Main. You keep track of the state you're in, and when you get a response you parse it with the correct callback function and if it is what you are expecting you move onto the next send command state.
private delegate void CallbackFunction(String Response); //our generic Delegate
private CallbackFunction CallbackResponse; //instantiate our delegate
private StateMachine currentState = ATRHBPCalStateMachine.Waiting;
SerialPort sp; //our serial port
private enum StateMachine
{
Waiting,
SendCmd1,
Cmd1Response,
SendCmd2,
Cmd2Response,
Error
}
private void do_State_Machine()
{
switch (StateMachine)
{
case StateMachine.Waiting:
//do nothing
break;
case StateMachine.SendCmd1:
CallbackResponse = Cmd1Response; //set our delegate to the first response
sp.Write("Send first command1"); //send our command through the serial port
currentState = StateMachine.Cmd1Response; //change to cmd1 response state
break;
case StateMachine.Cmd1Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.SendCmd2:
CallbackResponse = Cmd2Response; //set our delegate to the second response
sp.Write("Send command2"); //send our command through the serial port
currentState = StateMachine.Cmd2Response; //change to cmd1 response state
break;
case StateMachine.Cmd2Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.Error:
//error occurred do something
break;
}
}
private void Cmd1Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.SendCmd2;
}
else
{
currentState = StateMachine.Error;
}
}
private void Cmd2Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.Waiting;
backgroundWorker1.CancelAsync();
}
else
{
currentState = StateMachine.Error;
}
}
//In my case, I build a string builder until I get a carriage return or a colon character. This tells me
//I got all the characters I want for the response. Now we call my delegate which calls the correct response
//function. The datareceived event can fire mid response, so you need someway to know when you have the whole
//message.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string CurrentLine = "";
string Data = serialPortSensor.ReadExisting();
Data.Replace("\n", "");
foreach (char c in Data)
{
if (c == '\r' || c == ':')
{
sb.Append(c);
CurrentLine = sb.ToString();
sb.Clear();
CallbackResponse(CurrentLine); //calls our correct response function depending on the current delegate assigned
}
else
{
sb.Append(c);
}
}
}
I would put this in a background worker, and when you press a button or something you can set the current state to SendCmd1.
Button press
private void buttonStart_Click(object sender, EventArgs e)
{
if(!backgroundWorker1.IsBusy)
{
currentState = StateMachine.SendCmd1;
backgroundWorker1.RunWorkerAsync();
}
}
Background worker do work event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (backgroundWorker1.CancellationPending)
break;
do_State_Machine();
Thread.Sleep(100);
}
}
Hello friends have a form in C # that reads data from a serial device connected, my problem is that I even changing form of the method myPort.DataReceived still running and receiving data. There's no way I close the connection with the serial port because the method does not stop excutar. I've tried a command to zip it when I change my form but it crashes when you try to run the myPort.Close, I believe that is why the myPort.DataReceived still running, so I removed the code and it continues myPort.Close open in another form. I think my solution would be to stop the myPort.DataReceived to then close connection, but can not find way to do this.Below is an excerpt from my code:
namespace EntradaFinalCliente
{
public partial class ConsultaSerial : Form
{
string SerialString;
private SerialPort myport;
public ConsultaSerial()
{
InitializeComponent();
abrirSerial();
lerDados();
}
public void abrirSerial()
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = SerialPort1;
myport.DataReceived += myport_DataReceived;
}
private void lerDados()
{
if (myport.IsOpen == false)
{
try
{
myport.Open();
}
catch
{
return;
}
}
}
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
SerialString = myport.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
private void Analisa(object sender, EventArgs e)
{
checarSerial();
}
And this is my closing the form button:
private void button1_Click (object sender, EventArgs e)
{
myPort.Close ();
this.Hide ();
var form1 = new Form1 ();
form1.Closed + = (s, args) => this.Close ();
Form1.Show ();
}
The issue you have it that once the event has been triggered, your application would have entered the function myport_DataReceived. The function will continue to execute regardless of whether the port has been closed. If the port has been closed, the function would execute for the last time. Waiting for 100ms makes it worse. So my advice is to remove the wait and put a try catch statement around the code to make the thread terminate cleanly.
Furthermore, it is better if you use the sender to read the incoming data than using the member myPort because the sender is the one that fires the event. It also helps to remove confusion when you open two or more ports.
It is also advised that the body of DataReceived event handler function should be kept to minimum. Only do what you need to get the data out. You can then store the data in memory and do more complicated handling somewhere else using the stored data.
private void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100); // Why do you need to wait for 100 ms? If the event reaches here, it will have the data to read. Can remove?
try
{
SerialPort sp = (SerialPort)sender;
SerialString = sp.ReadExisting();
this.Invoke(new EventHandler(Analisa));
}
catch (Exception ex)
{
// Do something else
Console.WriteLine(ex.Message);
}
}
I'm trying to open and close a serial port with one button click event. But it always hangs whenever it hits the serialport.close part. Why?
private void btn_auto_Click(object sender, EventArgs e)
{
try
{
myport = new SerialPort();
myport.BaudRate = 9600;
myport.PortName = cb_portname.Text;
myport.Open();
myport.DataReceived += new SerialDataReceivedEventHandler(myport_DataReceived2);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}
void myport_DataReceived2(object sender, SerialDataReceivedEventArgs e)
{
try
{
in_data = myport.ReadLine();
this.Invoke(new EventHandler(displaydata_event2));
}
catch (Exception)
{
}
}
private void displaydata_event2(object sender, EventArgs e)
{
string inStr;
inStr = in_data;
if (inStr.Length == 18)
{
int indexOfSpace = inStr.IndexOf(':');
string Temp = inStr.Substring(indexOfSpace + 1);
txtData2.Text = Temp;
}
if (txtData2.Text != "")
{
myport.Close(); //===== ALWAYS HANGS AT THIS PART =====
MessageBox.Show("STOPPED");
}
}
So, it always hangs under the if txtData2 not equals part.
Is it due to it requires a button action for a serialport to close and it cannot auto close? Thanks in advance.
Looking at the source code for the SerialPort class, and in particular for its associated SerialStream class, it appears that the Close() method will block waiting for handlers of any raised events to complete.
Your handling of the received data seems a bit suspect in any case, in that you only even bother to look at the received data if the received line is exactly 18 characters long, as well as in that you are using an instance field to pass data between two methods (very bad idea).
But most likely the biggest issue here, the one causing the deadlock, is that you are calling the SerialPort.Close() method before the DataReceived event handler has completed. Don't do that. Fix your code so that handling received data is a completely independent operation from actually closing the serial port, so that the former can complete before you attempt the latter.