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);
}
}
}
}
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
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...
For our school project we have to write a code in c# to read and visualize serial data from a weather station.
The code that I have now:
public partial class MainWindow : Window
{
private SerialPort port;
DispatcherTimer timer = new DispatcherTimer();
private string buff;
public MainWindow()
{
InitializeComponent();
}
private void btnPort_Click(object sender, RoutedEventArgs e)
{
timer.Tick += timer_Tick;
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Start();
try
{
port = new SerialPort(); // Create a new SerialPort object with default settings.
port.PortName = "COM4";
port.BaudRate = 115200; // Opent de seriele poort, zet data snelheid op 9600 bps.
port.StopBits = StopBits.One; // One Stop bit is used. Stop bits separate each unit of data on an asynchronous serial connection. They are also sent continuously when no data is available for transmission.
port.Parity = Parity.None; // No parity check occurs.
port.DataReceived += Port_DataReceived;
port.Open(); // Opens a new serial port connection.
buff = "";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[128];
int len = port.Read(buffer, 0, buffer.Length); // .Read --> Reads a number of characters from the SerialPort input buffer and writes them into an array of characters at a given offset.
if (len > 0)
{
string str = "";
for (int i = 0; i < len; i++)
{
if (buffer[i] != 0)
{
str = str + ((char)buffer[i]).ToString();
}
}
buff += str;
}
// throw new NotImplementedException();
}
private void timer_Tick(object sender, EventArgs e)
{
try
{
if (buff != "")
{
textSerial.Text += buff;
buff = "";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This code has the following output:
Humidity: 33 %
Temperature: 25.7 deg C
Visible light: 112 lx
Infrared radiation: 1802.5 mW/m2
UV index: 0.12
CO2: 404 ppm CO2
Pressure: 102126 Pa
I have to store all the numbers in variables because I need to use them in my GUI.
To get all the numbers from the output :
public string[] convertNumbers(string inp)
{
string pattern = "[+-] ?\\b\\d + (\\.\\d +)?\\b";
Regex rgx = new Regex(pattern);
string input = inp;
string[] result = rgx.Split(input, 3);
return result;
}
The regex expression works fine and gives me the exact numbers.
I would like to save them into an array with 7 doubles.
What is the most efficient way to transfer the serial output into variables ?
Can somebody help me out ?
I'm making a C# program to work like a poor oscilloscope. I have an Arduino which sends to serial (Serial.write(analogRead(A0)) ) and then the C# has a thread which reads each ms a sample while the main thread refreshes the Chart. My doubt is, should I use Serial.write or Serial.print ?
Is it possible to get 2kS/s ? I'm using the baud rate of 115200 and here is the code.
namespace TEST
{
public partial class Form1 : Form
{
static int buffer_size = 1024;
public static string comboBoxText;
public static int[] buffer = new int[buffer_size];
IEnumerable<int> yData;
static int[] range = Enumerable.Range(0, buffer_size).ToArray();
IEnumerable<int> xData = range;
public static bool flag = true;
public Form1()
{
Random rand = new Random();
InitializeComponent();
for (int c = 0; c<buffer_size;c++) {
buffer[c] = 0;
}
Thread thread1 = new Thread(fillBuffer);
thread1.Start();
comboBox1.Items.Add("Select");
foreach (string s in SerialPort.GetPortNames())
{
comboBox1.Items.Add(s);
}
}
static public void fillBuffer()
{
Thread.Sleep(1000);
SerialPort serialPort1 = new SerialPort();
serialPort1.PortName = "COM5";
serialPort1.BaudRate = 115200;
serialPort1.Open();
while (true)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
yData = buffer;
chart1.Series[0].Points.DataBindY(yData);
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
try {
comboBoxText = comboBox1.Text;
}
catch
{
MessageBox.Show("Porta Inválida");
return;
}
comboBox1.Enabled = false;
}
}
Is there anything I can do to sample each 0.5ms and then display the sample as a collection of points ? I'm not getting good results. If anyone can help, thank you!
At a Baudrate of 115200 and a good processor speed, your algorithm seem fast enough. But one of the things that can slow down the speed is the interval of timer1. It should be set to the lowest possible. Also for the difference between Serial.Write and Serial.Print check out this forum . Also using the .net inbuilt serial port event handler would save you a lot of stress and is also a faster and more efficient solution. You might want to check it out here
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; }
}
}
}