First I'd like to say sorry for doubleposting. I'm afraid the context and questions I posted before didn't clearify it enough and although one of the solutions worked I am still struggling to grasp the concept, that's why I decided to make a new one. Also because there are apparently many different opinions of how it should be done in the right manner.
First here is a clean running code example that I`m using to figure this out:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
bool sent = false;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
if(trimmedMsg.Contains("*")) button_sendMsg.Enabled = true;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private void button_sendMsg_Click(object sender, EventArgs e)
{
send(textBox_message.Text);
button_sendMsg.Enabled = false;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
send(textBox_message.Text);
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
void send(String message)
{
serialPort.Write(message);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
It is a very simple GUI with a richTextBox to receive messages, a textbox to enter a message and two buttons that will send the message in two different ways.
The first case, where the user is sending the message manually is simple. Just deactivate the button so you can't spam it. The second case where I'm trying to send multiple messages in a loop seems way more complicated than I initially thought.
I try to outline my needs:
Using the localHandler in the approved solution from my other question works and for one simple case that's fine but I quickly realized that it is not flexible enough. I tried to figure it out but didn't get anywhere.
In my code example that I posted above, I separated the serialPort.Write in it's own method. I'm thinking something in the terms of this:
UI is running in it's own thread and serialPort_DataReceived is running in it's own thread, which it is doing anyways as my reasearch showed. So now when I'm receiving data everything is fine, the UI gets updated everytime I receive a message from the serial port. Now for the sending part I guess the best way is to give it it's own thread as well? So I can simply pause the thread where my message is being sent, wait for a reply on the main thread and then continue. Or which other concept would fulfill my need here?
I'm new to object-oriented programming, most of the stuff I have done so far is C based so I could use any help here. Thanks for considering and again, sorry for the double post. I just hope my question is more clear now.
After 4 days of almost no sleep I figure it out and want to post the solution to anybody who is trying to have some sort of a flow control in their serial communication. In the end I was using async methods. I think this is as simple as it can get for somebody who doesn't have a lot of C# experience. Here is the code for the form:
namespace serialInterfaceTest
{
public partial class Form1 : Form
{
string serialDataIn;
public Form1()
{
InitializeComponent();
serialPort.PortName = "COM3";
serialPort.BaudRate = 9600;
serialPort.Open();
}
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
serialDataIn = serialPort.ReadExisting();
this.Invoke(new EventHandler(saveData));
}
public void saveData(object sender, EventArgs e)
{
string trimmedMsg = serialDataIn;
richTextBox.Text += trimmedMsg;
}
private void richTextBox_TextChanged(object sender, EventArgs e)
{
richTextBox.SelectionStart = richTextBox.Text.Length;
richTextBox.ScrollToCaret();
}
private async void button_sendMsg_Click(object sender, EventArgs e)
{
button_sendMsg.Enabled = false;
await Task.Run(() => send(textBox_message.Text));
button_sendMsg.Enabled = true;
//WAIT FOR RESPONSE BEFORE ALLOWING THE USER TO SEND ANOTHER MESSAGE
}
private async void button_loopMsg_Click(object sender, EventArgs e)
{
button_loopMsg.Enabled = false;
for (int i = 0; i < 10; i++)
{
await Task.Run(() => send(textBox_message.Text));
//WAIT FOR RESPONSE BEFORE CONTINUING THE LOOP
}
button_loopMsg.Enabled = true;
}
private async Task send(String message)
{
serialDataIn = "";
serialPort.Write(message);
while (!serialDataIn.Contains("*"))
{
//PROCESS ANSWERS HERE
serialDataIn = serialPort.ReadExisting();
if (serialDataIn.Contains("*"))
{
this.Invoke(new EventHandler(saveData));
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (serialPort.IsOpen)
{
try
{
serialPort.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
}
I have the async method send data, and the two buttons are async as well. When I press them I'm just waiting for the task to complete before another input is allowed. I think this should be a good starting point for other projects as well. UI stays responsive, messages don't get queued up. The richTextBox on the UI get`s updated via Invoke so messages are displayed as soon as they arrive.
Here is the test code for an arduino:
#define println Serial.println
char serialIn;
String appendSerialData;
void setup()
{
Serial.begin(9600);
}
void loop()
{
appendSerialData = "";
while (Serial.available() > 0)
{
serialIn = Serial.read();
appendSerialData += serialIn;
}
/* asterisk functions as message identifier */
delay(1000);
if (appendSerialData != "") println(appendSerialData + " *");
for (int i = 0; i < 5; i ++)
{
println(i);
}
delay(100);
}
If there are any improvements I can make to this I`m happy to hear about it.