Is there code to receive data from a serial port? - c#

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);
}
}

Related

Receiving continuous data using 32feet in .Net (BLUETOOTH)

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!!!

C# SerialPort class receives incorrect data

I currently have a program made using VB6 code that uses the MSCOMM control to pull back data from the serial port. This manages to successfully receive the data from my serial port, in which a Denso BHT-904B device is connected.
I am now trying to move this code over to C# so it fits in with a new piece of software that i am developing. To do this i am using the SerialPort class. However, the issue is that when i open the port up the data received event only fires when the device fails to communicate (which im guessing is due to a timeout). The data then received in the event is '↑↑↑↑↑'.
My SerialPort control settings are the following:
DtrEnable = True
PortName = COM3
ReadBufferSize = 1024
WriteBufferSize = 512
The code that i am using behind my form control is:
namespace BHTTestingDotNet
{
public partial class Form1 : Form
{
private string rxString;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
serialPort.DtrEnable = true;
serialPort.Encoding = Encoding.Default;
serialPort.DataReceived += serialPort_DataReceived;
serialPort.ErrorReceived += serialPort_ErrorReceived;
serialPort.Open();
}
private void serialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
MessageBox.Show(e.ToString());
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
var serialPort = (SerialPort)sender;
var test = serialPort.BytesToRead;
SerialPort sr = (SerialPort)sender;
rxString = sr.ReadExisting();
this.BeginInvoke(new EventHandler(displayText));
}
private void displayText(object o, EventArgs e)
{
txtBHT.AppendText(rxString);
}
}
}
I have already tried to set both RtsEnable and DtrEnable to true but that didn't make any difference.
UPDATE - I have now changed to protocol settings on the device but i now only receive pipes and then a return symbol, for example like so:
|||||¬
I am using SerialPort class often and for my purposes I have made my own class
public class SerialPortDataSource : SerialPort
where SerialPort.DataReceived handler invoke this method:
private void SerialPortDataSource_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
if (BytesToRead > 0)
{
var buffor = new byte[BytesToRead];
Read(buffor, 0, buffor.Length);
_receivedBytes = buffor;
//wConsole.WriteLine(ArrayExtension.ToString(buffor));
var dataLogger = DataLogger;
if (dataLogger != null)
{
dataLogger.WriteLine("- DR - {0}", true, BitConverterExtension.ToHexString(buffor));
}
if (OnDataReceived != null)
{
OnDataReceived(this, buffor);
}
}
}
catch (InvalidOperationException)
{
// sometimes DataReceived event is invoked after port is closed which causes InvalidOperationException
}
}
This method is working for me in many applications with variety serial port settings.

C# COM port not disposed

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!

Multiple Serial Ports in C# / Trouble using List <>

