High speed serial communication over serial port in C# - c#

I am working with visual studio 2019. I am sending data from microcontroller over uart/serial port to the PC and I want to read it on the PC side.
The baud rate is 9600. I am using the following code. However, its very slow. I need to be able to read the code with very high speed (comparable to the baud rate I use).
At present, I am getting 2-3 packets per second. The timer interval is set at 10ms but even if I change it to 1ms, there is no difference. I cant figure out what am I doing wrong. Help will be greatly appreciated.
Regards,
Salman
public void readCapsule(SerialPort sp)
{
timer1.Enabled = false;
string headerStart = "";
string headerEnd = "";
List<Int32> newCoordinates = new List<Int32>();
headerStart = sp.ReadLine();
if (headerStart == "START")
{
for (int i = 0; i < 3; i++)
{
Int32 coords = sp.ReadByte();
newCoordinates.Add(coords);
}
tbRead.AppendText("X: " + newCoordinates[0].ToString() + " ");
tbRead.AppendText("Y: " + newCoordinates[1].ToString() + " ");
tbRead.AppendText("Z: " + newCoordinates[2].ToString() + Environment.NewLine);
headerEnd = sp.ReadLine();
newCoordinates.Clear();
}
}
private void timer1_Tick(object sender, EventArgs e)
{
readCapsule(spCapsule);
Application.DoEvents();
spCapsule.DiscardInBuffer();
timer1.Enabled = true;
}
My Microcontroller code is basically incrementing 3 variables and sending them in between the START and END header. I know that there are other lines being executed so the throughput wont be 9600 but the delay I experience is WAY too long to be contributed from the microcontroller side I think. The microcontroller code is below. I am using Atmega328p. I have verified on hyper terminal that the incoming data is way faster than the rate at which I read with my C# code:
while (1) {
counter++; val1 = val1 + 3; val2 = val2 + 3; val3 = val3 + 3;
if(counter >= 100) {
counter = 0;
val1 = 1; val2 = 2; val3 = 3;
}
transmitUart0('S'); transmitUart0('T'); transmitUart0('A'); transmitUart0('R'); transmitUart0('T');transmitUart0(0x0A);
transmitUart0(val1);
transmitUart0(val2);
transmitUart0(val3);
transmitUart0('E'); transmitUart0('N'); transmitUart0('D'); transmitUart0(0x0A);
_delay_ms(10);
}

We don't know what your microcontroller is doing but on your C# code you're introducing a huge overhead on your processing by reading bytes one by one.
The solution to reducing this overhead is, obviously, do not read bytes one by one. Just get rid of the for loop, do a second sp.ReadLine() right after you detect the header, work with the bytes you get to store the coordinates and discard the rest.
If this approach is not fixing your problem, then try to square it: read more commands in one go and process them. At this point, it might be easier to change the way the microcontroller works I guess.
EDIT: Now that you have included more details, and as you have already realized, my comments above are not really helpful.
)
First off, ReadLine() is known to be very slow, see for instance here.
What you're trying to do is not really that demanding if you work out the numbers. So the solution might be to use any other method, I would advise trying the BaseStream to implement your own way of reading between CRs (something similar to what the question linked above proposes).
Otherwise you can try the DataReceived event.
Finally, note that when you say:
The timer interval is set at 10ms but even if I change it to 1ms, there is no difference...
referring to the delay on your microcontroller. You got that the other way around: if you want to see better performance reading data on your PC you need to increase and not reduce this delay (otherwise you are sending even more data than the amount you can handle). But if ReadLines() is that slow, I doubt you can improve the performance exploiting this, unless you're willing to read one data sample every 3 or 4 seconds.

You should try to handle all the buffer data at once. So rather than reading byte by byte or line by line, subscribe to the event SerialPort.DataReceived and process the whole chunk at once.
See this example: https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.datareceived?view=netframework-4.8
private static void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
Console.WriteLine("Data Received:");
Console.Write(indata);
// Use BeginInvoke for synchonization!
}
For using BeginInvoke, see this other thread.

I have made some changes to the code as per the feedback. The delays are the same. Please see the code below:
public void readCapsule(SerialPort sp)
{
timer1.Enabled = false;
string headerStart = "";
string headerEnd = "";
List<Int32> newCoordinates = new List<Int32>();
headerStart = sp.ReadLine();
tbRead.AppendText(headerStart + Environment.NewLine);
}
private void timer1_Tick(object sender, EventArgs e)
{
readCapsule(spCapsule);
timer1.Enabled = true;
}

