I have the following C# program with a button called GetForceButton and a multiline textbox called ForceTextbox. Here is the code I have at the moment:
public Form1()
{
InitializeComponent();
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
serialPort1 = new System.IO.Ports.SerialPort(components);
serialPort1.PortName = "COM7";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
bool buttonpressed = false;
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadLine();
if (buttonpressed == true)
{
ForceTextbox.Text = indata + "\n";
}
else
{
ForceTextbox.Text = "No data received";
}
}
private void GetForceButton_Click(object sender, EventArgs e)
{
buttonpressed = true;
}
When I step through the code, indata is getting the value from the serialPort of "0.00\r" (including the speech brackets).
After stepping to the ForceTextbox.Text = indata + "\n"; line, an exception is being thrown up saying:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll. Additional information: Cross-thread operation not valid: Control 'ForceTextbox' accessed from a thread other than the thread it was created on.
What does that mean, or what am I doing wrong please?
You need to read this link.
The long and short of it is you need to make sure that you update GUI components on the same thread that started them. Mostly, this is done by the GUI thread.
You'll be using InvokeRequired as shown in that link.
You have to do this in C# all over the place unfortunately.
One other tutorial from Microsoft.
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 need to read from several serial ports at the same time.
I know I should use multithreading but yet I don't know how I should.
I can handle the event with "SerialDataReceivedEventArgs" assigning the ".DataReceived" event to the SerialPort object.
But when it's triggered I can't see which port triggered the event. So I don't know which ports should I read.
I tried to take it into a new thread assigning specific different names to the threads, but when I use this:
private void CheckSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
MessageBox.Show(Thread.CurrentThread.Name);
//this code runs on another thread. It is not running on the thread where the SerialPort was opened.
}
The same thing has happened if I triy this:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Invoke(new EventHandler(DisplayThreadText));
}
private void DisplayThreadText(object sender, EventArgs e)
{
//this code runs on the main thread. It is not running on the thread where the SerialPort was opened.
}
So my question is that how can I get, which port has triggered the given event?
Or should I use other methods? I don't know...
But if you can help me out I would appreciate that.
Sender object is the SerialPort.
public static void Main()
{
SerialPort mySerialPort = new SerialPort("COM1");
mySerialPort.BaudRate = 9600;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
mySerialPort.RtsEnable = true;
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
mySerialPort.Open();
Console.WriteLine("Press any key to continue...");
Console.WriteLine();
Console.ReadKey();
mySerialPort.Close();
}
private static void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
Console.WriteLine("Data Received:");
Console.Write(indata);
}
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.
first of all my issue:
I try to build a winform in a background worker. This Form class only include one webbrowser controll. The form will not build and jump out to main thread.
Im using c# with .NET 4.5 on Visual Studio 2013 Pro with WinForms
What i do:
Start Background worker
private void bt_dashboard_chat_Click(object sender, EventArgs e)
{
if (!this.bw_webchat.IsBusy)
{
this.bw_webchat.RunWorkerAsync(this.auth.getUserName());
}
}
The worker
{
String name = e.Argument as String;
DashBoard.Forms.Chat.DummyChat tmp = new Forms.Chat.DummyChat(name);
tmp.ShowDialog();
}
Form constructor
InitializeComponent();
this.wb_twitchchat.Url = new Uri("http://link.tld/" + name + "/chat");
this.Text = "Chat of " + name;
The issue. The thread jumps out at InitializeComponent(); on the line
this.wb_twitchchat = new System.Windows.Forms.WebBrowser();
This is the seconde line.
Have somebody any ideas why this happened? Other forms working fine in backgroundworker threads :/
Exception is catch by background worker itself. Use try/catch block around code you are mentioning "The worker".
Catched exception should be something like:
ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
Exception is based on code:
private void button1_Click(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.RunWorkerAsync();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
try
{
WebBrowser wb = new WebBrowser();
}
catch (Exception ex)
{
}
}
I've asked this question earlier today, but have refined my code so am putting a new question up here.
This is the code I have at the moment:
Arduino Code:
void setup()
{
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
Serial.begin(9600);
}
void loop()
{
if(Serial.available() > 0)
{
char letter = Serial.read();
if (letter == 'A')
{
digitalWrite(13,HIGH);
Serial.println("THE LED IS ON");
}
else if (letter == 'B')
{
digitalWriter(13,LOW);
Serial.println("THE LED IS OFF");
}
}
}
I have a C# program with an onButton, offButton, and textboxInterface. This is the code I have in C#.
C# Code:
using System.IO.Ports;
public partial class Form1: Form
{
public static System.IO.Ports.SerialPort serialPort1;
private delegate void LineReceivedEvent(string line);
public Form1()
{
InitizlizeComponent();
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
serialPort1 = new System.IO.Ports.SerialPort(components);
serialPort1.PortName = "COM7";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
}
private static void serialPort1_DataReceived(object sender, SerialDataEventReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
textboxInterface.Text = indata;
}
I think that is mostly right (?), the only error I am getting is as the last textboxInterface with an error coming up saying: *An object reference is required for the non-static field, method, or property 'Arduino_Interface.Form1.textboxInterface'*
Can someone please show me what stupid thing I'm doing...
First, remove static from the declaration of serialPort1_DataReceived. You need access to the form's instance fields so it cannot be static.
Second, this event will be raised on a background thread and you cannot update the UI from that thread. You will need to marshal the call to UI thread to update the textbox. Something like this:
private void serialPort1_DataReceived(object sender, SerialDataEventReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.BeginInvoke(new Action(() => textboxInterface.Text = indata));
}