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
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 know this questions gets asked a bit (at least from what I found here so far), but I can't really wrap my head around it. Already tried it with the example from msdn but still no succes. Here is what I'm trying to do: I have a USB-Counter connected to a TLL-ruler. I want to read the value constantly in a loop and write the readout to a textbox without blocking the main UI. I know from other questions that I should use Invoke or Backgroundworker, but have not really found an example which I understood and could use to adjust my code accordingly. The code without any modification, to keep it simple, is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace USB_Counter
{
public partial class Form1 : Form
{
[DllImport("HS_UC.dll", EntryPoint = "HS_UC_Close")] //+further DLL imports (driver for USBCounter)
public static extern int HS_UC_Close(int CounterNo);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) //initialize COunter
{
int A = HS_UC_Init(1, 3);
string B = Convert.ToString(A);
MessageBox.Show(B); //to check if there is no error
}
private void button2_Click(object sender, EventArgs e)
{
HS_UC_SetDistCode(1, 20, 40, 4, 0, 0); //set Reference
HS_UC_SetRefMode(1, 1);
}
private void button3_Click(object sender, EventArgs e)
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.Text = "Das ist: " + C;
textBox1.Update();
} while (a == 1);
}
}
}
Now this works as intendet, but as mentioned it blocks the main UI thread (obviously). If anyone found a similar question with some helpful tips to get started with this multithreading topic or any helpful tips regarding my question directly, that would be greatly appreciated.
Best regards from Berlin,
Chris
Update: got it working with following attempt:
private void Counter_Read()
{
int a = 1;
do
{
int A = 0;
int B = 0;
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
UpdateTextBox(C);
} while (a == 1);
}
public void UpdateTextBox(decimal C)
{
if (InvokeRequired)
{
this.Invoke(new Action<decimal>(UpdateTextBox), new object[] { C });
return;
}
textBox1.Text = "Das ist: " + C;
textBox1.Update();
}
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(() => Counter_Read());
t.Start();
}
From that I get a decimal output which i constantly updating and still am able to utilize the other buttons.
outsource the loop code into a method. Inside the method you will need to use BeginInvoke to write to the TextBox
private void DoTheLoop()
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.BeginInvoke(new Action(()=>{textBox1.Text = "Das ist: " + C;}));
} while (a == 1);
}
First version using a normal Thread:
Create a Thread and start it with the new method when the button3 is clicked
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(()=>DoTheLoop());
t.Start();
}
This should not block your GUI and the textbox will show the values
Second Version using a BackgroundWorker:
Create a BackgroundWorker and register the DoWork event:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
private void Form1_Load(object sender, EventArgs e)
{
worker.DoWork += Worker_DoWork;
}
inside the eventhandler call the same method DoTheLoop():
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
DoTheLoop();
}
start the worker in the button click event:
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
Same result in the end :)
You may want to take a look a this link MSDN.
However, a quick tip would be to register a method for the DoWork event and then execute the RunAsynchronously method.
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 the following code and i would like to split the incoming serial data into pieces and display the individual parts in separate textboxes. The incoming serial data is
angle1:angle2:value1:value2:value3:value4
where
angle1 has values ranging from -90 to 90
angle2 ranges from 0 to 90
value1 value2 value3 value4 from 0 to 1024
So I would like to display each of these in different textboxes.
This is what I have so far which displays the whole incoming string in a single textbox.
Any help, idea and tip is highly appreciated since I'm a complete newbie to c#.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public static System.IO.Ports.SerialPort port;
delegate void SetTextCallback(string text);
private BackgroundWorker hardWorker;
private Thread readThread = null;
public Form1()
{
InitializeComponent();
hardWorker = new BackgroundWorker();
string[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
}
private void Form1_Load(object sender, EventArgs e) {}
private void SetText(string text)
{
if (this.receiveText.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
//this.receiveText.Text += "";
this.receiveText.Text += text;
this.receiveText.Text += Environment.NewLine;
}
}
public void Read()
{
while (serialPort2.IsOpen)
{
try{
if (serialPort2.BytesToRead > 0)
{
Thread.Sleep(1000);
string message = serialPort2.ReadLine();
this.SetText(message);
}
}
catch (TimeoutException) { }
}
}
private void start_Click(object sender, EventArgs e)
{
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
if (comboBox1.SelectedIndex > -1)
{
string port = (string)comboBox1.SelectedItem;
serialPort2.PortName = port;
serialPort2.BaudRate = 9600;
serialPort2.DtrEnable = true;
serialPort2.ReadTimeout = 1000;
serialPort2.WriteTimeout = 500;
serialPort2.Open();
readThread = new Thread(new ThreadStart(this.Read));
readThread.Start();
this.hardWorker.RunWorkerAsync();
}
}
private void receiveText_TextChanged(object sender, EventArgs e)
{
receiveText.SelectionStart = receiveText.Text.Length;
receiveText.ScrollToCaret();
receiveText.Refresh();
}
}
}
SOLVED by changing the following:
private void SetText(string text)
{
if (this.receiveText.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
string[] newData = text.Split(':');
boxLR.Text = newData[0];
boxUD.Text = newData[1];
boxldrright.Text = newData[2];
boxldrleft.Text = newData[3];
boxldrup.Text = newData[4];
boxldrdown.Text = newData[5];
}
}
Thank you everyone for your help.
First thing first, you need to split your string to get all different values as individual string.
In your SetText() function;
string[] slist = text.Split(':');
Now if you have all required text box then you can assign value for string array to different text box.
Or else
if you are creating text box runtime then you have to create new text box as per length of slist array, then assign value to new text boxes.
Hope it helps..!!!
You can use the this.receivedText.Text.Split(':') function to split the received text string into an array of string, which you then can use to assign to the different text boxes individually.
In addition to this, you could also create a method, that takes the array as a parameter, to check the various value constraints.
If the length of the values are the same you can use
public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count )
I want to display graph of data coming from serial port.This data is continues having \r in between. For drawing graph I am using ZedGraph.dll. My code is as follows
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ZedGraph;
using System.IO.Ports;
namespace FilterRealTimeCSharp
{
public partial class Form1 : Form
{
List<double> measures;
/// The ZedGraph curve
LineItem myCurve;
BackgroundWorker worker;
// ZedGraphControl zedGraphControl1;
GraphPane myPane;
public Form1()
{
InitializeComponent();
//create an empty list
measures = new List<double>();
myPane = zedGraphControl2.GraphPane;
//init your zegGraphControl here
//create an empty curve: it will be filled later
myCurve = myPane.AddCurve("Porsche", null, Color.Red, SymbolType.Diamond);
//create the worker
worker = new BackgroundWorker();
// set this to true so that you can cancel the worker
worker.WorkerSupportsCancellation = true;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//the worker has completed
//do whatever you want here
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//put all your serial port code here
SerialPort sprt = new SerialPort("COM1");
sprt.BaudRate = 9600;
sprt.Parity = Parity.None;
sprt.StopBits = StopBits.One;
sprt.DataBits = 8;
sprt.Handshake = Handshake.None;
try
{
sprt.Open();
}
catch (Exception)
{
MessageBox.Show("Check port");
return;
}
//worker.CancellationPending will change to true when CancelAsync is called
//(so when the user clicks button2).
//while the worker should still continue, read incoming data
while (!worker.CancellationPending)
{
//wait for data to come...
System.Threading.Thread.Sleep(100);
string indata = sprt.ReadExisting();
//extract the values from the read data
//be careful here: make sure the read data is complete...
string[] splt = indata.Split('\r');
// string chop = splt[2];
//// string final = chop.Remove(5);
// float d = Convert.ToSingle(chop);
//update the measures
//measures is shared by several threads: you must lock it to access it safely
lock (measures)
{
for (int i = 1; i < splt.Length-2; i++)
{
string chop = splt[i];
// string final = chop.Remove(5);
float d = Convert.ToSingle(chop);
measures.Add(d);
}
}
//update the graph
BeginInvoke((Action)(() => UpdateGraph()));
}
//user wants to stop the worker
sprt.Close();
}
/// This function is called when the graph should be updated
private void UpdateGraph()
{
//messures is shared by several threads: you must lock it to access it safely
lock (measures)
{
//add each measure into the curve
for (int i = 0; i < measures.Count; i++)
{
//fill each with what ever you want
double x = myCurve.Points.Count;
double y = measures[i];
//add a new point to the curve
myCurve.AddPoint(x, y);
}
//all measures have been added
//we can empty the list
measures.Clear();
}
//the curve has been updated so refresh the graph
zedGraphControl2.AxisChange();
zedGraphControl2.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
worker.CancelAsync();
}
}
}
But It is not displaying continuous graph. I don't know where is the problem in my code.
Please help me to solve this problem.
Any help will be appreciated.
Why don't you try RollingPointPairList instead of list...?
there's an example for Real Time Plot using ZedGraph, Good Luck..:)
It may be that you are clearing your measures list every time you plot the curve. Each time, ZedGraph is plotting a single point and erasing the rest of the curve. Maybe?