Related

Stopping all SSH Connections at once

I'm working on a program that's supposed to establish "n" many SSH connections with a remote Linux server, and run time consuming commands on each connection. The "time consuming operation" is basically running a script that sets up Wireshark and listens to the traffic.
For this I'm using the SharpSSH library for C# and n many BackgroundWorkers as threads. Also for simplicity, the code below has n=2 BGW threads and SSH connections.
Code:
// runs when start is pressed
private void startButton_Click_1(object sender, EventArgs e)
{
sb = new StringBuilder();
DateTime timeNow = DateTime.Now;
clickTime = timeNow.ToString("yyyyMMddhhmmssfff"); // store the exact time of the click
bw = bwArray[0];
int index = 0; // ignore these 2 constants
foreach (BackgroundWorker bgw in bwArray)
{
if (bgw.IsBusy != true)
{
bgw.RunWorkerAsync();
// runWorkerAsync for every BackgroundWorker in the array
//index++;
}
}
}
// runWorkerAsync leads the BGWorker to this function
private void bw_doWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (worker.CancellationPending == true)
{
e.Cancel = true;
}
else
{
// let the UI know of button changes
int p = 0;
object param = "something"; // use this to pass any additional parameter back to the UI
worker.ReportProgress(p, param);
// UI notifying part ends here
// for the simplex case
if (numberOfConnections == 1)
startOperation();
// for the multiplex case
else if (numberOfConnections > 1)
{
//while (p < numberOfConnections)
//{
multiStartOperation();
// p++;
//}
}
Thread.Sleep(500);
}
}
// will be called for all ssh connections (in multiplex case)
private void multiStartOperation()
{
string[] command1Array = { "host2", "host2" };
string[] command2Array = { clickTime + "_h2", clickTime + "_h2" };
for (int index = 0; index < numberOfConnections; index++)
{
// shellArray is an array of SshExec objects
shellArray[index] = new SshExec(IPAddress, username, password);
try
{
shellArray[index].Connect();
}
catch (JSchException se)
{
Console.Write(se.StackTrace);
System.Windows.Forms.MessageBox.Show("Couldn't connect to the specified port.", "Connection Error!");
}
sb.Append(shellArray[index].RunCommand(command1Array[index]) + Environment.NewLine);
// first command is host3, or host4 etc.
// below is the time consuming command to run
string command = "./logcap.sh -c " + command2Array[index] + " -z";
// sb is a global stringBuilder object,
// to which the command output is appended
sb.Append(shellArray[index].RunCommand(command));
}
}
My problem is the following:
When I press the start button on the GUI, both connections should start and run the script. Whereas in the code given above, the first index of shellArray (which consists of SshExec objects) gets connected, prepares the commands and runs the time consuming command, at which point the program goes back to the UI, without even starting the second connection. This is obviously because of the for loop, but I couldn't figure out how to work around this yet.
I need to get the other backgroundworker to establish and run the second command with the second server, so that when I press the stop button on the GUI all connections and threads can stop all together.
PS: The commands will not stop running unless the user clicks stop, which sends a Ctrl-C signal to the server.
I'm relatively new to all the multithreading and networking concepts, so if there is any confusion or mistake please let me know.
Have a nice day.
Thank you for your answers, and the welcome. :)
The problem indeed was not being able to run multiple backgroundworkers at the same time.
I managed to solve the issue. It turns out that all I had to figure out was how to assign backgroundworkers to SSH connections. To do that, I created a class as follows:
class BGW
{
private BackgroundWorker bgw;
private int index;
//getters, setters, constructors...
}
After this, I converted bwArray which was an array of BackgroundWorkers into an array of BGW objects. At initialization, I assigned each BGW object an index.
Instead of having the stupid loop within multiStartOperation(), I sent an integer parameter to multiStartOperation() and that function used that index to reach the allocated backgroundworker.
So far it seems to work.
Have a nice day.

Serial Port Data Received only working when breakpoint set C#

