I am trying to perform a control where I receive information sent by Arduino.
This information is of type string and format VX 1.987 or VY 0.123 and are sent at high speed. After they are filtered and treated, they update the textbox in my program, and in this way what I see in the TextBox is the last information received.
What happens with my code is that at a certain point, for example, were sent 1000 lines by the Arduino, my program will update up to 600 and then stop showing the updates. I know that in the receive buffer are the other 400 rows, but they were not shown.
Summarizing how do I make memo time be receiving the information by serial and reading and putting in the textbox? Could this be a processing time issue? How to solve?
namespace ControleCaseiro
{
private void Form1_Load(object sender, EventArgs e)
{
serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
DataIn = serialPort1.ReadLine();
this.Invoke(new EventHandler(MostraDados));
}
catch(Exception ex)
{ }
}
private void MostraDados(object sender, EventArgs e)
{
switch (DataIn)
{
case "19\r":
textcontrole.Text = "XOFF";
break;
case "17\r":
textcontrole.Text = "XON";
if (botãoEnviaGcode)
{
LinhaArquivo++;
EnviaGcode();
}
break;
default:
FiltroDadosRecebidos(DataIn);
break;
}
}
private void FiltroDadosRecebidos(string valorRecebido)
{
string eixo="";
int tamanho=0;
string valorEixoAtual="";
if (valorRecebido.Length > 1)
{
tamanho = valorRecebido.Length;
eixo = valorRecebido.Substring(0, 2);
switch (eixo)
{
case "VX":
valorEixoAtual = valorRecebido.Substring(2, tamanho - 3);
txtPosAtualX.Text=valorEixoAtual;
break;
case "VY":
valorEixoAtual = valorRecebido.Substring(2, tamanho - 3);
txtPosAtualY.Text=valorEixoAtual;
break;
case "VZ":
valorEixoAtual = valorRecebido.Substring(2, tamanho - 3);
txtPosAtualZ.Text=valorEixoAtual;
break;
default :
DadosRecebidos.Items.Add(DataIn);// se não for dados referentes aos eixos, vai mostrar na listbox “DadosRecebidos”
break;
}
}
}
}
As I have already described in the beginning, what it is that the Arduino is sending the information and after a while, everything to and the information is not shown in the TextBox. What it looks like is that while OA SerialPort is receiving data, the event "SerialDataReceivedEventArgs " causes the program to show the information, when it is out of receive, no longer shows and the information is accumulated in the serialport buffer.
The issue is likely that you're not handling all the data in the receive buffer. You're only extracting one line of the receive buffer even though there may be multiple lines waiting to be processed. You could try changing your handler to something like this:
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
while (serialPort1.BytesToRead > 0)
{
DataIn = serialPort1.ReadLine();
this.Invoke(new EventHandler(MostraDados));
}
}
catch (Exception ex)
{ }
}
Related
So I am sending from an Arduino Uno an int from potentiometer(0 - 1023) and when I am reading it and print it in a label, there are no numbers.And I read somewhere that I need to read the bytes, how I am going to do that?
namespace Receiver
{
public partial class Form1 : Form
{
SerialPort port;
UITimer _timer = new UITimer();
public Form1()
{
InitializeComponent();
if (port == null)
{
port = new SerialPort("COM11", 9600);//Set your board COM
port.Open();
}
}
private void Form1_Load(object sender, EventArgs e)
{
_timer.Interval = 200;
_timer.Tick += _timer_Tick;
_timer.Enabled = true;
_timer.Start();
}
private void _timer_Tick(object sender, EventArgs e)
{
string a = port.ReadExisting();
afisare.Text = a;
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
_timer.Stop();
if (port != null && port.IsOpen)
{
port.Close();
}
}
}
}
First, make sure that you are using the correct Baud rate for your serial communication or otherwise you will receive unreadable data.
Your code is only missing a correct interpretation of the incoming data. On top of that I would recommend removing the timer and using the built-in DataReceived event. That means you can delete all your timer related code and add an event handler to your SerialPort initialization:
if (port == null)
{
port = new SerialPort("COM11", 9600); //Set your board COM
port.DataReceived += DataReceivedEvent;
port.Open();
}
Then you of course have to declare your DataReceivedEvent handler. Since you said that your potentiometer can contain values ranging from 0-1023 and you didn't provide your Arduino code, I'm assuming that that's the only thing being send over the port.
This would mean you are sending 2 bytes every cycle which need to be parsed back to an integer.
This works by performing a left shift of your two received bytes.
private void DataReceivedEvent(object sender, SerialDataReceivedEventArgs e)
{
SerialPort senderPort = (SerialPort)sender;
byte[] buffer = new byte[2];
if (senderPort.Read(buffer, 0, 2) != 0)
{
int data = (int)buffer[0] << 8 | buffer[1];
Console.WriteLine("Received data: {0}", data);
}
}
If you want to use div's Answer on the C# side, you have to send those two bytes as well.
That gives you a bit more precision than in your comment (dividing by 4 and multiply by 4.015 --why??--)
Using the corresponding shift operation:
void loop() {
int a= analogRead(A0);
Serial.write(a>>8);
Serial.write(a & 0xFF);
delay(200);
}
You must be sure that you use the c# DataReceivedEvent trigger, when both bytes are available:
https://learn.microsoft.com/de-de/dotnet/api/system.io.ports.serialport.bytestoread
Created a simple window form to receive and process data from serial port
also logging the data in textfile and displaying in the rich-text box.
For some reason, it is going into unresponsive state and not able to execute user action and freeze.
According to my understanding, earlier code was using two threads one UI and another one for data received event. Is this correct
But now as i am using background worker, it should create anther thread to process and append in the log and richtext box. Is this correct?
This is my first project in c# so pardon me if it is already answered somewhere else as i am unable to correlate this situation with the given answers and not able to implement them.
Is it a good idea to background worker if not then how can i solve this problem.
Thanks in advance.
Earlier the entire application was running on UI thread. Now i have tried to use background to create a new thread to store data receiving from serial port and processing inside that thread
//earlier code
void DataReceived_Event(object sender, SerialDataReceivedEventArgs e)
{
if (Port.IsOpen)
{
int bytes = Port.BytesToRead;
byte[] buffer = new byte[bytes];
Port.Read(buffer, 0, bytes);
receivedBytes.AddRange(buffer);
ProcessRecievedBytes(null);
}
}
//latest code with background worker
void DataReceived_Event(object sender, SerialDataReceivedEventArgs e)
{
if (Port.IsOpen)
{
int bytes = Port.BytesToRead;
byte[] buffer = new byte[bytes];
Port.Read(buffer, 0, bytes);
receivingBytes.AddRange(buffer);
if (!essentialBgWorker.IsBusy)
{
receivedBytes.AddRange(receivingBytes);
receivingBytes.Clear();
essentialBgWorker.RunWorkerAsync();
}
}
}
private void essentialBgWorker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (byte hexByte in receivedBytes)
{
//color = Color.Gray;
if ((Config.Data.Serial_DisplayLevel == GLOBAL.HEX_LEVEL_NONE))
{
if ((hexByte == '\n') || ((hexByte >= 0x20) && (hexByte <= 0x7E)))
{
String tmpString = new String((char)hexByte, 1);
//essentialBgWorker.ReportProgress(0, tmpString);
//in here i am putting in the log file and appending in the richtext box
PreprocessAppend(tmpString, Color.Black, false);
}
}
}
process.ProcessData(receivedBytes);//in here i am processing the data
receivedBytes.Clear();
}
private void essentialBgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
string str = e.UserState.ToString();
logWriter.Write(str);
logWriter.Flush();
if (e.ProgressPercentage == 0)
{
AppendSerial(str, Color.Black, false);
}
else if (e.ProgressPercentage == 1)
{
AppendSerial(str, Color.Black, false);
}
}
SerialTab.SerialPort.AppendSerial += delegate (string data, Color txtColor, bool newLineCheck)
{
this.BeginInvoke((MethodInvoker)(delegate ()
{
if (newLineCheck && (serialTextBox.Text != "") && (serialTextBox.Text[serialTextBox.TextLength - 1] != '\r') && (serialTextBox.Text[serialTextBox.TextLength - 1] != '\n'))
{
data = "\n" + data;
}
AppendTextbox(serialTextBox, data, txtColor);
}));
};
void AppendTextbox(RichTextBox tb, string data, Color txtColor)
{
if (data == null)
{
return;
}
int start = tb.TextLength;
tb.AppendText(data);
int end = tb.TextLength;
// Textbox may transform chars, so (end-start) != text.Length
tb.Select(start, end - start);
tb.SelectionColor = txtColor;
//reset color to defaults
tb.SelectionLength = 0;
tb.SelectionColor = serialTextBox.ForeColor;
//move caret to bottom of page
ScrollToBottom(tb);
//ensure text buffer stays below 15000 characters
checkTextBoxLength(tb);
}
void checkTextBoxLength(RichTextBox box)
{
//ensure text buffer in text box gets too large
if (box.Text.Length > 15000)
{
box.ReadOnly = false;
box.SelectionStart = 0;
box.SelectionLength = box.TextLength - 10000;
box.SelectedText = "";
box.ReadOnly = true;
}
}
Don't access windows controls from any thread other than the one that created them. When using a BackgroundWorker:
add the backgroundworker to the form designer
set its ReportsProgress property to true
attach an event handler to DoWork and another to ProgressChanged
in the DoWork event, do your background work such as reading from the serial port. Any time you want to update a windows control, call the backgroundworker ReportProgress() method with an object argument for the user state that contains some data you want to use in the control
I tend to use the progress int to also specify something, such as running a switch statement to chose one of several text boxes to update
The background worker will correctly ensure the ProgressChanged event is executed by the UI thread, meaning you can update controls from within it
Looking at your code, it seems you were going along these lines because I can see a commented out ReportProgress call. Show the contents of your ProgressChanged event handler. Make absolutely sure the only time you ever access any windows control (read or write any property or call any method) is in ProgressChanged, NOT DoWork
Edit/Update
Now I can see more of your code, it looks like you've embarked on a path of making it all way too complicated. I'll make another answer
I see what you are trying to do and you kind of have the right idea, I think you are just having a threading issue. Do you have the code for your PreprocessAppend method? You probably need to use BeginInvoke in that function and that should fix your issue.
Personally, I don't think you need a backgroundworker, as the Serial DataReceived event is threaded. I would recommend what the other answer said and use ReadExisting as that will grab all the characters in the received buffer and you can determine what to do from there. Just be careful because the DataReceived event can fire whenever it determines is a good time, so you could only get a partial line back. If you go this route you'll have to build your string until you know you have the whole thing and then process it.
In my example below, I'm expecting a Line Feed as my terminator so I build a string until I have the whole line then I process it.
//private variables
private SerialPort sp;
private StringBuilder sb;
private char LF = (char)10;
//function to initialize all my objects
public void init_state_machine()
{
sp = new SerialPort(currentSettings.ComPort, currentSettings.BaudRate);
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
sb = new StringBuilder();
sb.Clear();
}
private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string currentLine = "";
string Data = sp.ReadExisting();
foreach (char c in Data)
{
if (c == LF)
{
sb.Append(c);
//because it's threaded its possible to enter this code while processing so we will
//clear our string building immediately
currentLine = sb.ToString();
sb.Clear();
//process your line here or whatever
processReceivedData(currentLine);
}
else
{
sb.Append(c);
}
}
}
//this is where you process the response. For a simple example I just append the string
//to our textbox, but you could do any computations in here.
private void processReceivedData(string s)
{
this.BeginInvoke((MethodInvoker)delegate
{
serialTextBox.Text += s;
});
}
I recommend you junk all of your existing code, and proceed with using something like this, derived from the example from MSDN
namespace WindowsFormsApp3
{
public partial class Form1 : Form
{
SerialPort mySerialPort = new SerialPort("COM1");
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mySerialPort.BaudRate = 9600;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
mySerialPort.RtsEnable = true;
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
mySerialPort.Open();
}
private void DataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
textBox1.InvokeIfRequired(ctrl => ctrl.AppendText(indata));
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
mySerialPort.Close();
}
}
public static class ControlHelpers
{
public static void InvokeIfRequired<T>(this T control, Action<T> action) where T : ISynchronizeInvoke
{
if (control.InvokeRequired)
{
control.Invoke(new Action(() => action(control)), null);
}
else
{
action(control);
}
}
}
}
Your DataReceived handler is quite complex. The MSDN example uses a far simpler interface where it takes care internally of the reading and buffering etc with ReadExisting. You might want to attach to it and just keep appending the data you received into some kind of buffer (stringbuilder?), and regularly inspect the buffer to see if it contains a full message that you want to process
i have develop application for getting weight from weigh Bridge machine using C#.Net. i am trying lot of ways but, doesn't read correct data format weight from weigh bridge machine. i am getting output like
00000001Kg00000001B00000001B00000001B00000001B00000001B00000001B00000001B
continuously get from serial port.i want to get weight from weigh bridge machine my code is listed below:
private void Form1_Load(object sender, EventArgs e)
{
string[] portNames = SerialPort.GetPortNames();
foreach (var portName in portNames)
{
comboBox1.Items.Add(portName);
}
comboBox1.SelectedIndex = 0;
}
private void button1_Click(object sender, EventArgs e)
{
if (_serialPort != null && _serialPort.IsOpen)
_serialPort.Close();
if (_serialPort != null)
_serialPort.Dispose();
_serialPort = new SerialPort(comboBox1.Text, BaudRate, Parity.None, 8, StopBits.One);
_serialPort.DataReceived += SerialPortOnDataReceived;
_serialPort.Open();
textBox1.Text = "Listening on " + _serialPort.PortName + "...\r\n";
}
private delegate void Closure();
private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
{
if (InvokeRequired)
BeginInvoke(new Closure(() => { SerialPortOnDataReceived(sender, serialDataReceivedEventArgs); }));
else
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
string str = System.Text.Encoding.UTF8.GetString(data);
textBox1.Text += str.ToString();
}
}
how could i get right weight for save it into my database? in Order to get the right weight it must be like
00000001Kg
at real time and change itself according to weigh scale weight.
Your last line of code is just appending everything to the textbox. You might want to clear it beforehand, and maybe try to check if the data you received is your undesired string (00000001B) and just don't add it.
To log data, you could use the TextChanged event of the textbox... just an idea...
I am receiving data on serial port 250 packets per second where each packet is of size 23 bytes. I am using following code handling the data received on serial port.
private SerialPort connectComPort = new SerialPort();
List<byte> receivedBytes1 = new List<byte>();
public Form1()
{
InitializeComponent();
connectComPort.DataReceived += new SerialDataReceivedEventHandler(receiveData);
//Background worker for parsing packet
m_oWorker = new BackgroundWorker();
m_oWorker.DoWork += new DoWorkEventHandler(m_oWorker_DoWork);
m_oWorker.ProgressChanged += new ProgressChangedEventHandler(m_oWorker_ProgressChanged);
m_oWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_oWorker_RunWorkerCompleted);
m_oWorker.WorkerReportsProgress = true;
m_oWorker.WorkerSupportsCancellation = true;
}
private void buttton_Click(object sender, EventArgs e)
{
m_oWorker.RunWorkerAsync();
}
void m_oWorker_DoWork(object sender, DoWorkEventArgs e)
{
modprocessReceivedBuffer();
m_oWorker.ReportProgress(100);
}
private void receiveData(object sender, SerialDataReceivedEventArgs e)
{
while (connectComPort.BytesToRead > 0)
receivedBytes1.Add((byte)connectComPort.ReadByte());
}
private void modprocessReceivedBuffer()
{
while (1 == 1)
{
if (receivedBytes1.Count() != 0)
{
var tiff = receivedBytes1.GetRange(0, (int)receivedBytes1[4]).ToList<byte>();
receivedBytes1.RemoveRange(0, (int)receivedBytes1[4]);
modifiedProcess(tiff);
}
else
{
Thread.Sleep(100);
}
}
}
Thus I am just queuing the data received on serial port in a list and I am running a process on background thread whose job is to parse packet. My question is their any better method than this to handle such large data. Currently its 250 packets/sec but this rate can be increased to 16000 packets/sec.
This (VB converted to C#) is what I would do. This will eliminate the issue you were going to eventually have with two threads accessing the list. I also changed the code to read all the bytes at once. There are comments in the code that point at areas to be addressed.
System.Threading.AutoResetEvent dataRcvd = new System.Threading.AutoResetEvent(false);
private void receiveData(object sender, SerialDataReceivedEventArgs e)
{
dataRcvd.Set();
}
private void modprocessReceivedBuffer()
{
while (1 == 1) {
dataRcvd.WaitOne();
while (connectComPort.BytesToRead > 0) {
byte[] buf = new byte[connectComPort.BytesToRead];
int bytsRead = connectComPort.Read(buf, 0, buf.Length);
if (buf.Length != bytsRead) {
Array.Resize(ref buf, bytsRead);
}
//what if there is more than one message in receivedBytes1
if (receivedBytes1.Count() != 0) {
//I think a check is needed for enoung bytes in receivedBytes1????????
var tiff = receivedBytes1.GetRange(0, Convert.ToInt32(receivedBytes1(4))).ToList<byte>();
receivedBytes1.RemoveRange(0, Convert.ToInt32(receivedBytes1(4)));
modifiedProcess(tiff);
}
}
}
}
I have a serialport dataRecived event with 2 methods inside it. The LogFile is loging data and drawSetpoint is drawing a graph.
public void serialPort1_DataRecived (object sender, SerialDataReceivedEventArgs e)
{
DateTime time = DateTime.Now;
string Rdata = serialPort1.ReadLine();
LogFile(Rdata, (int)numericSetpoint.Value);
drawSetpoint(time,numericSetpoint.Value.ToString());
}
Both methods take the second argument from a numericUpDown control which looks like this
public void numericSetpoint_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13)
{
if (serialPort1.IsOpen)
{
int setpoint = (int)numericSetpoint.Value;
//send to serial port .....
}
The problem is both methods in my data recived event read digits as I type them. For example, if I type 150, LogFile will show 1,15,150 and the draw function will start drawing 1,15,150. I would like both functions to take the value from the numericSetpoint control after the enter key is pressed so i get the whole value. How could I do that ?
You are using the KeyPress event. Instead consider using the ValueChanged event which only fires when enter is pressed or the user leaves the control https://msdn.microsoft.com/en-us/library/system.windows.forms.numericupdown.valuechanged%28v=vs.110%29.aspx
It sounds like your data rx event is firing while your typing. I would try filling a buffer in your data rx event and hold off on any logging or plotting until you've sent your data. There are bunch of "safe" ways to do this but the core logic is as follows:
byte[] buffer = new byte[MAX_BUFFER];
private volatile bool _ready = false;
private Object _lock = new Object();
public void serialPort1_DataRecived (object sender, SerialDataReceivedEventArgs e)
{
DateTime time = DateTime.Now;
// Either read as bytes or convert string to bytes, add to your buffer
// string Rdata = serialPort1.ReadLine();
lock(_lock )
{
if(_ready)
{
_ready = false;
var myData = your buffer as string
// clear buffer
LogFile(myData, (int)numericSetpoint.Value);
drawSetpoint(time,numericSetpoint.Value.ToString());
}
}
}
...
public void numericSetpoint_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13)
{
if (serialPort1.IsOpen)
{
int setpoint = (int)numericSetpoint.Value;
//send to serial port .....
lock(_lock ) { _ready = true; }
}
}
}