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.
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);
}
}
So, I'm trying to use my Barcode Scanner as a 'Serial' device as opposed to a Keyboard emulator but it is not creating the com port. I have scanned the set-up codes from the manual that set it as a Serial device, that seems to configure the scanner correctly (it stops sending scanned codes to text-box\text editor) but because there is no COM port, I cannot capture the data when I scan a barcode......
Windows installed the driver when it was first plugged in, there wasn't a disk\driver supplied... wondered if anyone else has experienced the same issue.....
Here is my code....
class Program
{
// Create the serial port with basic settings
private SerialPort port = new SerialPort("com1", 9600, Parity.None, 8, StopBits.One);
[STAThread]
static void Main(string[] args)
{
new Program();
}
private Program()
{
string[] ports = System.IO.Ports.SerialPort.GetPortNames();
Console.WriteLine("Incoming Data:");
// Attach a method to be called when there
// is data waiting in the port's buffer
port.DataReceived += new
SerialDataReceivedEventHandler(port_DataReceived);
// Begin communications
port.Open();
// Enter an application loop to keep this thread alive
Application.Run();
}
private void port_DataReceived(object sender,
SerialDataReceivedEventArgs e)
{
// Show all the incoming data in the port's buffer
Console.WriteLine(port.ReadExisting());
}
}
I get the error message..... 'The port 'com1' does not exist'..... when I try to open the Port.
When I create a virtual Port (using 3rd party app) the code runs BUT I still don't get the data from the Scanner....
I just newbie, and I was having task - recieve data from BarCode scaner by serial port... I spent a lot of time... and I have next result
using System.IO.Ports;
using System.Timers;
namespace BarCode_manager
{
public partial class MainWindow : Window
{
private static SerialPort currentPort = new SerialPort();
private static System.Timers.Timer aTimer;
private delegate void updateDelegate(string txt);
public MainWindow()
{
InitializeComponent();
currentPort.PortName = "COM6";
currentPort.BaudRate = 9600;
currentPort.ReadTimeout = 1000;
aTimer = new System.Timers.Timer(1000);
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
if (!currentPort.IsOpen)
{
currentPort.Open();
System.Threading.Thread.Sleep(100); /// for recieve all data from scaner to buffer
currentPort.DiscardInBuffer(); /// clear buffer
}
try
{
string strFromPort = currentPort.ReadExisting();
lblPortData.Dispatcher.BeginInvoke(new updateDelegate(updateTextBox), strFromPort);
}
catch { }
}
private void updateTextBox(string txt)
{
if (txt.Length != 0)
{
aTimer.Stop();
aTimer.Dispose();
txtReceive.Text = Convert.ToString(aTimer.Enabled);
currentPort.Close();
}
lblPortData.Text = txt;
lblCount.Content = txt.Length;
txtReceive.Text = Convert.ToString(aTimer.Enabled);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (currentPort.IsOpen)
currentPort.Close();
}
}
}
you may used below code. I can able to open the COM which I configured in specific port.
SerialPort _serialPort;
// delegate is used to write to a UI control from a non-UI thread
private delegate void SetTextDeleg(string text);
private void Form1_Load(object sender, EventArgs e)
{
// all of the options for a serial device
// can be sent through the constructor of the SerialPort class
// PortName = "COM1", Baud Rate = 19200, Parity = None,
// Data Bits = 8, Stop Bits = One, Handshake = None
//_serialPort = new SerialPort("COM8", 19200, Parity.None, 8, StopBits.One);
_serialPort = new SerialPort("COM8", 19200, Parity.None, 8, StopBits.One);
_serialPort.Handshake = Handshake.None;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
}
I'm in the process of writing my own barcode scripts. My scanner defaults to being a plug-n-play USB-HID...Human Interface Device...as opposed to being a USB-COMn port. I have to scan a barcode to switch it over to serial port mode. You can watch the transformation process in the Device Manager tree...as a "Ports" branch sprouts out, containing your barcode scanner's details. Mine's COM3.
Here the serial communication port shows error it cannot be accessed .....But the serial communication port works perfect in arduino, so it cant be the port problem, its not the driver problem either, the driver is updated and works well, so the problem can be in the code .....i am a newbie to C#.
public partial class Form1 : Form
{
private SerialPort myport;
private string in_data;
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
myport = new SerialPort();
myport.BaudRate = 19200;
myport.PortName = pn.Text;
myport.Parity = Parity.None;
myport.DataBits = 8;
myport.StopBits = StopBits.One;
myport.DataReceived += myport_DataReceived;
try
{
myport.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error!!");
}
}
void myport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
*in_data = myport.ReadLine();***
this.Invoke(new EventHandler(displaydata_event));
}
private void displaydata_event(object sender, EventArgs e)
{
string[] newData = in_data.Split(',');
bv.Text = newData[0];
bi.Text = newData[1];
pv.Text = newData[2];
pi.Text = newData[3];
t.Text = newData[4];
}
}
it cannot be accessed
This may indicates that the port doesnt exists or is already in use.
Maybe another application is already listening on this port. (arduino?)
Comport.Open() Exceptions are descibed here:
MSDN SerialPort.Open Method
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
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.