I'm trying to read data from serial port. It reads data when I set breakpoint.
I have tried with parent delegate invoke, some delay also. It doesn't work for me.
Here is my code
Read code from class file:
public void comport_DataReceived2(object sender, SerialDataReceivedEventArgs e)
{
var bytes = comport.BytesToRead;
var buffer = new byte[bytes];
string test2 = comport.ReadExisting();
if (IsReadPDSS)
{
if(test2 != string.Empty && test2 != " " && test2.Length > 30)
{
test2 = test2.Substring(30);
test2.Replace("000000000000P0000W", "");
strReceived += test2;
}
}
else
{
strReceived = test2;
}
}
windows form retriving read data :
string ss=FormObj.strReceived.ToString();
When you do application debugging, your system is not entirely frozen. Only the application you debug. Thus, while your app is in a breakpoint, incoming data on the serial port still is being accumulated.
The control flow is a bit fuzzy (probably because you changed it over a few times while looking for the problem). As it is written now, you read the data from the serial port whenever the event is raised. It is not likely that 30 bytes have arrived at the time you read the data. If you break into the debugger and do single stepping, it is rather more likely that you will find more than 30 bytes in the receive buffer (depending on what your device which transmits does).
Hence, a better way to write the control flow would look like this:
public void comport_DataReceived2(object sender, SerialDataReceivedEventArgs e)
{
var bytes = comport.BytesToRead;
if( bytes > 30 )
{
var test2 = comport.ReadExisting();
// additional testing code as required...
}
}
Depending on how the event raising behavior works, you might need to accumulate the data yourself in an extra buffer if the event is not getting re-raised after being fired for the first time... But that should be easy enough to test and adapt.

C# - How to read from a SerialPort for an infinite amount of time

I have an arduino connected to my serialport, which generates numbers from 0 to 64 all the time.
I wanted to read these signals in c# and managed to attach them to a richtextbox.
Unfortunately at some point they stop being written in the box and i have to open the port again to append the text again to the box.
Here is a sample of the code:
private void btnOpenPort_Click(object sender, EventArgs e)
{
if (Arduino.IsOpen == false)
{
Arduino.BaudRate = 115200;
Arduino.PortName = cBPortWaehlen.SelectedItem.ToString();
Arduino.Open();
}
while (Arduino.BytesToRead != 0)
{
richTextBox1.AppendText(Arduino.ReadExisting());
}
}
I assumed the statement Arduino.BytesToRead would never turn false, as long as my arduino sends signals, but this seems not to be the case. How can I achieve that instead?
First of all, about any serial connection made in C# has a default event handler, called DataReceived. I believe that you can use it, and delete the while code block you have there.
Second, I think that the while block is too operation-intensive, so if you don't go with my first suggestion, try and place a Thread.Sleep(1000) inside your while, so it won't execute that many times. If you want to refresh the data every few milliseconds, replace Thread.Sleep(1000) by giving the amount of milliseconds that your prefer.
Hope this answered your question.
Later Edit:
The code you can have looks like this:
public void OpenArduinoConnection()
{
if(!arduinoBoard.IsOpen)
{
arduinoBoard.DataReceived += arduinoBoard_DataReceived;
arduinoBoard.PortName = "yourportname";
arduinoBoard.Open();
}
else
{
throw new InvalidOperationException("The Serial Port is already open!");
}
}
void arduinoBoard_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// your code here
}

serialport continuous real time data c#