I have a system that sends an "at" command to a serial port and displays the return on a MessageBox.
But I needed to do this in all available serial ports. So I created a List and I'm adding all the ports on it.
I managed to send the command, but could not continue the rest of the code to catch the return because I am having trouble handling the lists. I am a beginner in C #.
Below is my current code.
The part that is commented out is what I'm struggling to continue.
This part belongs to the old code (when it was just one serial port).
public partial class Form1 : Form
{
List<SerialPort> serialPort = new List<SerialPort>();
// delegate is used to write to a UI control from a non-UI thread
private delegate void SetTextDeleg(string text);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var portNames = SerialPort.GetPortNames();
foreach (var port in portNames) {
SerialPort sp;
sp = new SerialPort(port, 19200, Parity.None, 8, StopBits.One);
sp.Handshake = Handshake.None;
//sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sp.ReadTimeout = 500;
sp.WriteTimeout = 500;
serialPort.Add(sp);
listPorts.Items.Add(port);
}
}
private void listPorts_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
foreach (var sp in serialPort) {
// Open port
try
{
if (!sp.IsOpen)
sp.Open();
MessageBox.Show(sp.PortName + " aberto!");
sp.Write("at\r\n");
}
catch (Exception ex)
{
MessageBox.Show("Error opening/writing to serial port :: " + ex.Message, "Error!");
}
}
}
/* HELP START
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(500);
string data = sp.ReadLine();
this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
}
private void si_DataReceived(string data)
{
String retorno = data.Trim();
MessageBox.Show(retorno);
// Fecha a porta após pegar o retorno
sp.Close();
}
HELP END */
}
What to put in place 'sp.ReadLine ();' and 'sp.Close ();'?
I don't know do this because of the List <>
The simplest approach would be to use a lambda expression which would capture the port you're using. A lambda expression is a way of building a delegate "inline" - and one which is able to use the local variables from the method you declare it in.
For example:
foreach (var port in portNames)
{
// Object initializer to simplify setting properties
SerialPort sp = new SerialPort(port, 19200, Parity.None, 8, StopBits.One)
{
Handshake = Hanshake.None,
ReadTimeout = 500,
WriteTimeout = 500
};
sp.DataReceived += (sender, args) =>
{
Thread.Sleep(500); // Not sure you need this...
string data = sp.ReadLine();
Action action = () => {
MessageBox.Show(data.Trim());
sp.Close();
};
BeginInvoke(action);
};
serialPort.Add(sp);
listPorts.Items.Add(port);
}
A few notes about this:
Just because some data has been received doesn't mean that a whole line has, so ReadLine may still block
If you only need to show a message box, you may not need Control.BeginInvoke. (If you need to do more here, you might want to extract most of that code into a separate method which just takes a string, then create an action which would call that method.)
Are you sure you want to close the serial port as soon as the first line has been received?
You can chage your sp_DataReceived method as,
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(500);
SerialPort sp = (SerialPort)sender;
string data = sp.ReadLine();
this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
sp.Close();
}
and remove the sp.Close(); from si_DataReceived method.
If you want to have a Serial port value in your si_DataReceived method,
you should pass it there:
// First, add port into your delegate
private delegate void SetTextDeleg(SerialPort port, string text);
...
/* HELP START */
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(500);
SerialPort sp = (SerialPort) sender; // <- Obtain the serial port
string data = sp.ReadLine();
// Pass the serial port into si_DataReceived: SetTextDeleg(sp, ...
this.BeginInvoke(new SetTextDeleg(sp, si_DataReceived), new object[] { data });
}
// "SerialPort sp" is added
private void si_DataReceived(SerialPort sp, string data) {
String retorno = data.Trim();
MessageBox.Show(retorno);
// Fecha a porta após pegar o retorno
sp.Close();
}
/* HELP END */
See also:
http://msdn.microsoft.com/library/system.io.ports.serialport.datareceived.aspx

serialport and dispatchertimer, how to make my thread pause when com port is open?

