The code below builds with no errors, the connection to the device seems to work too although I do not get any feedback from the device.
on V.S., I have placed a break point at the linemyReceivedLines = sp.ReadExisting(); and the variable myReceivedLines comes back null.
On another similar program connecting to the same device, couple lines of feedback appear (see below), why is this variable null in my case?
Lines that appear on other program:
Connecting...
start
Printer is now online.
echo:Marlin: 1.0.0 RC2
echo: Last Updated: 2012-05-22-1 | Author: eMAKER
...etc...
Code:
//Fields
string myReceivedLines;
//subscriber method for the port.DataReceived Event
private void DataReceivedHandler(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
myReceivedLines = sp.ReadExisting();
}
protected override void SolveInstance(IGH_DataAccess DA)
{
List<string> gcode = new List<string>();
DA.GetDataList(0, gcode);
string selectedportname = default(string);
DA.GetData(1, ref selectedportname);
int selectedbaudrate = default(int);
DA.GetData(2, ref selectedbaudrate);
bool connecttodevice=default(bool);
DA.GetData(3, ref connecttodevice);
bool sendtoprint= default(bool);
DA.GetData(4, ref sendtoprint);
if (!DA.GetDataList(0, gcode)) return;
if (!DA.GetData(1, ref selectedportname)) return;
if (!DA.GetData(2, ref selectedbaudrate)) return;
if (!DA.GetData(3, ref connecttodevice)) return;
if (!DA.GetData(4, ref sendtoprint)) return;
SerialPort port = new SerialPort(selectedportname, selectedbaudrate, Parity.None, 8, StopBits.One); //Create the serial port
port.DtrEnable = true; //enables the Data Terminal Ready (DTR) signal during serial communication (Handshaking)
port.Open(); //Open the port
port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
if (gcode == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Specify a valid GCode");
return;
}
if (connecttodevice == true)
{
DA.SetDataList(0, myReceivedLines);
}
else
{
port.Close();
}
if (sendtoprint == true)
{
foreach (String s in gcode)
{
port.WriteLine(s);
}
}
}
if (connecttodevice == true)
{
DA.SetDataList(0, myReceivedLines);
}
SerialPort.ReadExisting() cannot return null, at worst you'll get an empty string. The simple explanation is that you are using myReceivedLines before any data was received. Which is very likely in this case, you are using it right after you open the port. The odds that DataReceived will fire before you use myReceivedLines are vanishing small. The code is pretty inscrutable, you'll need to substantially revise it. Just keep in mind that the serial port will receive data at an entirely unpredictable moment in time. You'll need to have your DataReceived event handler push progress.
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 using .net 4.5 and since i got aware about some internet discussion about .net having wrong code implementation to rad serial data, i went with the code below.
The problems is however even despite i can see i create a COM port connection (to the right com port number), its never firing on data received.
The data receiving is based upon a simple Arduino app, (Arduino monitor does show data gets send over serial), but .net never seams to fire upon serial events.
I've put breakpoints on eventread, it never gets hit
i've looked at other discussions here like .NET SerialPort DataReceived event not firing but so far they don't resolve the issue i have. I tried various combination of serial line setups, and believe the below ones are correct.
as for Arduino part the line is setup as:
Serial.begin(9600);
I call my class like : `InfraredSensor mySens = new InfraredSensor("COM4",9600);'
class InfraredSensor
{
private string Eventlogapp = "IRlogging";
private SerialPort Port;
public InfraredSensor(string COMport, int baudrate) //constructor
{
if (applicationlog != "") this.EventlogSapec = applicationlog;
WriteEventLog(EventlogSapec, EventSource, "Initializing-IR:" + COMport, info, EventIRStarted);
// I found that the .net standard implementation for com port reading is not OK (.net doesnt follow win32 api).
// There are numerous readings about it, but a good way to read seams to be to use Serial BaseStream.ReadAsync
// code below is based upon : http://www.c-sharpcorner.com/code/2880/serial-port-with-efficient-data-reading-in-c-sharp.aspx
this.comport = COMport;
SerialPort Port = new SerialPort(comport);
Port.BaudRate = baudrate;
Port.DataBits = 8;
Port.Parity = Parity.None;
Port.StopBits = StopBits.One;
Port.Handshake = Handshake.None;
Port.NewLine = Environment.NewLine;
Port.ReceivedBytesThreshold = 2; // + linefeed
Port.DataReceived += ReadEvent;
Port.Open();
Port.DtrEnable = true;
// i've tested from here i do have an open connection
// its just dat ReadEvent never fires...
}
private void ReadEvent(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[2];//todo i only send "A" or "B", for the debug moment
Action kickoffRead = null;
kickoffRead = (Action)(() => Port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
{
try
{
int count = Port.BaseStream.EndRead(ar);
byte[] dst = new byte[count];
Buffer.BlockCopy(buffer, 0, dst, 0, count);
RaiseAppSerialDataEvent(dst);
}
catch (Exception ex)
{
WriteEventLog(Eventlogapp, "IR", "Failure-IR:" + ex.Message, info, 204);
}
kickoffRead();
}, null)); kickoffRead();
}
private void RaiseAppSerialDataEvent(byte[] Data)
{
// code never gets to here
string msg = Encoding.Default.GetString(Data);
int breakpointhere = 0;
if (msg.Contains('A')) WriteEventLog(Eventlogapp, "IR", "Sensor A", info, 213);
if (msg.Contains('B')) WriteEventLog(Eventlogapp, "IR", "Sensor B", info, 214);
}
}
I ran out of ideas (and hair as its driving me nuts) what could cause this behaviour ?
finally this took me 2 days ...
in arduino code i now use
serial.print("Mytext\n"); // using \n to be sure of escape character
for the serial line config i now use
this.comport = COMport;
this.Port = new SerialPort(comport);
this.Port.BaudRate = baudrate;
this.Port.DataBits = 8;
this.Port.Parity = Parity.None;
this.Port.StopBits = StopBits.One;
this.Port.Handshake = Handshake.None;
this.Port.RtsEnable = true;
// Port.NewLine = "\n";
// Port.ReceivedBytesThreshold = 2; // + linefeed
this.Port.DataReceived += new new SerialDataReceivedEventHandler(ReadEvent);
....
..
SerialDataReceivedEventHandler(ReadEvent);
//code didnt change inside but the method has changed see the +=
The DataReceived event fires when there is data coming through the serial port, but it can fire randomly. So you could get part of the message in one event and the other half of the message in another event. You should have some sort of key to know when you have the whole message. So build your string until you have the whole message then do something based on the message.
For example, my device's message is done when there is a Line Feed. So my code looks something like:
char ESC = (char)27;
char CR = (char)13;
char LF = (char)10;
StringBuilder sb = new StringBuilder();
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string Data = serialPort1.ReadExisting();
foreach (char c in Data)
{
if (c == LF)
{
sb.Append(c);
//we have our message, do something, maybe like
if (sb.ToString().Contains("A"))
{
//print A
}
else
{
//print B
}
}
else
{
sb.Append(c);
}
}
}
In the code below, the strings received within myReceivedLines appear when connecting with my serial port (when connecttodevice is true). However they disapear when I launch another command (when homeall is true).
I added the field called myReceivedLines within the class so that I could use the method String.Add() to all the feedback received and commands sent (having like a console within the program).
Why does the feedback dispear when a command is sent and how can I make sure all the strings stay in the variable myReceivedLines? Is the string going to myReceivedLine disapearing because they happen within a subscriber method? How do I solve that?
NB: GH_DataAccess.SetDataList(Int32, IEnumerable) is a method from the Kernel a software called Grasshopper to assign values to an output (it has to be used within the GH_Component.SolveInstance() method which is also from this Kernel), I am using this to visualise myReceivedLines.
code:
public class SendToPrintComponent : GH_Component
{
//Fields
List<string> myReceivedLines = new List<string>();
SerialPort port;
//subscriber method for the port.DataReceived Event
private void DataReceivedHandler(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
while (sp.BytesToRead > 0)
{
try
{
myReceivedLines.Add(sp.ReadLine());
}
catch (TimeoutException)
{
break;
}
}
}
protected override void SolveInstance(IGH_DataAccess DA)
{
//Opening the port
if (port == null)
{
string selectedportname = default(string);
DA.GetData(1, ref selectedportname);
int selectedbaudrate = default(int);
DA.GetData(2, ref selectedbaudrate);
//Assigning an object to the field within the SolveInstance method()
port = new SerialPort(selectedportname, selectedbaudrate, Parity.None, 8, StopBits.One);
//Enables the data terminal ready (dtr) signal during serial communication (handshaking)
port.DtrEnable = true;
port.WriteTimeout = 500;
port.ReadTimeout = 500;
}
//Event Handling Method
bool connecttodevice = default(bool);
DA.GetData(3, ref connecttodevice);
**if (connecttodevice == true)**
{
if (!port.IsOpen)
{
port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
DA.SetDataList(0, myReceivedLines);
port.Open();
}
}
else
if (port.IsOpen)
{
port.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);
port.Close();
}
if (port.IsOpen)
{
DA.SetData(1, "Port Open");
}
//If the port is open do all the rest
if (port.IsOpen)
{
bool homeall = default(bool);
DA.GetData(5, ref homeall);
//Home all sends all the axis to the origin
**if (homeall == true)**
{
port.Write("G28" + "\n");
myReceivedLines.Add("G28" + "\n");
DA.SetDataList(2, myReceivedLines);
}
}
else
{
DA.SetData(1, "Port Closed");
}
}
}
If you are trying to append to a string, I would reccomend a StringBuilder object.
Or the less cleaner resolution, use the += operator,
string s = "abcd";
s+="efgh";
Console.WriteLine(s); //s prints abcdefgh
First of all your variables (myReceivedLines and port) are not static. I'm not sure if you want them to be static because I can't see how your using SendToPrintComponent class.
And could you explain DA.SetDataList(0, myReceivedLines); or better yet include the code because the problem could be there...
I am using the SerialPort.ReadLine() method to display the data received from a Serial Port (code below).
Previously the code looked like that and received data but it did not send data. Now it is the other way around:
Since I placed the port.DataReceived event within the if(port==null) statement and added SerialPort port; as field, I don't receive data anymore. Can placing the event within an if statement change the way data is received and displayed? How can I fix that?
//Fields
List<string> myReceivedLines = new List<string>();
SerialPort port;
//subscriber method for the port.DataReceived Event
private void DataReceivedHandler(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
while (sp.BytesToRead > 0)
{
try
{
myReceivedLines.Add(sp.ReadLine());
}
catch (TimeoutException)
{
break;
}
}
}
protected override void SolveInstance(IGH_DataAccess DA)
{
//Opening the port
if (port == null)
{
string selectedportname = default(string);
DA.GetData(1, ref selectedportname);
int selectedbaudrate = default(int);
DA.GetData(2, ref selectedbaudrate);
bool connecttodevice = default(bool);
DA.GetData(3, ref connecttodevice);
//Assigning an object to the field within the SolveInstance method()
port = new SerialPort(selectedportname, selectedbaudrate, Parity.None, 8, StopBits.One);
//Event Handling Method
if (connecttodevice == true)
{
port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
DA.SetDataList(0, myReceivedLines);
}
//Enables the data terminal ready (dtr) signal during serial communication (handshaking)
port.DtrEnable = true;
port.Open();
}
//Displays if port if opened
if (port.IsOpen)
{
DA.SetData(1, "Port Open");
}
//If the port is open do all the rest
if (port.IsOpen)
{
//Assigning the input to variables for the code.
List<string> gcode = new List<string>();
DA.GetDataList(0, gcode);
bool sendtoprint = default(bool);
DA.GetData(4, ref sendtoprint);
bool homeall = default(bool);
DA.GetData(5, ref homeall);
//What happens when input is set
if (sendtoprint == true)
{
if (homeall == true)
{
port.Write("G28" + "\n");
}
}
else
{
DA.SetData(1, "Port Closed");
}
}
Try something like this, removing the attaching of the eventhandler out of the port creation section
if (port == null)
{
string selectedportname = default(string);
DA.GetData(1, ref selectedportname);
int selectedbaudrate = default(int);
DA.GetData(2, ref selectedbaudrate);
bool connecttodevice = default(bool);
DA.GetData(3, ref connecttodevice);
//Assigning an object to the field within the SolveInstance method()
port = new SerialPort(selectedportname, selectedbaudrate, Parity.None, 8, StopBits.One);
//Enables the data terminal ready (dtr) signal during serial communication (handshaking)
port.DtrEnable = true;
}
if (connecttodevice == true)
{
if(!port.IsOpen)
{
port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
DA.SetDataList(0, myReceivedLines);
port.Open();
}
}
else
{
if(port.IsOpen)
{
port.DataReceived -= new SerialDataReceivedEventHandler(DataReceivedHandler);
// DA.SetDataList(0, myReceivedLines); // Not sure how you want to remove this
port.Close();
}
}
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.