SerialPort s = new SerialPort("COM32");
public MainWindow()
{
InitializeComponent();
s.DataReceived += dataAction;
}
private void dataAction(object sender, SerialDataReceivedEventArgs e)
{
var data = s.ReadLine();
File.AppendAllText(recordAllSerialPortPath, data);
if (data == "ABCD")
s.WriteLine("aaaa");
if(data=="aaa")
File.AppendAllText(loggerPath, data);
}
I get data from serial port, and I want to write all the data to recordAllSerialPortPath.
additionally I want to answer each line on diffrent way (write to SerailPort or write to logger).
It's most important to answer fast , and the problem now with my code it the append text to recordAllSerialPortPath takes time and i do it synchronized , so my code wait until the line write to recordAllSerialPortPath and then continue to answer.
If I move the "record" to the end of function that not help because the next line will wait until I finish to record this line.
How can I record each line but not wait to this in my code.
I know I can do Task.run(()=>File.AppendAllText(recordAllSerialPortPath, data)); But i not sure if it's the right sulution.
I will be happy get some way to fix my problem (advantages and disadvantages)
Related
I use GPS data and NMEA sentences.Even I only want to see and save the sentences which begins with "$GNGGA" and "$GNTVG" in my richtextbox, there are other sentences(lines) begining with different codes($GNGLL, $GLGSV, $GPGSV etc). What should I do to only get "$GNGGA" and "$GNTVG" sentences to Richtextbox?
Here is my code;
string[] gParca;
string gKG, gDB, gUydular, gYukseklik, gEnlem, gBoylam, gYataySapma, gKilitlenme, gVelocity, gSaat;
private void GPSVelocity(string NMEA2)
{
gParca = NMEA2.Split(new char[] { ',' });
switch(gParca[0])
{
case "$GNVTG":
gVelocity = gParca[7];
break;
}
private void GPSDataBilgisi(string NMEA)
{
gParca=NMEA.Split(new char[] { ',' });
switch (gParca[0])
{
//Global Positioning System Fix data
case "$GNGGA":
gParca[2] = gParca[2].Replace('.', ',');
gParca[4] = gParca[4].Replace('.', ',');
}
}
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
GPSDataBilgisi(serialPortGPS.ReadLine());
GPSVelocity(serialPortGPS.ReadLine());
}
private void GPSVel(string NMEA2)
{
if(checkBoxSave.Checked)
{
richTextBoxGPSData.AppendText(NMEA2);
}
}
private void GPSData(string NMEA)
{
if(checkBoxSave.Checked)
{
richTextBoxGPSData.AppendText(NMEA);
}
}
Disclaimer
As per OP's reply to my question, I am assuming serialPortGPS.ReadLine() works exactly like TextReader.ReadLine().
Since you want to filter the input you are getting, add a filter to the function that retrieves the data.
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
for(int i = 0; i < 2; i++)
{
string line = serialPortGPS.ReadLine();
if(line.StartsWith("$GNGGA"))
GPSDataBilgisi(line);
if(line.StartsWith("$GNVTG"))
GPSVelocity(line);
}
}
If the line starts with anything else (e.g. "HELLOIAMALINE"), neither if-check will pass and the code will not do anything with the line, it just moves on to the next one.
You can remove the switch statements in your code, they are no longer needed (of course keep the logic that is inside them!)
There are some caveats here, because I think your code and intention is a bit vague. If you can clarify any of these, I can update my answer.
Going by your question, I assume you do not want to call ReadLine() exactly twice (once for each method). I infer that you want to read every line individually (because that's how TextReader usually works), check if it starts with a "good" value, and then execute the function that needs it.
My original example specifically reads 2 rows and parses them. If you are instead trying to read all lines from the gps update, change the code as follows:
private void serialPortGPS_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string line;
while((line = serialPortGPS.ReadLine()) != null)
{
if(line.StartsWith("$GNGGA"))
GPSDataBilgisi(line);
if(line.StartsWith("$GNVTG"))
GPSVelocity(line);
}
}
Part of me wonders if reading directly from serialPortGPS is the correct approach. This is pure speculation, but I would expect your SerialDataReceivedEventArgs e to contain a property with the newly received data. It's not impossible that what you are currently doing is correct, but it is more unusual than using the event args that you receive from the update event.
You could use LINQ to immediately filter out the rows that you need (instead of having to manually iterate over them). However, Textreader implements a pattern where it only processes one line at a time (similar to yield return). If you use LINQ, you're going to end up processing all lines at the same time when you want to filter them. If you need to process line by line (e.g. because the data you're processing has a huge memory footprint), then you should avoid LINQ
If you want an example of LINQ:
List<string> theLinesThatIWant =
allTheLines.Where(line => line.StartsWith("$GNGGA")).ToList()
But like I said, only use this if you are able and willing to have all the data in memory at the same time.
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.
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
}
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.
I need to take incoming data from a serial port place the 'ReadExisting' string into a queue, and then de-que back into the user interface.
During the de-que process I will be formatting the incoming string to remove unwanted characters, add LineFeeds (there are no EOL characters in the incoming), etc. and post various parts of the string into several controls (listbox, textbox) for viewing.
This is as far as I have been able to get to create a string (RxString) from incoming data:
private void serialPort1_DataReceived(object sender,
System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = serialPort1.ReadExisting();
this.Invoke(new EventHandler(DisplayText));
}
I am able to get the incoming data, but when I try to format it and display it some of the data that comes in gets lost or dropped or something and the text gets unreadable. If I don't format the text & send it straight to a listbox the data is all there, but not useable because of all the extra code characters.
I'd like to handle the (DisplayText) using a backgroundworker so the serial data can come in to a queue so as to not be lost while the backgroundworker handles placing the info on screen. Unless theres a better way.
I assume that the data comes one line at the time, so you could always use the Queue-class and use the method Enqueue to add the incoming data.
And to get an item from the Background-worker, just use Dequeue
Here's a link to the MSDN-article about the Queue-class
http://msdn.microsoft.com/en-us/library/7977ey2c.aspx
Example:
private Queue<string> data = new Queue<string>();
private void Rx_GetData(e)
{
var rxString = e.ReadExisting();
data.Enqueue(rxString);
}
private void BackgroundWorker_DoWork()
{
while(rxConn.IsConnected) // Haven't worked with serial connections, so I don't know the proper property here..
{
if(data.Count > 0)
{
var str = data.Dequeue();
FormatString(str);
}
Thread.Sleep(10);
}
}