i have a serial port that will iterate through the ports with this method:
foreach (string s in SerialPort.GetPortNames())
{
var serialOneOfMany = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (serialOneOfMany.IsOpen)
{
serialOneOfMany.Close();
}
else
{
try
{
serialOneOfMany.Open();
}
catch
{
var openSerial = new System.Timers.Timer(3100);
openSerial.Elapsed += (o, e) =>
{
serialOneOfMany.Open();
openSerial.Enabled = false;
openSerial.Dispose();
};
openSerial.Enabled = true;
}
}
if (serialOneOfMany.IsOpen)
{
string received;
try
{
lblPortNum.Content = s;
lblPortNum.Refresh();
serialOneOfMany.Write(testMessage);
serialOneOfMany.DataReceived += new SerialDataReceivedEventHandler(testSerialPort_DataReceived);
}
catch (TimeoutException e)
{
serialOneOfMany.Close();
continue;
}
}
}
so, i want to open the port, send it a message, listen for the response, then close it. as everyone knows, every comport found in GetPortNames isn't a valid serial port. so, what i've been doing is setting a timer with a dispatcher timer:
DispatcherTimer time = new DispatcherTimer();
time.Interval = TimeSpan.FromMilliseconds(3000);
time.Tick += new EventHandler(someEventHandler);
time.Start();
here's the other method handled here:
private void someEventHandler(Object sender, EventArgs args)
{
SerialPort serial = (SerialPort)sender;
if (serial.IsOpen)
serial.Close();
serial.Dispose();
//if you want this event handler executed for just once
DispatcherTimer thisTimer = (DispatcherTimer)sender;
thisTimer.Stop();
}
so, it'll open the com port, if it doesn't get a response within 3 seconds, it will close the port. the problem i'm having is that the foreach loop will just barrel through the code and open the comport several times, i'll get a message saying The COM Port is open already and can't be used. so basically it's not pausing in openSerial.
i want it to open a new serial port, and if it's not accessible, wait 3100 milliseconds and try again. how do i do that?
UPDATED CODE:
private void button1_Click(object sender, RoutedEventArgs e)
{
CheckPorts();
}
private void checkPorts()
{
SendMessage("messageToDevice1", 19200);
SendMessage("Message2", 9600);
}
private void SendMessage(string testMessage, int baudRate)
{
int baudRate = 9600;
string testMessage = "test";
txtPortName.Text = "Testing all serial ports";
foreach (string s in SerialPort.GetPortNames())
{
SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (!newPort.IsOpen)
{
try
{
newPort.Open();
}
catch { }
}
if (newPort.IsOpen)
{
openPorts.Add(newPort);
newPort.Write(testMessage);
newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
}
else
{
newPort.Dispose();
}
}
txtPortName.Text = "Waiting for response";
tmrPortTest.Enabled = true;
}
my new problem is that it just blows through the com ports, i need it to stop for each one, take a second to listen, then close it. it just blows through the foreach loop.
now, the reason why i don't just open up the port and keep it open through all the messages is that my devices have different baud rates, and i can't adjust them to all match. so, i need to open the ports, then send messages, listen, if they don't respond to the first round of messages, then open them up at the new baudrate and send a new batch of messages. but the foreachloop doens't pause for me to listen.
I think this more or less agrees with rare's answer. The port where you receive a response (you would probably want to check the response as well) will remain open and all the others should close.
private List<SerialPort> openPorts = new List<SerialPort>();
private void button3_Click(object sender, EventArgs e)
{
int baudRate = 9600;
string testMessage = "test";
txtPortName.Text = "Testing all serial ports";
foreach (string s in SerialPort.GetPortNames())
{
SerialPort newPort = new SerialPort(s, baudRate, Parity.None, 8, StopBits.One);
if (!newPort.IsOpen)
{
try
{
newPort.Open();
}
catch { }
}
if (newPort.IsOpen)
{
openPorts.Add(newPort);
newPort.Write(testMessage);
newPort.DataReceived += new SerialDataReceivedEventHandler(serialOneOfMany_DataReceived);
}
else
{
newPort.Dispose();
}
}
txtPortName.Text = "Waiting for response";
tmrPortTest.Enabled = true;
}
private void serialOneOfMany_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
txtPortName.Text = ((SerialPort)sender).PortName;
}
private void tmrPortTest_Tick(object sender, EventArgs e)
{
tmrPortTest.Enabled = false;
foreach (SerialPort port in openPorts)
{
if (port.PortName != txtPortName.Text)
{
port.Close();
port.Dispose();
}
}
}
Here's how I would do this --
First, try to open all the serial ports. The ones that actually do open are put in a list.
Assign all serial ports in the list to the same DataReceived event handler. The event handler is where you will save the port name (it's in the args) and kill the timer if you rx'd the response
Send your testMessage out all the open ports
Set just one timer for 3.1 seconds
Close the ports once the timer fires or the event handler rx's the response.

Categories

Resources