Hi I'm trying to program a simple C# WPF that displays time information on a virtual scoreboard in real time from a timing system. I'm fairly new to programming so in depth explanation would be appreciated.
I have created a new thread to handle the incoming data from the COM port and as the app is developed this data will be interpreted. For now I just wanted to display the raw information (in hex) that is coming from the timer into a textbox. This works but not as intended. I am receiving tons of duplicate information, my only explanation is I am reading the data too slowly or its reading the same byte over and over. What I would like to happen is to take out each byte and display them, all controlled by one start/stop button.
Possible solutions include storing the entire buffer in a list or array which I'm not quite sure of yet, I don't want to add so many threads that the program freezes everything up.
Here is my code so far (I'm new to pretty much all the code I have written here, so if anything is bad practice please let me know):
public partial class MainWindow : Window
{
SerialPort comms;
Thread commThread;
bool flag;
string message;
public MainWindow()
{
InitializeComponent();
comms = new SerialPort();
}
private void PortControl_Click(object sender, RoutedEventArgs e)
{
if (!comms.IsOpen)
{
PortControl.Content = "Stop";
comms.PortName = "COM1";
comms.BaudRate = 9600;
comms.DataBits = 8;
comms.StopBits = StopBits.One;
comms.Parity = Parity.Even;
comms.ReadTimeout = 500;
comms.ReceivedBytesThreshold = 1;
commThread = new Thread(new ThreadStart(Handle));
comms.Open();
comms.DataReceived += new SerialDataReceivedEventHandler(ReadIn);
}
else
{
PortControl.Content = "Start";
flag = false;
comms.DataReceived -= ReadIn;
commThread.Join();
comms.Close();
}
}
private void ReadIn(object sender, SerialDataReceivedEventArgs e)
{
if (!commThread.IsAlive)
{
flag = true;
commThread.Start();
}
}
private void Handle()
{
while (flag)
{
if (comms.IsOpen)
{
try
{
message = comms.ReadByte().ToString("X2");
Dispatcher.BeginInvoke((Action)(() =>
{
ConsoleBox.Text += message + " ";
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
}
Here is one solution.
The serial port is receiving the data in its own thread, and you should read the incoming bytes in the data received handler.
I propose to read the data and add it to a thread-safe FIFO list in the data received handler and read the data from the list in the main thread.
See my solution in post Serial port reading + Threads or something better?

C# Continuously / uninterrupted reading data (from third party SDK)

Intro:
I am developing software that uses motion trackers to analyse human motor systems. Currently I am implementing hardware from xsens and using their SDK to receive data from their wireless sensors.
The SDK offers a COM interface with a "getData" method which you call to receive the currently available xyz axis data (simplified). If you do not call getData, you skip that "beat" so you will be missing data, there is no caching in their hardware/SDK.
Problem:
My problem is that I need to get data at a rate of at least 75Hz, preferably a bit more, but 75 would be acceptable, but I am currently quickly dropping to just 20 signals per second...
If I remove the processing bit (see the sample below) I get perfect sample rates, so I think either the dequeue is causing the enqueue to pause. Or the "heavy" CPU load is causing all threads to wait. I have no idea how to figure out what is actually causing it, the profiler (EQATEC) just shows my "GetData" method is taking longer after a while.
Question:
What is the best technique to use to accomplish this? Why would my "reading" thread be interrupted/blocked? There must be more cases where people need to read from something without being interrupted, but I have been Googleing for 2 weeks now and apparently I can't find the correct words.
Please advise.
Thanks
Simplified code sample, version 4, using a MultiMedia timer (http://www.codeproject.com/Articles/5501/The-Multimedia-Timer-for-the-NET-Framework) and a BackgroundWorker
public class Sample
{
private MultiMediaTimer _backgroundGetData;
private bool _backgroundGettingData;
private BackgroundWorker _backgroundProcessData;
private ConcurrentQueue<double> _acceleration = new ConcurrentQueue<double>();
private void StartProcess()
{
if (_backgroundGetData == null)
{
_backgroundGetData = new MultiMediaTimer {Period = 10, Resolution = 1, Mode = TimerMode.Periodic, SynchronizingObject = this};
_backgroundGetData.Tick += BackgroundGetDataOnTick;
}
_backgroundProcessData = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = true};
_backgroundProcessData.DoWork += BackgroundProcessDataOnDoWork;
_backgroundGetData.Start();
}
private void BackgroundProcessDataOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
double value;
if (!_acceleration.TryDequeue(out value)) value = 0;
//Do a lot of work with the values collected so far,
//this will take some time and I suspect it's the cause of the delays?
}
private void BackgroundGetDataOnTick(object sender, EventArgs eventArgs)
{
if (_backgroundGettingData) return;
_backgroundGettingData = true;
//123 represents a value I am reading from the sensors using the SDK
double value = 123;
if (value == -1)
{
Thread.Sleep(5);
continue;
}
_acceleration.Enqueue(value);
if (_acceleration.Count < 5) continue;
if (!_backgroundProcessData.IsBusy)
{
_backgroundProcessData.RunWorkerAsync();
}
_backgroundGettingData = false;
}
}
I am seeing the problem here
_backgroundProcessDataThread.Start();
while (!_backgroundProcessDataThread.IsAlive){}
_backgroundGetDataThread.Start();
while (!_backgroundGetDataThread.IsAlive) {}
Well, you can see here that you are having infinite loop here and the second thread starts only after first has finished its work. i.e. first thread is done. This is in no way an ideal model.
Sorry, I recognized the issue later.
The problem is, _backgroundGetDataThread will start only after _backgroundProcessDataThread has done its work.

Categories