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.
Related
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;
}
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)
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
}
I have a USB device from which I need to read data via LibUsbDotNet.
If I use the following code after I've successfully opened the device...
protected UsbEndpointReader _libUsbReader = null;
protected UsbEndpointWriter _libUsbWriter = null;
.
IUsbDevice wholeUsbDevice = PhysicalLibUSBDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// This is a "whole" USB device. Before it can be used,
// the desired configuration and interface must be selected.
// Select config #1
wholeUsbDevice.SetConfiguration(1);
// Claim interface #0.
wholeUsbDevice.ClaimInterface(0);
}
// Create the reader and writer streams
_libUsbReader = PhysicalLibUSBDevice.OpenEndpointReader(LibUsbDotNet.Main.ReadEndpointID.Ep01);
_libUsbWriter = PhysicalLibUSBDevice.OpenEndpointWriter(LibUsbDotNet.Main.WriteEndpointID.Ep02);
_libUsbReader.DataReceivedEnabled = true;
_libUsbReader.DataReceived += OnDataReceived;
_libUsbReader.ReadThreadPriority = System.Threading.ThreadPriority.Highest;
_libUsbReader.ReadBufferSize = 32;
and define the method:
private void OnDataReceived(object sender, EndpointDataEventArgs e)
{
Console.WriteLine("Data received");
}
The event never fires. I know the packets are being received by my code as I have a USB analyser attached and can see them coming in.
If I change the code to remove the reliance upon the event callback and instead use the loop below on a background thread to read the reader object
while(true)
{
ErrorCode ec = ErrorCode.None;
Thread.Sleep(5);
int bytesRead;
byte[] buffer = new byte[32];
ec = _libUsbReader.Read(buffer, 1000, out bytesRead);
if(bytesRead>0) Console.WriteLine("Data Received");
}
Then everything works great.
I've tried playing around with the order I initialise, the values etc and can still get no joy. Can anyone suggest why the events aren't firing if I use the DataReceived event?
I'm using LibUsbDotNet 2.2.8.
I know this is old, but in your code:
...
_libUsbReader.DataReceivedEnabled = true;
_libUsbReader.DataReceived += OnDataReceived;
_libUsbReader.ReadThreadPriority = System.Threading.ThreadPriority.Highest;
_libUsbReader.ReadBufferSize = 32;
...
I have DataReceivedEnabled set after DataReceived (not sure if this makes a difference).
Also, I do not change thread priority and ReadBufferSize. The latter is 4096 by default. When data arrives, the EndpointDataEventArgs parameter to the event has the Count property with the actual number of bytes received.
Hope this helps.
Please try like this:
_libUsbReader.DataReceived += mEp_DataReceived;
private void mEp_DataReceived(object sender, EndpointDataEventArgs e)
{
try
{
Invoke(new OnDataReceivedDelegate(OnDataReceived), new object[] { sender, e });
}
catch
{
closeDevice();
}
}
When opening the endpoint for reading, you should define the endpoint type to interrupt, otherwise the Datarecieve event won't fire.
(Optionally define the size of the read buffer)
In your code you'll have to modify just this row:
_libUsbReader = PhysicalLibUSBDevice.OpenEndpointReader(LibUsbDotNet.Main.ReadEndpointID.Ep01, 64, EndpointType.Interrupt);
I think the sollution is that you should read the endpoint content at the event (or discard it). I had the same problem, when only printing at the console the message I only entered 1 time. When I process in the event the data on the endpoint with read command, the problem seems to be solved.
I'm more familiar with C language and recently I've been ask to do C# for serial communication.
Below is my code for receiving data from COM port:
public void RxData()
{
int i = 0;
int Data;
bool StartRx = false;
int timer;
while (true)
{
Data = sp.ReadByte();
if (Data == 0x01)
{
StartRx = true;
}
if (StartRx == true)
{
RxBuffer[i++] = Data;
}
if (Data == 0x04)
{
RxChkSum = RxBuffer[i - 2];
break;
}
timer++;
if(timer>100)
{
timer = 0;
break;
}
}
}
Above is the way I receive data starting with 0x01 and ends with 0x04.
I'm incrementing a timer to count til 100 and quit the while loop in case I don't receive any data. Some sort like a timeout.
But seems like the timer don't work. When I don't receive any data, my program just stuck in the while loop forever.
I know this is the way we write in embedded c programming. But is this the right way to write in C#?
I think you might be interested in handling data coming from the serial port using an event handler. In the .net SerialPort class, you can register an event handler for data received:
var sp = new SerialPort("COM1") {
BaudRate = 9600,
Parity = Parity.None,
StopBits = StopBits.One,
DataBits = 8,
Handshake = Handshake.None
};
sp.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
You can also set the ReceivedBytesThreshold property on the SerialPort, which determines when your event handler will get fired.
Then, you just set up your event handler to read data and do what you need with it:
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string data = sp.ReadExisting();
}
Using this type of pattern, you don't have to loop, you just set the threshold you need, and let the framework call your event handler when the serial port's got that many bytes ready for you.
Hopefully that helps and I haven't missed your point completely. :)
ReadByte is a synchronous call. It will only return when there is a byte read.
To have your attempt getting to work you can check for available data before reading:
if(sp.BytesToRead > 0)
{
Data = sp.ReadByte();
}
Besides that I prefer asynchronous reading as hmqcnoesy suggested.