this is my first interaction here! I'm learning C# by myself, there will be a lot of errors beside to the bug i'm trying to get rid of, please be patient :)
I'm developing a very basic app that let you interact with serial ports, something like the tool integrated in Arduino.
There is a button that is meant to Connect/Disconnect, it lanches/stops a Backgroundworker that keeps the form updated with new data. I've tryed to move .close(); every where in the code, but nothing changed, when I try to open it again it catches the Exception. And other apps con't access too. I think that I simply don't know what I'm doing :)
Can you help me releasing the resource?
The code involved in this operation:
private void ConnectB_Click(object sender, EventArgs e)
{
if (!connected)
{
int baud = Convert.ToInt32(baudRate.SelectedItem.ToString());
COMport = new SerialPort(COMpick.SelectedItem.ToString(), baud, Parity.None, 8, StopBits.One);
try
{
COMport.Open();
connected = true;
ConnectB.Text = "Disconnect";
}
catch (ArgumentOutOfRangeException)
{
MessageBox.Show("Baud rate not valid.");
connected = false;
}
catch (ArgumentException)
{
MessageBox.Show("Port name not valid.");
connected = false;
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Access denied, try close applications that may using the port.");
connected = false;
}
if (backWorker.IsBusy != true)
{
backWorker.RunWorkerAsync();
}
}
else
{
connected = false;
backWorker.CancelAsync();
ConnectB.Text = "Connect";
}
}
private void backWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
if (worker.CancellationPending == true)
{
e.Cancel = true;
break;
}
else if(COMport.IsOpen)
{
// Get data and print it in the form
try
{
inbox = COMport.ReadLine() + '\n';
}
catch (InvalidOperationException) { }
//Scroll down the form, passing something useless to make it work
worker.ReportProgress(inbox.Length);
}
}
}
private void backWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.COMport.Close();
this.COMport.Dispose();
}
Thank you!
Related
I am working on a program that receives data from a serial port and displays this on a GUI. I am very new to C# and this is a part of a project I am working on. I have tried to look for code for the past few weeks and watched every tutorial on this matter, all to fail. I am still very new to programming and OOP.
The issue I am having is that the received data is not being pasted in the display box. I have verified that the serial port works using Putty so that cannot be the issue.
Edit: Quick update, upon learning how to use the meter I am working with and closer inspection of the user manual, I discovered that I was able to connect to the meter using serialportname.open(). The issue was that I was not requesting data. For example, by writing "READ?" into the meter, a reading would be returned.
I see that you're not using the DataReceived event.
It could be an approach to what you're trying to achieve; it will trigger each time your serial port receives data, so you could use it to insert into the textbox1
private void serialPort1_DataReceived(object sender,SerialDataReceivedEventArgs e)
{
string Data= serialPort1.ReadLine();
displayToTxt(Data);
}
private void displayToTxt(string Data)
{
BeginInvoke(new EventHandler(delegate
{
textBox1.AppendText(Data);
}));
}
I used delegate to avoid thread errors.
first you need to add EventHandler
SerialPort1.DataReceived += new SerialDataReceivedEventHandler(ReceiveData);
then get data and update UI
public void ReceiveData(object sender, SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(30);
SerialPort _SerialPort = (SerialPort)sender;
int _bytesToRead = _SerialPort.BytesToRead;
byte[] recvData = new byte[_bytesToRead];
_SerialPort.Read(recvData, 0, _bytesToRead);
this.BeginInvoke(new SetTextDeleg(UpdateUI), new object[] { recvData });
}
private void UpdateUI(byte[] data)
{
string str = BitConverter.ToString(data);
textBox1.Text += str;
}
I know this is old. But since it is still showing up in searches, I thought I would add my answer.
As mentioned in another answer, you need to assign a handler to the serial port.
public void OpenPorts()
{
foreach (string nm in SerialPort.GetPortNames())
{
SerialPort sp = new()
{
PortName = nm,
ReadBufferSize = 2048,
DiscardNull = true,
RtsEnable = true,
DtrEnable = true,
ReceivedBytesThreshold = 1
};
try
{
//This should throw an exception if the port is already open.
sp.Open();
sp.DataReceived += DataReceived;
//Make sure the buffer is empty
if (sp.BytesToRead != 0)
{
sp.DiscardInBuffer();
}
}
catch (Exception ex)
{
//Handle the exception here
}
}
public void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
while ((sender as SerialPort).BytesToRead > 0)
{
SerialBuffer += (sender as SerialPort).ReadExisting();
}
(sender as SerialPort).DiscardInBuffer();
}
catch (InvalidOperationException ex)
{
_ = MessageBox.Show("exception thrown at DataReceived.", "Crashed", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
I am currently trying to build a windows forms app that gets sensor data from an arduino via the serial com.
when checking in the arduino IDE the data gets writen into the serial port correctly.
But i can't figure out how to read the data via c#.
class Program
{
static SerialPort SP;
static void Main(string[] args)
{
SP = new SerialPort();
SP.PortName = "COM7";
SP.BaudRate = 9600;
SP.Handshake = System.IO.Ports.Handshake.RequestToSend;
SP.Open();
while (true)
{
Console.WriteLine(DateTime.Now.ToString() + " : " + SP.ReadLine());
}
}
}
My guess is that the Port is not properly set up, but i have no idea what i am missing.
The Goal is just to receive strings from the arduino, i do not necessarily need to send any data to the arduino.
edit: i am working with an arduino micro
Did you close Arduino IDE?
You need to add a wait code before reading from the port
Below is a working example:
private SerialPort _currentPort = new SerialPort("COM7", 9600);
private readonly object _sync = new object();
public bool Open()
{
_currentPort.Encoding = Encoding.UTF8;
_currentPort.DtrEnable = true;
_currentPort.ReadTimeout = 2000;
try
{
if (!_currentPort.IsOpen)
lock (_sync)
{
if (_currentPort.IsOpen)
return true;
_currentPort.Open();
System.Threading.Thread.Sleep(1500);
}
}
catch (Exception e)
{
//_localLogger?.Error($"{_currentPort.PortName}, {e.Message}", e);
return false;
}
return _currentPort.IsOpen;
}
public bool Subscribe()
{
try
{
if (Open())
{
_currentPort.DataReceived += CurrentPortOnDataReceived;
return true;
}
return false;
}
catch (Exception e)
{
//_localLogger?.Error($"{_currentPort.PortName}, {e.Message}", e);
return false;
}
}
private void CurrentPortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!_currentPort.IsOpen)
{
//_localLogger.Info($"{_currentPort} is closed");
Open();
}
Console.WriteLine(_currentPort.ReadExisting());
}
So I've recently come across a what I think is a deadlock that only happens sometimes. After I close my form the app just hangs. I'm using SerialPorts for communication. I've read this answer and this answer which both suggest I use BeginInvoke() instead of Invoke() for most methods unless I need to wait for a control to update.
I don't know if this makes sense but I'm trying to figure out how to make the bug happen every time I close the form, so that if its resolved by using BeginInvoke I'm sure its gone. My code to close the port is
private void closeDevicePort()
{
if (devicePort != null)
{
try
{
setMode("D0");
toggleAllBtns();
devicePort.Close();
devicePort = null; //sometimes doesn't get past here
Console.WriteLine("closing++++ here");
}
catch (Exception ex) {
Console.WriteLine(ex);
}
//devicePort = null;
}
}
which gets called through a GUIForm_FormClosed event on FormClosed. How do I make this happen every time to test that changing to BeginInvoke() actually works for my winforms app?
Also here is my DataReceivedHandler where I set the devicePort (which I didn't think was making anything explicitly multi-threaded)
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort1 = (SerialPort)sender;
if (serialPort1.IsOpen)
{
try
{
string message = serialPort1.ReadLine();
if (devicePort == null)
{
devicePort = serialPort1;
}
}catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
I have a plotData() function which plots data in realtime. Is using BeginInvoke() instead of Invoke() fine here too?
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort1 = (SerialPort)sender;
if (serialPort1.IsOpen)
{
try
{
string message = serialPort1.ReadLine();
Console.WriteLine(message);
if (message.Substring(0, 1) == "!")
{
oldTime = newTime;
//checkPortsTimer.Stop();
//checkPortsTimer.Interval = 1500;
//checkPortsTimer.Start();
}
string flag = message.Substring(1, 1);
string command = message.Substring(2, 1);
string package = message.Substring(3);
switch (flag)
{
case "S":
switch (command)
{
case "C": //ready mode
setMode("SC");
if (devicePort == null)
{
addDevicePort(serialPort1);
}
break;
}
break;
case "D": //data stream
float[] data = Array.ConvertAll(package.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), float.Parse);
txtData_Update(data);
plotData(data);
break;
default:
Console.WriteLine("not a recognised command. Try Again");
break;
}
}
catch (Exception ex)
{
Console.WriteLine("in data received exception handler");
Console.WriteLine(ex);
}
}
}
I am creating a Windows Form application, where it is connecting to a device through bluetooth. I am able to send commands to the device and I am receiving the data continuously. The problem I am facing is that I am not able to show the continuous data in the text box. The text box only shows the first line of characters the application is receiving. Here is my code:
CONNECT BUTTON ACTION:
private void btnConnect_Click(object sender, EventArgs e)
{
if (listBox.SelectedItem != null)
{
lblProgress.Text = "";
btnStart.Enabled = true;
cBoxAvailablePorts.Enabled = cBoxAvailableBaudRates.Enabled = true;
try
{
int pos = listBox.SelectedIndex;
deviceInfo = array.ElementAt(pos);
if (pairDevice())
{
Thread thread = new Thread(() => connectThread());
thread.Start();
}
else
{
MessageBox.Show("Pair failed!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
else
{
MessageBox.Show("Please connect to a device!");
}
}
THREAD ACTION
private void connectThread()
{
//BluetoothClient client = new BluetoothClient();
bc.BeginConnect(deviceInfo.DeviceAddress, serviceClass, this.connectCallBack, bc);
}
CALLBACK ACTION:
private void connectCallBack(IAsyncResult result)
{
//BluetoothClient client = (BluetoothClient)result.AsyncState;
try
{
if (bc.Connected)
{
MessageBox.Show("Connected!");
}
else
{
MessageBox.Show("Connection Failed!");
}
}
catch (Exception)
{
MessageBox.Show("Not able to identify Bluetooth devices! Please try again.!");
}
}
START BUTTON ACTION:
Here I send a command "S".
In button action I call sendMessage("S").
The function that is called is shown below:
public void sendMessage(string msg)
{
try
{
if (bc.Connected)
{
Stream stream = bc.GetStream();
stream.ReadTimeout = 1000;
StreamWriter streamWriter = new StreamWriter(stream);
streamWriter.WriteLine(msg);
streamWriter.Flush();
// Read operation
StreamReader streamReader = new StreamReader(stream);
string result = streamReader.ReadLine();
txtResult.Text = result;
}
else
{
MessageBox.Show("Sending failed!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I wrote the StreamReader part in a loop, and it gave me Socket Exception.
I also tried to get the data from Serial Port and used DataReceived event just in case, but still it didn't help.
Any help would be appreciated.
Thank you!
OKAY! I solved the problem. Without getting in trouble with 32feet library (though it is fun to code with 32feet), I thought to make communication through serial port. I connected the device with my laptop and got to know the outgoing COMPORT in bluetooth setting of my laptop. The two-way communication can only be done through outgoing COMPORT, not the incoming COMPORT.
Suppose the outgoing COMPORT is COM12 and the baud rate that I have set is 9600.
So here is my code:
public delegate void updateDelegate(string text);
private updateDelegate objDelegate;
private SerialPort serialPort;
public View() // constructor
{
InitializeComponent();
this.WindowState = FormWindowState.Normal;
this.StartPosition = FormStartPosition.CenterScreen;
this.objDelegate = new updateDelegate(getText);
serialPort = new SerialPort("COM12", 9600);
serialPort.Handshake = Handshake.None;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.DtrEnable = true;
serialPort.RtsEnable = true;
}
START BUTTON ACTION
private void btnStart_Click(object sender, EventArgs e)
{
sendData("S");
}
// SEND COMMAND
public void sendData(string msg)
{
try
{
if (!serialPort.IsOpen)
{
serialPort.Open();
//serialPort.Close();
}
if (serialPort.IsOpen)
{
serialPort.Write(msg);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// READ DATA
public void readData()
{
try
{
serialPort.DataReceived += SerialPort_DataReceived;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string res = serialPort.ReadExisting();
Thread.Sleep(500);
txtResult.Invoke(this.objDelegate, new object[] {res});
}
public void getText(string text)
{
txtResult.Text = text;
}
I hope this will help someone! Thank you!!!
I had done a quick-search and still cant find the answer for my question .
Serial port variable
int close;
SerialPort _serialPort = new SerialPort("COM1", 1200, Parity.None, 8, StopBits.One);
Serial port delegate code
private void si_DataReceived(string data)
{
if (close == 1)
{
string d1 = data.Trim().Replace("TT", "");
d1 = d1.Replace("Tl3", "");
txtCan.Text = d1;
}
else
{
return;
}
}
private delegate void SetTextDeleg(string text);
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _serialPort.ReadLine();
this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
}
Connect button code
private void button4_Click(object sender, EventArgs e)
{
if (button4.Text == "Close Connection")
{
progressBar1.Style = ProgressBarStyle.Continuous;
progressBar1.MarqueeAnimationSpeed = 0;
close=0;
try
{
string d1 = txtCan.Text;
double r1 = Convert.ToDouble(d1) * 10;
txtCan.Text = Math.Round(r1, 3).ToString();
button4.Text = "Open Connection";
button1.Enabled = true;
readOnly(true);
}
catch (Exception ex)
{
button4.Text = "Open Connection";
button1.Enabled = true;
progressBar1.Style = ProgressBarStyle.Continuous;
progressBar1.MarqueeAnimationSpeed = 0;
MessageBox.Show("Cant connect.");
}
}
else
{
progressBar1.Style = ProgressBarStyle.Marquee;
progressBar1.MarqueeAnimationSpeed = 30;
close = 1;
txtCan.Text = "";
txtCan.Focus();
readOnly(false);
button1.Enabled = false;
button4.Text = "Close Connection";
try
{
if(!_serialPort.IsOpen)
{
_serialPort.Handshake = Handshake.None;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
_serialPort.Open();
}
}
catch
{
button4.Text = "Open Connection";
button1.Enabled = true;
readOnly(true);
progressBar1.Style = ProgressBarStyle.Continuous;
progressBar1.MarqueeAnimationSpeed = 0;
MessageBox.Show("Cant connect.");
}
}
}
My solution to close a com port, but it will fail if I re-open a form in countable times (like twice or triple times then it will crash)
private void myForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (_serialPort.IsOpen)
{
e.Cancel = true;
Thread CloseDown = new Thread(new ThreadStart(CloseSerialOnExit));
CloseDown.Start();
}
}
private void CloseSerialOnExit()
{
try
{
backgroundWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
}
this.BeginInvoke(new EventHandler(NowClose));
}
private void NowClose(object sender, EventArgs e)
{
this.Close(); //now close the form
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
_serialPort.Close();
}
string data = _serialPort.ReadLine();
There is no lack of potential trouble with this statement. You have a pretty nasty failure-mode on your hands when this call doesn't complete. It doesn't have to, you could turn off the serial device at just the wrong time, jerk the cable, get the line terminator corrupted and you'll have a pretty big headache.
The SerialPort.Close() method cannot complete until any of the event handlers stop running. This can significantly increase the odds for deadlock. Using the ReadLine() method is fine, but then you also have to set the ReadTimeout property to, say, 10 seconds so you can be sure that accidents don't turn into undebuggable problems. Let the TimeoutException either terminate your program or set a "serial port is dead" status variable.
Further improve the odds for success by disassociating the lifetime of the SerialPort with the lifetime of your form. In general the only sensible thing to do is to create the instance at program startup and not close it until your program terminates. Just keep it in a separate class, it can be static to make it easy to use.
And, unintuitively, it is now no longer important to call Close() at all. The finalizer will take care of it.
Also make sure that, if you use a USB emulator, to never disconnect it without going through the Safely Remove Hardware tray icon. USB drivers generally deal with a surprise removal very poorly. Some are so poor that they make the device disappear even though you have it opened. And trying to close it always fails, you cannot cleanly exit your program anymore.