Why SerialPort only works after button pressed - c#

I Have created some code for communicating with a device over the serialport. By sending certain command with serialPort1.WriteLine I should receive answer with serialPort1.ReadExisting(). Well I'm able to detect the device but I need to read some other information but to get this information correct I only get it when I place the code in a button_Click function. The funny thing is that All is written in a function and I wish to get the information after the device is detected immediately. When I call the function after the device has been detected it doesn't work and when I call the function after the button is pressed I get the correct information. I do not understand why this is so.
The function for detecting the device is like this:
private void detectVM25ToolStripMenuItem_Click(object sender, EventArgs e)
{
// Detect VM25 and show connection is established
String device;
//Search all portnames
String[] ports = SerialPort.GetPortNames();
int totalPorts = ports.Length;
int count = 0 ;
//Test which enabled port is the VM25.
foreach (string port in ports)
{
count = count + 1;
serialPort1.PortName = port;
serialPort1.Open();
if (serialPort1.IsOpen)
{
serialPort1.WriteLine("#S" + Environment.NewLine);
answer = serialPort1.ReadExisting();
if (answer != "")
{
device = answer.Substring(0, 4);
if (device == "VM25")
{
getRecordings();
statusLblDevice.ForeColor = Color.LawnGreen;
statusLblDevice.Text = port + " - " + device + " - Connected";
VM25Port = port;
}
}
else if (answer == "")
{
serialPort1.Close();
if (count == totalPorts)
{
MessageBox.Show("No device found");
}
}
}
}
}
The function getRecordings() should give me data. If I place this function in my form and get called after a button is pressed I get the correct info but when it is inside the above function it doesn't do anything.
private void getRecordings()
{
if (serialPort1.IsOpen)
{
serialPort1.WriteLine("#H" + Environment.NewLine);
memoryInfo = serialPort1.ReadExisting();
label1.Text = memoryInfo;
}
}
Does anybody knows why this is the case? I would like not to have press a button and get this information after it has detected the device. I also tried to create a delay with `Task.Delay()' unfortunately this did not help

It's surely because you have no synchronization whatsoever. .ReadExisting() does not get an entire response, it gets only what's already been received. So calling it right after .WriteLine(...)... well your data hasn't even reached the device yet, so there definitely won't be an answer ready to read.
Since you know the length of your expected answer (or at least the prefix you're interested in), set the read timeout and then call .Read() (or better, .BaseStream.ReadAsync()) with that length.
Even that is only going to mostly work when the port is freshly opened... for subsequent commands you risk getting data in your buffer from the tail end of a reply to an earlier command. Correct use of a serial port really requires the two ends to agree on some sort of framing, whether that is "a message contains only ASCII and ends with CR+LF" or "a message starts with the length of its payload" or some less common scheme.

with the point out of #Ben Voigt I discovered I should tackle this issue different.
I have use the method serialPort1.ReadTo() because I know that each message end with "/a".
therefore I fixed the function as follows
private void detectVM25ToolStripMenuItem_Click(object sender, EventArgs e)
{
// Detect VM25 and show connection is established
String device;
//Search all portnames
String[] ports = SerialPort.GetPortNames();
int totalPorts = ports.Length;
int count = 0 ;
//Test which enabled port is the VM25.
foreach (string port in ports)
{
count = count + 1;
serialPort1.PortName = port;
serialPort1.Open();
if (serialPort1.IsOpen)
{
serialPort1.WriteLine("#S" + Environment.NewLine);
answer = serialPort1.ReadExisting();
if (answer != "")
{
device = answer.Substring(0, 4);
if (device == "VM25")
{
statusLblDevice.ForeColor = Color.LawnGreen;
statusLblDevice.Text = port + " - " + device + " - Connected";
VM25Port = port;
serialPort1.WriteLine("#H" + Environment.NewLine);
string input = serialPort1.ReadTo("/a");
label1.Text = input;
string totalMeasurements = input.Substring(0, 11);
string totalFFT = input.Substring(11, 11);
statusLblMeas.Text = totalMeasurements;
statusLblFFT.Text = totalFFT;
}
}
else if (answer == "")
{
serialPort1.Close();
if (count == totalPorts)
{
MessageBox.Show("No device found");
}
}
}
}
}
Note the changes of reading the serialport where I stated that it should read up to "/a"

Related

How can I implement asynchronous functions when writing to a Serial COM Port?

I have a program that reports the size of a folder structure over Serial COM every X seconds.
It also has the ability to report a list of all the files within the folder structure, when a user sends a command also over Serial COM.
As an example:
A user is getting the size info every 10 seconds, at some point they want to know the list of files. So they send a “1” over the COM port and the program then starts reporting the files back.
The issue I am having is that two functions can’t write to the COM Port at the same time and so it starts throwing exceptions.
What I would like to do is have both functions wait until the other is finished. Here is my code.
This is the simple fucntion for writing to the COM Port:
private void ComWrite(string msg)
{
ComPort.Write(msg);
}
I call this from these two fucntions:
This one reports the file names:
private void GetFileNames()
{
fileNames = Directory.GetFiles(textBox1.Text, "*.wav", SearchOption.AllDirectories);
for (int i = 0; i < fileNames.Length; i++)
{
ComWrite((fileNames[i] + "\r\n"));
}
}
This one is on a timer and reports the folder size:
public void OnTimedEvent(object source, ElapsedEventArgs elapsed)
{
folderSize = DirSize(new DirectoryInfo(textBox1.Text)) / 1000000;
string labelText = folderSize.ToString() + "Mb";
label3.Text = labelText;
if (checkBox1.Checked)
{
try
{
ComWrite(labelText + "\r\n");
label9.Text = labelText;
}
catch (Exception)
{
MessageBox.Show("Please Open COMPORT before sending command");
}
}
}
How can I implement asynchronous functions, or some other method of stopping them from falling over one another?
EDIT: Requested Code.
Here is setup the COM Port on form load.
private void Form1_Load(object sender, EventArgs e)
{
label5.Text = "Idle";
ComPort.BaudRate = Convert.ToInt32("9600");
ComPort.DataBits = Convert.ToInt16("8");
ComPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
ComPort.Handshake = (Handshake)Enum.Parse(typeof(Handshake), "None");
ComPort.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
}
And you open the COM Port you select via the combobox with a button click here:
private void btnPortState_Click(object sender, EventArgs e)
{
try
{
if (btnPortState.Text == "COMPort Closed / Click to Open")
{
btnPortState.BackColor = Color.Green;
btnPortState.Text = "COMPort Open / Click To Close";
ComPort.PortName = Convert.ToString(cboPorts.Text);
ComPort.Open();
}
else if (btnPortState.Text == "COMPort Open / Click To Close")
{
btnPortState.Text = "COMPort Closed / Click to Open";
btnPortState.BackColor = Color.Firebrick;
ComPort.Close();
}
}
catch (Exception)
{
MessageBox.Show("You must select a COMPORT before opening it.");
}
}
I think what you want is a mutual exclusion for any transaction made to your com port.
If the problem is that 2 different threads can start to write to the comport at the same time this can be solved by using a semaphore.
In your case I am guessing that it wouldn't be good if the directory size would be transmitted while you are sending the file names. So you can't simply synchronize inside ComWrite.
Instead, I would propose the following:
In Class:
//semaphore so only 1 Thread at a time can pass through
private static Semaphore semaphore = new Semaphore(1, 1); //(1,1) -> see comments
In OnTimedEvent:
if (checkBox1.Checked)
{
try
{
semaphore.WaitOne(); //Increases semaphore counter
ComWrite(labelText + "\r\n");
semaphore.Release();
label9.Text = labelText;
}
(...)
And
private void GetFileNames()
{
semaphore.WaitOne();
(...) //writing the file names to ComPort
semaphore.Release();
}
Semaphores are kind of working like a counter. If the counter reaches its max count no more threads can pass by the Wait call and are waiting for their turn. releasing the semaphore basically tells the counter to decrease and waiting threads are now allowed to enter.

splitting serialport data into different textboxes C#

I'm new to c#.
I'm using the Nucleo board to send data through a serial port into my GUI. The data consists of pulse rate, number of steps and body temperature.
My code here works completely fine to display all the data into a single textbox, but I want to display each value in different textboxes.
This is what the incoming data looks like
S0E // where "S" is for steps and "E" is for the end
P5E // where "P" is for pulse rate and "E" is for the end
T22.5E // where "T" is for body temp. and "E" is for the end
Here is the code I am using:
private void showbtn_Click(object sender, EventArgs e) //Showbtn click
{
text.Clear(); //clears the text in the textbox
bool foundUser = false; // sets the boolean foundUser to false
int userindex = 0; // sets interger to zero
for (int i = 0; i < userlist.Count; i++)
{
if (userlist[i].name.Equals(Nametb.Text)) // if the user entered name equals to the name in the list
{
foundUser = true;
userindex = i;
}
}
if (foundUser == true)
{
string userText; // the following lines of code displays the user details in the textbox
userText = "name :" + userlist[userindex].name + "user :" + Environment.NewLine + "age:" + userlist[userindex].age +
Environment.NewLine + " gender:" + userlist[userindex].gender + Environment.NewLine + "height:" + userlist[userindex].height +
Environment.NewLine + "weight:" + userlist[userindex].weight + Environment.NewLine + "BMI :" + userlist[userindex].bmI;
text.Text = userText;
}
else
{
text.Text = "no user found"; // if the user not found displays as "no user Found"
}
t = comboBox1.Text.ToString();
sErial(t);
}
private void button2_Click(object sender, EventArgs e) // searches for available com ports
{
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports)
{
comboBox1.Items.Add(port); // adds them to the combo box
}
}
string t;
private SerialPort SerialPort1;
void sErial(string Port_name)
{
SerialPort1 = new SerialPort(Port_name, 9600, Parity.None, 8, StopBits.One); //serial port properties
SerialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
SerialPort1.Open(); //opens serial port
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort1 = (SerialPort)sender;
string w = serialPort1.ReadLine(); // assigns the data from the serial port to a string
if (w != String.Empty)
{
if (abort == false)
{
Invoke(new Action(() => rt1.AppendText(w))); // displays the data in a textbox
}
}
}
If I have understood correctly, you receive data from an external device and you wish to split apart a string that contains multiple numeric data elements delimited by single characters, so you can display the results to different textboxes.
This is a typical problem to be solved by people who receive data from an embedded system and need to learn how to handle it on a host PC, but are new to programming in C#.
There are many different ways to do this. Since you say you can receive and display the entire string in a single textbox, I will focus only on how to split it apart and display the parts in three different richTextBoxes.
For a c# beginner, string.split and substring are pretty straightforward ways to break apart a string. Here is one simple example using string.split.
I made a complete example for you: you can see below that it works. First I created a form containing three rich textboxes called rt1, rt2, and rt3, along with an input textbox called tbInput and a button called btnSplit.
Then in the designer I double-clicked on the button to add an event handler for btnInput_click and added the code below to that handler. That's it!
private void btnSplit_Click(object sender, EventArgs e)
{
string blah = tbInput.Text;
tbInput.Clear();
var lst = blah.ToUpper().Split('E').ToList();
foreach (var item in lst)
if (item.Trim().StartsWith("S"))
rt1.AppendText($"Steps: {item.Remove(0, 1)} \n");
else if (item.Trim().StartsWith("T"))
rt2.AppendText($"Temperature: {item.Remove(0, 1)} \n");
else if (item.Trim().StartsWith("P"))
rt3.AppendText($"Pulse: {item.Remove(0, 1)} \n");
}
The code converts the input to upper case, splits the string on the letter "E" and assigns the results to a list, so given the starting string containing three "E"s, you will now have a list containing three strings:
item01 == S111
item02 == T98.6
item03 == P70
I then trim leading and trailing white space and assign the result to one of three RichTextBoxes based on the leading character. I use item.Remove(0,1) to remove the leading character before appending the result to the textbox. I use interpolation to embed the result in a string of my choosing.
I should probably point out that unless you are running this code on a thread other than the UI thread, you do not need to use an Action delegate and you do not need to Invoke anything. If you're on the main thread, just append the text to the textbox.
If you are planning to make a cross-thread call to a control running on the UI, an Action<> delegate is not the right way to do this. If that's what you're trying to do, look at MethodInvoker(). However, I strongly recommend that you not try to use multiple threads until you are quite advanced as a programmer and have read several books on the subject. They certainly are not needed for a simple project like this one :)
Does this help?
#craig.Feied
this was the part which I altered.
thanks again for your help.
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort1 = (SerialPort)sender;
var rt1 = new RichTextBox();
var rt2 = new RichTextBox();
var rt3 = new RichTextBox();
string blah = serialPort1.ReadLine();
var lst = blah.Split('E').ToList();
foreach (var item in lst)
if (item.Trim().StartsWith("S"))
rt1.AppendText($"Steps: {item.Remove(0, 1)} \n");
else if (item.Trim().StartsWith("T"))
rt2.AppendText($"Temperature: {item.Remove(0, 1)} \n");
else if (item.Trim().StartsWith("P"))
rt3.AppendText($"Pulse: {item.Remove(0, 1)} \n");
}

C# how to refresh serial port in c#

i want to make my application can refresh serial port in c#. when list of port(In ComboBox) is empty and i hit button refresh, it's work perfectly and show the list of active port. but if i disconnected the Serial Port and i hit refresh button, actually it's must make the list of port(In Combobox) is empty because the serial port is disconnected. So how to make when i hit refresh button and the condition is disconnected, we make all of port list(in Combobox ) is empty ?
this is my code in refresh button:
private void button2_Click_2(object sender, EventArgs e)
{
if(String.IsNullOrEmpty(cboPort.Text))
{
comm.SetPortNameValues(cboPort);
for (int i = 0; i < cboPort.Items.Count; i++)
{
string value = cboPort.GetItemText(cboPort.Items[i]);
if (String.IsNullOrEmpty(value))
{
string a = cboPort.SelectedIndex.ToString();
return;
}
else
{
cboPort.SelectedIndex = 0;
}
}
}
else if ((cboPort.Text) != " " && cboPort.SelectedIndex == -1)
{
cboPort.Text = " ";
return;
}
}
this is my code in setportnamevalues :
public void SetPortNameValues(object obj)
{
foreach (string str in SerialPort.GetPortNames())
{
((ComboBox)obj).Items.Add(str);
}
}
my expetation is :
1. i connect serial port
2. i run my app
3. i disconnect serial port
4. i hit refresh
5. final result is port list empty in combobox
thanks for helping and responses, i am still new in c#. Greetings!
i Finally get the answer.
this is modification of setportnamevalues :
public void SetPortNameValues(object obj)
{
string[] ports = SerialPort.GetPortNames(); // load all name of com ports to string
((ComboBox)obj).Items.Clear(); //delete previous names in combobox items
foreach (string port in ports) //add this names to comboboxPort items
{
((ComboBox)obj).Items.Add(port); //if there are some com ports ,select first
}
if (((ComboBox)obj).Items.Count > 0)
{
((ComboBox)obj).SelectedIndex = 0;
}
else
{
((ComboBox)obj).Text = " "; //if there are no com ports ,write Empty
}
}
in here modification in button action :
private void button2_Click_2(object sender, EventArgs e)
{
comm.SetPortNameValues(cboPort);
}
yeah, finally i get what i want.
My solution as below
Initialize COM list
Add serialPort_OnClick event for combobox so that whenever user click on combobox, the COM items will be reloaded
private void InitializePortSetting()
{
mDateTime = DateTime.Now;
//1. Setting port list
// Get a list of serial port names.
portList = SerialPort.GetPortNames();
Console.WriteLine("The following serial ports were found:");
// Display each port name to the console.
foreach (string port in portList)
{
port_name.Items.Add(port);
}
}
private void serialPort_OnClick(object sender, EventArgs e)
{
port_name.Items.Clear();
port_name.Text = "";
//port_name.Dispose();
// Get a list of serial port names.
portList = SerialPort.GetPortNames();
Console.WriteLine("The following serial ports were found:");
// Display each port name to the console.
foreach (string port in portList)
{
port_name.Items.Add(port);
}
}

Get input from serial port (RS-232)

im writing a program which reads the input from the serial port. It does recieve something but its breaking the line without reason.
The right input
This right inout should be
Sending...Sending...Sending...Sending...Sending...
Without changing line.
The actual input
Sending...
Se
ndin
g...
S
endi
ng..
.
Send
ing.
..
Se
ndin
g...
The code
public void Serial ()
{
try
{
SerialPort serial = new SerialPort(this.comboBox1.Text);
serial.BaudRate = 9600;
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;
serial.DataBits = 8;
serial.Handshake = Handshake.None;
serial.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceivedHandler);
serial.Open();
}
catch
{
}
}
public void SerialDataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string dataIn = sp.ReadExisting();
if (log_time == true)
{
this.richTextBox1.AppendText(time + dataIn);
}
else
{
this.richTextBox1.AppendText(dataIn + "\n");
}
}
The this.combobox1.Text is working fine, im using try because if not the program would crash if the serial port wasnt on!
Im initializing the serial on an other void with Serial();
How can i get the right input?
In your else clause, you should not append "\n" to this.richTextBox1. That is the special character for a new line. If you want the right input, need something like this.richTextBox1.AppendText(dataIn + "...");

IO.Ports exception throw and retry loop

I've been lurking here for a while, and have learned a ton just poking through questions. I'm pretty stumped on something, though. I'm using C#, and I'm trying to use IO.Ports to communicate with a USB device.
I've got code working that assumes the correct serial port, but sometimes my device winds up on a different port when plugged in, and I'd like to be able to run my code without having to change one variable and recompile. So, I want the code to poll the user for a port number, try to open the port, catch IOException when the port name's wrong, and re-poll until a valid port is given.
This is what I have so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
namespace USBDev1
{
class Program
{
static void Main(string[] args)
{
String portname = "COM";
SerialPort port = new SerialPort();
port.BaudRate = 9600;
bool loopthing = true;
while (loopthing == true)
{
Console.WriteLine("Which port?");
portname = "COM" + Console.ReadLine();
try
{
port.PortName = portname;
port.Open();
loopthing = false;
}
catch (System.IO.IOException e)
{
Console.WriteLine("Didn't work, yo");
throw (e);
}
}
// Body code
}
}
}
I am not sure what you are asking, but I think you should list the available ports before choosing one. The following code may work; it compiles, but it is not tested.
This is also not a good way to do it. A better way would be to list the ports before you plug in your device, then list the ports again to see the new port that showed up after you plugged in the device.
SerialPort port;
bool isCorrectPortFound = false;
// Try different ports until a device reacts when a character is written to it
while (!isCorrectPortFound)
{
// Get all open ports
string[] ports = SerialPort.GetPortNames();
// Menu choice for a port to select
char portSelect = '0';
// Write the port names to the screen
foreach (string s in ports)
{
portSelect++;
Console.Write(portSelect);
Console.Write(". ");
Console.WriteLine(s);
}
Console.WriteLine();
Console.Write("Select from port 1 to " + portSelect.ToString() + " > ");
int selectedPort = (Console.Read()) - '0'; // Character value of 1 to ...
try
{
// Assume selectedPort is a valid integer, set baud, etc. as per your choice.
port = new SerialPort(ports[selectedPort] /* COMportBaudRate,
COMportParity,
COMportDataBits,
COMportStopBits */);
// OK, port is open, write to the device. The device
// must respond visually, blinking a LED or something.
port.Write("A");
Console.WriteLine();
Console.Write("Did the device get your message? (y n) > ");
int a = (Console.Read()) - 'a';
if (a + 'a' == 'y')
isCorrectPortFound = true;
else
{
port.Close();
port = null;
}
}
catch (Exception ex)
{
// Display a message box, exit, etc.
}
}
// Do other stuff

Categories

Resources